[gtk/stackpage: 3/11] tools: Split gtk-builder-tool
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/stackpage: 3/11] tools: Split gtk-builder-tool
- Date: Fri, 8 Feb 2019 05:10:38 +0000 (UTC)
commit addcf2e52626635aafaea591e5bba82b526de92b
Author: Matthias Clasen <mclasen redhat com>
Date: Thu Feb 7 04:12:09 2019 -0500
tools: Split gtk-builder-tool
Put each command into its own file. This is in
preparation for redoing the simplify command.
gtk/tools/gtk-builder-tool-enumerate.c | 74 +++
gtk/tools/gtk-builder-tool-preview.c | 197 ++++++
gtk/tools/gtk-builder-tool-simplify.c | 717 ++++++++++++++++++++++
gtk/tools/gtk-builder-tool-validate.c | 152 +++++
gtk/tools/gtk-builder-tool.c | 1028 +-------------------------------
gtk/tools/meson.build | 6 +-
6 files changed, 1151 insertions(+), 1023 deletions(-)
---
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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#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<packing>\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<cell-packing>\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<child>\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, "%*s<property", data->indent, "");
+ 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, "</property>\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</%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</%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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#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 <gtk/gtk.h>
#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<packing>\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<cell-packing>\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<child>\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, "%*s<property", data->indent, "");
- 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, "</property>\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</%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</%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']],
]
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]