[gtk+/composite-templates: 15/15] Added inline template creation support. Now builder creates new types dinamically when needed. that
- From: Juan Pablo Ugarte <jpu src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/composite-templates: 15/15] Added inline template creation support. Now builder creates new types dinamically when needed. that
- Date: Wed, 28 Nov 2012 16:45:45 +0000 (UTC)
commit 592fb548aa291596526dbf71f387139aa5794591
Author: Juan Pablo Ugarte <juanpablougarte gmail com>
Date: Thu Nov 15 16:36:35 2012 -0300
Added inline template creation support. Now builder creates new types dinamically when needed.
that is when it finds a template element without an asociated external object.
gtk/gtkbuilder.c | 18 +++++
gtk/gtkbuilderparser.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++-
gtk/gtkbuilderprivate.h | 4 +
gtk/gtkcontainer.c | 2 +
gtk/tests/builder.c | 48 +++++++++++++
5 files changed, 246 insertions(+), 1 deletions(-)
---
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index bcd2c09..27e3962 100644
--- a/gtk/gtkbuilder.c
+++ b/gtk/gtkbuilder.c
@@ -274,6 +274,8 @@ struct _GtkBuilderPrivate
GSList *signals;
gchar *filename;
gchar *resource_prefix;
+
+ GType ignore_type;
};
G_DEFINE_TYPE (GtkBuilder, gtk_builder, G_TYPE_OBJECT)
@@ -628,6 +630,10 @@ _gtk_builder_construct (GtkBuilder *builder,
return NULL;
}
+ /* Safeguard to avoid recursion if we are building a new type with builder */
+ if (object_type == builder->priv->ignore_type)
+ return NULL;
+
gtk_builder_get_parameters (builder, info->object_type,
info->id,
info->properties,
@@ -2081,3 +2087,15 @@ _gtk_builder_get_absolute_filename (GtkBuilder *builder, const gchar *string)
return filename;
}
+
+void
+_gtk_builder_set_ignore_type (GtkBuilder *builder, GType ignore_type)
+{
+ builder->priv->ignore_type = ignore_type;
+}
+
+GType
+_gtk_builder_get_ignore_type (GtkBuilder *builder)
+{
+ return builder->priv->ignore_type;
+}
diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c
index 1dc7a48..6ddd210 100644
--- a/gtk/gtkbuilderparser.c
+++ b/gtk/gtkbuilderparser.c
@@ -29,7 +29,7 @@
#include "gtkversion.h"
#include "gtktypebuiltins.h"
#include "gtkintl.h"
-
+#include "gtkcontainer.h"
static void free_property_info (PropertyInfo *info);
static void free_object_info (ObjectInfo *info);
@@ -216,6 +216,13 @@ builder_construct (ParserData *data,
if (object_info->object)
return object_info->object;
+ /* Safeguard to avoid recursion if we are building a new type with builder
+ * _gtk_builder_construct() also checks for this, but there is no need to
+ * reverse the property list if we are not going to build the object.
+ */
+ if (object_info->object_type == _gtk_builder_get_ignore_type (data->builder))
+ return NULL;
+
object_info->properties = g_slist_reverse (object_info->properties);
object = _gtk_builder_construct (data->builder, object_info, error);
@@ -674,6 +681,150 @@ parse_signal (ParserData *data,
info->tag.name = element_name;
}
+typedef struct
+{
+ const gchar *tmpl_class, *tmpl_id;
+
+ gboolean tmpl_found, done;
+
+ GString *xml;
+} TemplateParseData;
+
+static void
+extract_template_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ TemplateParseData *state = user_data;
+ gint i;
+
+ if (state->done) return;
+
+ if (g_strcmp0 (element_name, "template") == 0)
+ {
+ gboolean right_class = FALSE, right_id = FALSE;
+
+ for (i = 0; attribute_names[i]; i++)
+ {
+ if (!g_strcmp0 (attribute_names[i], "class"))
+ right_class = (g_strcmp0 (attribute_values[i], state->tmpl_class) == 0);
+ else if (!g_strcmp0 (attribute_names[i], "id"))
+ right_id = (g_strcmp0 (attribute_values[i], state->tmpl_id) == 0);
+ }
+
+ if (right_class && right_id)
+ state->tmpl_found = TRUE;
+ }
+
+ g_string_append_printf (state->xml, "<%s", element_name);
+ for (i = 0; attribute_names[i]; i++)
+ {
+ g_string_append_printf (state->xml, " %s=\"%s\"",
+ attribute_names[i], attribute_values[i]);
+ }
+ g_string_append_printf (state->xml, ">");
+}
+
+static void
+extract_template_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ TemplateParseData *state = user_data;
+
+ if (state->done) return;
+
+ g_string_append_printf (state->xml, "</%s>", element_name);
+
+ if (g_strcmp0 (element_name, "interface") == 0 && state->tmpl_found)
+ state->done = TRUE;
+}
+
+static void
+extract_template_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ TemplateParseData *state = user_data;
+
+ if (state->done) return;
+
+ g_string_append_len (state->xml, text, text_len);
+}
+
+static gchar *
+extract_template (const gchar *buffer, const gchar *class_name, const gchar *class_id)
+{
+ GMarkupParser parser = { extract_template_start_element, extract_template_end_element, extract_template_text };
+ TemplateParseData state = { class_name, class_id, FALSE, FALSE, g_string_new ("")};
+ GMarkupParseContext *context;
+
+ context = g_markup_parse_context_new (&parser,
+ G_MARKUP_TREAT_CDATA_AS_TEXT |
+ G_MARKUP_PREFIX_ERROR_POSITION,
+ &state, NULL);
+
+ g_markup_parse_context_parse (context, buffer, -1, NULL);
+ g_markup_parse_context_end_parse (context, NULL);
+ g_markup_parse_context_free (context);
+
+ if (state.done)
+ {
+ gchar *retval = state.xml->str;
+ g_string_free (state.xml, FALSE);
+ g_file_set_contents ("/tmp/a.dump", retval, -1, NULL);
+ return retval;
+ }
+
+ g_string_free (state.xml, TRUE);
+ return NULL;
+}
+
+typedef struct
+{
+ GTypeInfo *info;
+ gchar *tmpl;
+ gchar *id;
+} TmplClassData;
+
+static void
+composite_template_derived_class_init (gpointer g_class, gpointer class_data)
+{
+ TmplClassData *data = class_data;
+ gtk_container_class_set_template_from_string (g_class, data->tmpl, data->id);
+}
+
+static GType
+create_inline_type (GType parent_type,
+ const gchar *class_name,
+ const gchar *class_id,
+ const gchar *template_xml)
+{
+ TmplClassData *tmpl;
+ GTypeQuery query;
+ GTypeInfo *info;
+
+ g_type_query (parent_type, &query);
+
+ tmpl = g_new0 (TmplClassData, 1);
+ tmpl->info = info = g_new0 (GTypeInfo, 1);
+ tmpl->tmpl = extract_template (template_xml, class_name, class_id);
+ tmpl->id = g_strdup (class_id);
+
+ info->class_size = query.class_size;
+ info->class_init = composite_template_derived_class_init;
+ info->class_data = tmpl; /* Let it leak! */
+ info->instance_size = query.instance_size;
+
+ return g_type_register_static (parent_type, class_name, info, 0);
+}
+
static void
parse_template (ParserData *data,
const gchar *element_name,
@@ -767,6 +918,27 @@ parse_template (ParserData *data,
parser_add_object_id (data, object_info->id, error);
}
+ else if (parent_class)
+ {
+ GType parent_type;
+
+ if (g_type_from_name (class_name))
+ {
+ error_generic (error, GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH, data,
+ element_name, "template class '%s' already registered", class_name);
+ return;
+ }
+
+ if (!(parent_type = gtk_builder_get_type_from_name (data->builder, parent_class)))
+ {
+ error_generic (error, GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH, data,
+ element_name, "invalid parent class type found '%s'", parent_class);
+ return;
+ }
+
+ /* Generate inline type */
+ create_inline_type (parent_type, class_name, id, data->buffer);
+ }
}
/* Called by GtkBuilder */
@@ -1308,6 +1480,7 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
domain = gtk_builder_get_translation_domain (builder);
data = g_new0 (ParserData, 1);
+ data->buffer = buffer;
data->builder = builder;
data->filename = filename;
data->domain = g_strdup (domain);
diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h
index 987c5fc..025dfdf 100644
--- a/gtk/gtkbuilderprivate.h
+++ b/gtk/gtkbuilderprivate.h
@@ -91,6 +91,7 @@ typedef struct {
} SubParser;
typedef struct {
+ const gchar *buffer;
const gchar *last_element;
GtkBuilder *builder;
gchar *domain;
@@ -164,4 +165,7 @@ void _gtk_builder_menu_end (ParserData *parser_data);
const gchar * _gtk_builder_object_get_name (GObject *object);
+void _gtk_builder_set_ignore_type (GtkBuilder *builder, GType ignore_type);
+GType _gtk_builder_get_ignore_type (GtkBuilder *builder);
+
#endif /* __GTK_BUILDER_PRIVATE_H__ */
diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c
index 7341f19..90b800c 100644
--- a/gtk/gtkcontainer.c
+++ b/gtk/gtkcontainer.c
@@ -1682,6 +1682,8 @@ gtk_container_constructor (GType type,
builder = gtk_builder_new ();
gtk_builder_expose_object (builder, cpriv->tmpl_id, object);
+ /* Safeguard to avoid recursion */
+ _gtk_builder_set_ignore_type (builder, type);
if (cpriv->tmpl_type == TMPL_STRING)
ret = gtk_builder_add_from_string (builder, cpriv->tmpl, cpriv->tmpl_len, &error);
diff --git a/gtk/tests/builder.c b/gtk/tests/builder.c
index c39da5a..7a516df 100644
--- a/gtk/tests/builder.c
+++ b/gtk/tests/builder.c
@@ -2845,6 +2845,53 @@ test_template (void)
test_template_real (TRUE);
}
+static void
+test_inline_template ()
+{
+ GError *error = NULL;
+ GtkBuilder *builder;
+ const gchar buffer[] =
+ "<interface>\n"
+ " <template class=\"MyGtkGrid\" parent=\"GtkGrid\" id=\"mygrid_tmpl\">\n"
+ " <property name=\"visible\">True</property>\n"
+ " <child>\n"
+ " <object class=\"GtkLabel\" id=\"gridlabel\">\n"
+ " <property name=\"visible\">True</property>\n"
+ " </object>\n"
+ " </child>\n"
+ " </template>\n"
+ "</interface>\n"
+ "<interface>\n"
+ " <object class=\"GtkWindow\" id=\"window\">"
+ " <child>\n"
+ " <object class=\"MyGtkGrid\" id=\"mygrid\">\n"
+ " <property name=\"visible\">True</property>\n"
+ " </object>\n"
+ " </child>\n"
+ " </object>\n"
+ "</interface>\n";
+
+ builder = gtk_builder_new ();
+
+ /* make sure the type we are trying to register does not exist */
+ g_assert (!g_type_from_name ("MyGtkGrid"));
+
+ gtk_builder_add_from_string (builder, buffer, -1, &error);
+
+ if (error) g_warning ("%s", error->message);
+ g_assert (error == NULL);
+
+ /* Check if new type was registered on the fly! */
+ g_assert (g_type_from_name ("MyGtkGrid"));
+
+ g_assert (GTK_IS_WINDOW (gtk_builder_get_object (builder, "window")));
+
+ /* Check if inline derived child was built */
+ g_assert (GTK_IS_GRID (gtk_builder_get_object (builder, "mygrid")));
+
+ g_object_unref (builder);
+}
+
static GObject *external_object = NULL, *external_object_swapped = NULL;
void
@@ -2944,6 +2991,7 @@ main (int argc, char **argv)
g_test_add_func ("/Builder/GMenu", test_gmenu);
g_test_add_func ("/Builder/LevelBar", test_level_bar);
g_test_add_func ("/Builder/Template", test_template);
+ g_test_add_func ("/Builder/Inline Template", test_inline_template);
g_test_add_func ("/Builder/Expose Object", test_expose_object);
return g_test_run();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]