summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/reference/gtk/gtk4-builder-tool.rst8
-rw-r--r--tools/fake-scope.c213
-rw-r--r--tools/fake-scope.h9
-rw-r--r--tools/gtk-builder-tool-enumerate.c39
-rw-r--r--tools/gtk-builder-tool-validate.c203
-rw-r--r--tools/meson.build3
6 files changed, 367 insertions, 108 deletions
diff --git a/docs/reference/gtk/gtk4-builder-tool.rst b/docs/reference/gtk/gtk4-builder-tool.rst
index b3d08863b2..a7e38e73d4 100644
--- a/docs/reference/gtk/gtk4-builder-tool.rst
+++ b/docs/reference/gtk/gtk4-builder-tool.rst
@@ -13,7 +13,7 @@ SYNOPSIS
| **gtk4-builder-tool** <COMMAND> [OPTIONS...] <FILE>
|
| **gtk4-builder-tool** validate [OPTIONS...] <FILE>
-| **gtk4-builder-tool** enumerate <FILE>
+| **gtk4-builder-tool** enumerate [OPTIONS...] <FILE>
| **gtk4-builder-tool** simplify [OPTIONS...] <FILE>
| **gtk4-builder-tool** preview [OPTIONS...] <FILE>
| **gtk4-builder-tool** screenshot [OPTIONS...] <FILE>
@@ -40,9 +40,13 @@ errors to ``stderr``.
Enumeration
^^^^^^^^^^^
-The ``enumerate`` command lists all the named objects that are present in the UI
+The ``enumerate`` command prints all the named objects that are present in the UI
definition file.
+``--callbacks``
+
+ Print the names of callbacks as well.
+
Preview
^^^^^^^
diff --git a/tools/fake-scope.c b/tools/fake-scope.c
new file mode 100644
index 0000000000..8234d577a8
--- /dev/null
+++ b/tools/fake-scope.c
@@ -0,0 +1,213 @@
+/* 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 "config.h"
+
+#include "fake-scope.h"
+#include "gtk-builder-tool.h"
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+
+/* {{{ Scope implementation */
+
+struct _FakeScope
+{
+ GtkBuilderCScope parent;
+
+ GPtrArray *types;
+ GPtrArray *callbacks;
+};
+
+static GtkBuilderScopeInterface *parent_scope_iface;
+
+static void
+dummy_cb (void)
+{
+}
+
+static GClosure *
+fake_scope_create_closure (GtkBuilderScope *scope,
+ GtkBuilder *builder,
+ const char *function_name,
+ GtkBuilderClosureFlags flags,
+ GObject *object,
+ GError **error)
+{
+ FakeScope *self = FAKE_SCOPE (scope);
+ GClosure *closure;
+ gboolean swapped = flags & GTK_BUILDER_CLOSURE_SWAPPED;
+
+ g_ptr_array_add (self->callbacks, g_strdup (function_name));
+
+ if (object == NULL)
+ object = gtk_builder_get_current_object (builder);
+
+ if (object)
+ {
+ if (swapped)
+ closure = g_cclosure_new_object_swap (dummy_cb, object);
+ else
+ closure = g_cclosure_new_object (dummy_cb, object);
+ }
+ else
+ {
+ if (swapped)
+ closure = g_cclosure_new_swap (dummy_cb, NULL, NULL);
+ else
+ closure = g_cclosure_new (dummy_cb, NULL, NULL);
+ }
+
+ return closure;
+}
+
+static GType
+fake_scope_get_type_from_name (GtkBuilderScope *scope,
+ GtkBuilder *builder,
+ const char *type_name)
+{
+ FakeScope *self = FAKE_SCOPE (scope);
+ GType type;
+
+ type = parent_scope_iface->get_type_from_name (scope, builder, type_name);
+
+ g_ptr_array_add (self->types, g_strdup (type_name));
+
+ return type;
+}
+
+static GType
+fake_scope_get_type_from_function (GtkBuilderScope *scope,
+ GtkBuilder *builder,
+ const char *function_name)
+{
+ FakeScope *self = FAKE_SCOPE (scope);
+ GType type;
+
+ type = parent_scope_iface->get_type_from_function (scope, builder, function_name);
+
+ if (type != G_TYPE_INVALID)
+ g_ptr_array_add (self->types, g_strdup (g_type_name (type)));
+
+ return type;
+}
+
+static void
+fake_scope_scope_init (GtkBuilderScopeInterface *iface)
+{
+ parent_scope_iface = g_type_interface_peek_parent (iface);
+
+ iface->get_type_from_name = fake_scope_get_type_from_name;
+ iface->get_type_from_function = fake_scope_get_type_from_function;
+ iface->create_closure = fake_scope_create_closure;
+}
+
+G_DEFINE_TYPE_WITH_CODE (FakeScope, fake_scope, GTK_TYPE_BUILDER_CSCOPE,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDER_SCOPE,
+ fake_scope_scope_init))
+
+static void
+fake_scope_init (FakeScope *scope)
+{
+ scope->types = g_ptr_array_new_with_free_func (g_free);
+ scope->callbacks = g_ptr_array_new_with_free_func (g_free);
+}
+
+static void
+fake_scope_finalize (GObject *object)
+{
+ FakeScope *self = FAKE_SCOPE (object);
+
+ g_ptr_array_unref (self->types);
+ g_ptr_array_unref (self->callbacks);
+
+ G_OBJECT_CLASS (fake_scope_parent_class)->finalize (object);
+}
+
+static void
+fake_scope_class_init (FakeScopeClass *class)
+{
+ G_OBJECT_CLASS (class)->finalize = fake_scope_finalize;
+}
+
+/* }}} */
+/* {{{ API */
+
+FakeScope *
+fake_scope_new (void)
+{
+ return g_object_new (fake_scope_get_type (), NULL);
+}
+
+static int
+cmp_strings (gconstpointer a,
+ gconstpointer b)
+{
+ const char **aa = (const char **)a;
+ const char **bb = (const char **)b;
+
+ return strcmp (*aa, *bb);
+}
+
+static void
+g_ptr_array_unique (GPtrArray *array,
+ GCompareFunc cmp)
+{
+ int i;
+
+ i = 1;
+ while (i < array->len)
+ {
+ gconstpointer *one = g_ptr_array_index (array, i - 1);
+ gconstpointer *two = g_ptr_array_index (array, i);
+
+ if (cmp (&one, &two) == 0)
+ g_ptr_array_remove_index (array, i);
+ else
+ i++;
+ }
+}
+
+GPtrArray *
+fake_scope_get_types (FakeScope *self)
+{
+ g_ptr_array_sort (self->types, cmp_strings);
+ g_ptr_array_unique (self->types, cmp_strings);
+
+ return self->types;
+}
+
+GPtrArray *
+fake_scope_get_callbacks (FakeScope *self)
+{
+ g_ptr_array_sort (self->callbacks, cmp_strings);
+ g_ptr_array_unique (self->callbacks, cmp_strings);
+
+ return self->callbacks;
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/tools/fake-scope.h b/tools/fake-scope.h
new file mode 100644
index 0000000000..05234129b8
--- /dev/null
+++ b/tools/fake-scope.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <gtk.h>
+
+G_DECLARE_FINAL_TYPE (FakeScope, fake_scope, FAKE, SCOPE, GtkBuilderCScope)
+
+FakeScope * fake_scope_new (void);
+GPtrArray * fake_scope_get_types (FakeScope *self);
+GPtrArray * fake_scope_get_callbacks (FakeScope *self);
diff --git a/tools/gtk-builder-tool-enumerate.c b/tools/gtk-builder-tool-enumerate.c
index f0c6c8dc7e..81c4cfc555 100644
--- a/tools/gtk-builder-tool-enumerate.c
+++ b/tools/gtk-builder-tool-enumerate.c
@@ -29,6 +29,7 @@
#include <gtk/gtk.h>
#include "gtkbuilderprivate.h"
#include "gtk-builder-tool.h"
+#include "fake-scope.h"
static const char *
object_get_id (GObject *object)
@@ -42,15 +43,16 @@ object_get_id (GObject *object)
void
do_enumerate (int *argc, const char ***argv)
{
+ FakeScope *scope;
GtkBuilder *builder;
GError *error = NULL;
int ret;
GSList *list, *l;
- GObject *object;
- const char *name;
+ gboolean callbacks = FALSE;
char **filenames = NULL;
GOptionContext *context;
const GOptionEntry entries[] = {
+ { "callbacks", 0, 0, G_OPTION_ARG_NONE, &callbacks, "Also print callbacks", NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE") },
{ NULL, }
};
@@ -59,7 +61,7 @@ do_enumerate (int *argc, const char ***argv)
context = g_option_context_new (NULL);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
- g_option_context_set_summary (context, _("List all named objects."));
+ g_option_context_set_summary (context, _("Print all named objects."));
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
@@ -83,6 +85,9 @@ do_enumerate (int *argc, const char ***argv)
}
builder = gtk_builder_new ();
+ scope = fake_scope_new ();
+ gtk_builder_set_scope (builder, GTK_BUILDER_SCOPE (scope));
+
ret = gtk_builder_add_from_file (builder, filenames[0], &error);
if (ret == 0)
@@ -91,11 +96,14 @@ do_enumerate (int *argc, const char ***argv)
exit (1);
}
+ if (callbacks)
+ g_print ("Objects:\n");
+
list = gtk_builder_get_objects (builder);
for (l = list; l; l = l->next)
{
- object = l->data;
- name = object_get_id (object);
+ GObject *object = l->data;
+ const char *name = object_get_id (object);
if (g_str_has_prefix (name, "___") && g_str_has_suffix (name, "___"))
continue;
@@ -103,6 +111,27 @@ do_enumerate (int *argc, const char ***argv)
}
g_slist_free (list);
+ if (callbacks)
+ {
+ GPtrArray *names;
+ gboolean need_prefix = TRUE;
+
+ names = fake_scope_get_callbacks (scope);
+ for (int i = 0; i < names->len; i++)
+ {
+ const char *name = g_ptr_array_index (names, i);
+
+ if (need_prefix)
+ {
+ need_prefix = FALSE;
+ g_print ("\nCallbacks:\n");
+ }
+
+ g_print ("%s\n", name);
+ }
+ }
+
+ g_object_unref (scope);
g_object_unref (builder);
g_strfreev (filenames);
diff --git a/tools/gtk-builder-tool-validate.c b/tools/gtk-builder-tool-validate.c
index f887412700..244771f875 100644
--- a/tools/gtk-builder-tool-validate.c
+++ b/tools/gtk-builder-tool-validate.c
@@ -29,6 +29,8 @@
#include <gtk/gtk.h>
#include "gtkbuilderprivate.h"
#include "gtk-builder-tool.h"
+#include "fake-scope.h"
+
static GType
make_fake_type (const char *type_name,
@@ -54,73 +56,10 @@ make_fake_type (const char *type_name,
0);
}
-static void
-do_validate_template (const char *filename,
- const char *type_name,
- const char *parent_name)
-{
- GType template_type;
- GObject *object;
- GtkBuilder *builder;
- GError *error = NULL;
- int 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);
-
- object = g_object_new (template_type, NULL);
- if (!object)
- {
- 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, object , 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);
- }
-}
+/* {{{ Deprecations */
static gboolean
-parse_template_error (const char *message,
- char **class_name,
- char **parent_name)
-{
- char *p;
-
- 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 *class_name && *parent_name;
-}
-
-static gboolean
-is_deprecated (GObject *object)
+is_deprecated (const char *name)
{
const char *names[] = {
"GtkAppChooser",
@@ -160,52 +99,33 @@ is_deprecated (GObject *object)
NULL
};
- return g_strv_contains (names, G_OBJECT_TYPE_NAME (object));
-}
-
-static const char *
-object_get_id (GObject *object)
-{
- const char *name;
-
- if (GTK_IS_BUILDABLE (object))
- name = gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
- else
- name = g_object_get_data (object, "gtk-builder-id");
-
- if (g_str_has_prefix (name, "___"))
- return NULL;
-
- return name;
+ return g_strv_contains (names, name);
}
static gboolean
-check_deprecations (GtkBuilder *builder,
- GError **error)
+fake_scope_check_deprecations (FakeScope *self,
+ GError **error)
{
- GSList *objects;
+ GPtrArray *types;
GString *s;
+ types = fake_scope_get_types (self);
+
s = g_string_new ("");
- objects = gtk_builder_get_objects (builder);
- for (GSList *l = objects; l; l = l->next)
+ for (int i = 0; i < types->len; i++)
{
- GObject *obj = l->data;
+ const char *name = g_ptr_array_index (types, i);
- if (is_deprecated (obj))
+ if (is_deprecated (name))
{
if (s->len == 0)
g_string_append (s, "Deprecated types:\n");
- g_string_append_printf (s, "%s", G_OBJECT_TYPE_NAME (obj));
- if (object_get_id (obj))
- g_string_append_printf (s, " (named '%s')", object_get_id (obj));
+ g_string_append_printf (s, "%s", name);
g_string_append (s, "\n");
}
}
- g_slist_free (objects);
-
if (s->len > 0)
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, s->str);
@@ -214,37 +134,118 @@ check_deprecations (GtkBuilder *builder,
return *error == NULL;
}
+/* }}} */
+
+static gboolean
+validate_template (const char *filename,
+ const char *type_name,
+ const char *parent_name,
+ gboolean deprecations)
+{
+ GType template_type;
+ GObject *object;
+ FakeScope *scope;
+ GtkBuilder *builder;
+ GError *error = NULL;
+ gboolean 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);
+
+ object = g_object_new (template_type, NULL);
+ if (!object)
+ {
+ g_printerr ("Failed to create an instance of the template type %s\n", type_name);
+ return FALSE;
+ }
+
+ builder = gtk_builder_new ();
+ scope = fake_scope_new ();
+ gtk_builder_set_scope (builder, GTK_BUILDER_SCOPE (scope));
+ ret = gtk_builder_extend_with_template (builder, object, template_type, " ", 1, &error);
+ if (ret)
+ ret = gtk_builder_add_from_file (builder, filename, &error);
+ if (ret && deprecations)
+ ret = fake_scope_check_deprecations (scope, &error);
+ g_object_unref (scope);
+ g_object_unref (builder);
+
+ if (!ret)
+ {
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ }
+
+ return ret;
+}
+
+static gboolean
+parse_template_error (const char *message,
+ char **class_name,
+ char **parent_name)
+{
+ char *p;
+
+ 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 *class_name && *parent_name;
+}
+
static gboolean
validate_file (const char *filename,
gboolean deprecations)
{
+ FakeScope *scope;
GtkBuilder *builder;
GError *error = NULL;
- int ret;
+ gboolean ret;
char *class_name = NULL;
char *parent_name = NULL;
builder = gtk_builder_new ();
+ scope = fake_scope_new ();
+ gtk_builder_set_scope (builder, GTK_BUILDER_SCOPE (scope));
ret = gtk_builder_add_from_file (builder, filename, &error);
if (ret && deprecations)
- ret = check_deprecations (builder, &error);
+ ret = fake_scope_check_deprecations (scope, &error);
+ g_object_unref (scope);
g_object_unref (builder);
- if (ret == 0)
+ if (!ret)
{
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);
+ ret = validate_template (filename, class_name, parent_name, deprecations);
}
else
{
g_printerr ("%s\n", error->message);
- return FALSE;
}
+
+ g_error_free (error);
}
- return TRUE;
+ return ret;
}
void
@@ -284,3 +285,5 @@ do_validate (int *argc, const char ***argv)
g_strfreev (filenames);
}
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/tools/meson.build b/tools/meson.build
index 52afd5433d..0e90d88945 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -29,7 +29,8 @@ gtk_tools = [
'gtk-builder-tool-validate.c',
'gtk-builder-tool-enumerate.c',
'gtk-builder-tool-screenshot.c',
- 'gtk-builder-tool-preview.c'], [libgtk_dep] ],
+ 'gtk-builder-tool-preview.c',
+ 'fake-scope.c'], [libgtk_dep] ],
['gtk4-update-icon-cache', ['updateiconcache.c'] + extra_update_icon_cache_objs, [ libgtk_static_dep ] ],
['gtk4-encode-symbolic-svg', ['encodesymbolic.c'], [ libgtk_static_dep ] ],
]