[gtk+/composite-templates-new: 2/3] GtkBuilder: Add gtk_builder_extend_with_template()
- From: Tristan Van Berkom <tvb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/composite-templates-new: 2/3] GtkBuilder: Add gtk_builder_extend_with_template()
- Date: Wed, 20 Mar 2013 08:58:20 +0000 (UTC)
commit a60759ed6036529be010ab56d6c1c304e9c71226
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date: Wed Mar 20 16:33:52 2013 +0900
GtkBuilder: Add gtk_builder_extend_with_template()
This adds the definition of the <template> tag with some documentation
on the variant of the format.
gtk_builder_extend_with_template() is to be used while GtkContainer
builds from composite templates but can be used manually. A couple
of error codes are also added to handle a few new possible failure
cases.
gtk/gtkbuilder.c | 201 +++++++++++++++++++++++++++++++++++++++++++++--
gtk/gtkbuilder.h | 15 +++-
gtk/gtkbuilderparser.c | 110 +++++++++++++++++++++++++-
gtk/gtkbuilderprivate.h | 5 +
4 files changed, 320 insertions(+), 11 deletions(-)
---
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index 6a7e886..74580a3 100644
--- a/gtk/gtkbuilder.c
+++ b/gtk/gtkbuilder.c
@@ -275,6 +275,7 @@ struct _GtkBuilderPrivate
GSList *signals;
gchar *filename;
gchar *resource_prefix;
+ GType template_type;
};
G_DEFINE_TYPE (GtkBuilder, gtk_builder, G_TYPE_OBJECT)
@@ -459,8 +460,9 @@ gtk_builder_get_parameters (GtkBuilder *builder,
GType object_type,
const gchar *object_name,
GSList *properties,
+ GParamFlags filter_flags,
GArray **parameters,
- GArray **construct_parameters)
+ GArray **filtered_parameters)
{
GSList *l;
GParamSpec *pspec;
@@ -471,8 +473,10 @@ gtk_builder_get_parameters (GtkBuilder *builder,
oclass = g_type_class_ref (object_type);
g_assert (oclass != NULL);
- *parameters = g_array_new (FALSE, FALSE, sizeof (GParameter));
- *construct_parameters = g_array_new (FALSE, FALSE, sizeof (GParameter));
+ if (parameters)
+ *parameters = g_array_new (FALSE, FALSE, sizeof (GParameter));
+ if (filtered_parameters)
+ *filtered_parameters = g_array_new (FALSE, FALSE, sizeof (GParameter));
for (l = properties; l; l = l->next)
{
@@ -530,10 +534,16 @@ gtk_builder_get_parameters (GtkBuilder *builder,
continue;
}
- if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
- g_array_append_val (*construct_parameters, parameter);
+ if (pspec->flags & filter_flags)
+ {
+ if (filtered_parameters)
+ g_array_append_val (*filtered_parameters, parameter);
+ }
else
- g_array_append_val (*parameters, parameter);
+ {
+ if (parameters)
+ g_array_append_val (*parameters, parameter);
+ }
}
g_type_class_unref (oclass);
@@ -619,10 +629,22 @@ _gtk_builder_construct (GtkBuilder *builder,
info->class_name);
return NULL;
}
+ else if (builder->priv->template_type != 0 &&
+ g_type_is_a (object_type, builder->priv->template_type))
+ {
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_OBJECT_TYPE_REFUSED,
+ "Refused to build object of type `%s' because it "
+ "conforms to the template type `%s', avoiding infinite recursion.",
+ info->class_name, g_type_name (builder->priv->template_type));
+ return NULL;
+ }
gtk_builder_get_parameters (builder, object_type,
info->id,
info->properties,
+ (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY),
¶meters,
&construct_parameters);
@@ -735,6 +757,61 @@ _gtk_builder_construct (GtkBuilder *builder,
}
void
+_gtk_builder_apply_properties (GtkBuilder *builder,
+ ObjectInfo *info,
+ GError **error)
+{
+ GArray *parameters;
+ GType object_type;
+ GtkBuildableIface *iface;
+ GtkBuildable *buildable;
+ gboolean custom_set_property;
+ gint i;
+
+ g_assert (info->object != NULL);
+ g_assert (info->class_name != NULL);
+ object_type = gtk_builder_get_type_from_name (builder, info->class_name);
+
+ /* Fetch all properties that are not construct-only */
+ gtk_builder_get_parameters (builder, object_type,
+ info->id,
+ info->properties,
+ G_PARAM_CONSTRUCT_ONLY,
+ ¶meters, NULL);
+
+ custom_set_property = FALSE;
+ buildable = NULL;
+ iface = NULL;
+ if (GTK_IS_BUILDABLE (info->object))
+ {
+ buildable = GTK_BUILDABLE (info->object);
+ iface = GTK_BUILDABLE_GET_IFACE (info->object);
+ if (iface->set_buildable_property)
+ custom_set_property = TRUE;
+ }
+
+ for (i = 0; i < parameters->len; i++)
+ {
+ GParameter *param = &g_array_index (parameters, GParameter, i);
+ if (custom_set_property)
+ iface->set_buildable_property (buildable, builder, param->name, ¶m->value);
+ else
+ g_object_set_property (info->object, param->name, ¶m->value);
+
+#if G_ENABLE_DEBUG
+ if (gtk_get_debug_flags () & GTK_DEBUG_BUILDER)
+ {
+ gchar *str = g_strdup_value_contents ((const GValue*)¶m->value);
+ g_print ("set %s: %s = %s\n", info->id, param->name, str);
+ g_free (str);
+ }
+#endif
+ g_value_unset (¶m->value);
+ }
+ g_array_free (parameters, TRUE);
+}
+
+void
_gtk_builder_add (GtkBuilder *builder,
ChildInfo *child_info)
{
@@ -988,6 +1065,112 @@ gtk_builder_add_objects_from_file (GtkBuilder *builder,
}
/**
+ * gtk_builder_extend_with_template:
+ * @builder: a #GtkBuilder
+ * @widget: the #GtkWidget to extend with the provided template XML
+ * @template_type: The #GType for which @widget is being extended.
+ * @buffer: the string to parse
+ * @length: the length of @buffer (may be -1 if @buffer is nul-terminated)
+ * @error: (allow-none): return location for an error, or %NULL
+ *
+ * Extends @widget with the provided template interface definition XML.
+ *
+ * Normally users don't need to call this function directly, but instead
+ * should use the automated facilities for defining composite container widgets
+ * such as gtk_container_class_set_template_from_resource() and friends.
+ *
+ * Unlike regular interface descriptions, this method will expect a
+ * <template> tag as a direct child of the toplevel <interface>
+ * tag. The <template> tag must specify the "class" attribute which
+ * must be the type name of @template_type. Optionally, the "parent" attribute
+ * may be specified to specify the direct parent type of @template_type, this
+ * is ignored by the builder but necessary for Glade to introspect what kind
+ * of properties and internal children exist for a given type when the actual
+ * type does not exist.
+ *
+ * The XML which is contained inside the <template> tag behaves as if
+ * it were added to the <object> tag defining @widget itself. You may set
+ * properties on @widget by inserting <property> tags into the <template>
+ * tag, and also add <child> tags to add children and extend @widget in the
+ * normal way you would with <object> tags.
+ *
+ * Additionally, <object> tags can also be added before and
+ * after the initial <template> tag in the normal way, allowing
+ * one to define auxilary objects which might be referenced by other
+ * widgets declared as children of the <template> tag.
+ *
+ * <example>
+ * <title>A GtkBuilder Template Definition</title>
+ * <programlisting><![CDATA[
+ * <interface>
+ * <template class="FooWidget" parent="GtkBox">
+ * <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+ * <property name="spacing">4</property>
+ * <child>
+ * <object class="GtkButton" id="hello_button">
+ * <property name="label">Hello World</property>
+ * </object>
+ * </child>
+ * <child>
+ * <object class="GtkButton" id="goodbye_button">
+ * <property name="label">Goodbye World</property>
+ * </object>
+ * </child>
+ * </template>
+ * </interface>
+ * ]]></programlisting>
+ * </example>
+ *
+ * Upon errors 0 will be returned and @error will be assigned a
+ * #GError from the #GTK_BUILDER_ERROR or #G_MARKUP_ERROR domain.
+ *
+ * Returns: A positive value on success, 0 if an error occurred
+ *
+ * Since: 3.10
+ */
+guint
+gtk_builder_extend_with_template (GtkBuilder *builder,
+ GtkWidget *widget,
+ GType template_type,
+ const gchar *buffer,
+ gsize length,
+ GError **error)
+{
+ GError *tmp_error;
+
+ g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
+ g_return_val_if_fail (g_type_name (template_type) != NULL, 0);
+ g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (widget), template_type), 0);
+ g_return_val_if_fail (buffer && buffer[0], 0);
+
+ tmp_error = NULL;
+
+ g_free (builder->priv->filename);
+ g_free (builder->priv->resource_prefix);
+ builder->priv->filename = g_strdup (".");
+ builder->priv->resource_prefix = NULL;
+ builder->priv->template_type = template_type;
+
+ gtk_builder_expose_object (builder, g_type_name (template_type), G_OBJECT (widget));
+ _gtk_builder_parser_parse_buffer (builder, "<input>",
+ buffer, length,
+ NULL,
+ &tmp_error);
+
+ /* In the wild case that this builder might be reused after */
+ builder->priv->template_type = 0;
+
+ if (tmp_error != NULL)
+ {
+ g_propagate_error (error, tmp_error);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
* gtk_builder_add_from_resource:
* @builder: a #GtkBuilder
* @resource_path: the path of the resource file to parse
@@ -2147,6 +2330,12 @@ _gtk_builder_get_absolute_filename (GtkBuilder *builder, const gchar *string)
return filename;
}
+GType
+_gtk_builder_get_template_type (GtkBuilder *builder)
+{
+ return builder->priv->template_type;
+}
+
/**
* gtk_builder_add_callback_symbol:
* @builder: a #GtkBuilder
diff --git a/gtk/gtkbuilder.h b/gtk/gtkbuilder.h
index 5b22eb1..af4a3d1 100644
--- a/gtk/gtkbuilder.h
+++ b/gtk/gtkbuilder.h
@@ -59,6 +59,10 @@ typedef struct _GtkBuilderPrivate GtkBuilderPrivate;
* @GTK_BUILDER_ERROR_VERSION_MISMATCH: The input file requires a newer version
* of GTK+.
* @GTK_BUILDER_ERROR_DUPLICATE_ID: An object id occurred twice.
+ * @GTK_BUILDER_ERROR_OBJECT_TYPE_REFUSED: An object type is of the same type or
+ * derived from the type of the template type given to gtk_builder_extend_with_template().
+ * This is refused in order to avoid infinite recursion.
+ * @GTK_BUILDER_ERROR_TEMPLATE_MISMATCH: The wrong template was used with gtk_builder_extend_with_template()
*
* Error codes that identify various errors that can occur while using
* #GtkBuilder.
@@ -73,7 +77,9 @@ typedef enum
GTK_BUILDER_ERROR_MISSING_PROPERTY_VALUE,
GTK_BUILDER_ERROR_INVALID_VALUE,
GTK_BUILDER_ERROR_VERSION_MISMATCH,
- GTK_BUILDER_ERROR_DUPLICATE_ID
+ GTK_BUILDER_ERROR_DUPLICATE_ID,
+ GTK_BUILDER_ERROR_OBJECT_TYPE_REFUSED,
+ GTK_BUILDER_ERROR_TEMPLATE_MISMATCH
} GtkBuilderError;
GQuark gtk_builder_error_quark (void);
@@ -138,6 +144,13 @@ guint gtk_builder_add_objects_from_string (GtkBuilder *builder,
gsize length,
gchar **object_ids,
GError **error);
+GDK_AVAILABLE_IN_3_10
+guint gtk_builder_extend_with_template (GtkBuilder *builder,
+ GtkWidget *widget,
+ GType template_type,
+ const gchar *buffer,
+ gsize length,
+ GError **error);
GObject* gtk_builder_get_object (GtkBuilder *builder,
const gchar *name);
GSList* gtk_builder_get_objects (GtkBuilder *builder);
diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c
index 6344258..b41ecc7 100644
--- a/gtk/gtkbuilderparser.c
+++ b/gtk/gtkbuilderparser.c
@@ -187,14 +187,27 @@ builder_construct (ParserData *data,
g_assert (object_info != NULL);
- if (object_info->object)
+ if (object_info->object && object_info->applied_properties)
return object_info->object;
object_info->properties = g_slist_reverse (object_info->properties);
- object = _gtk_builder_construct (data->builder, object_info, error);
- if (!object)
- return NULL;
+ if (object_info->object == NULL)
+ {
+ object = _gtk_builder_construct (data->builder, object_info, error);
+ if (!object)
+ return NULL;
+ }
+ else
+ {
+ /* We're building a template, the object is already set and
+ * we just want to resolve the properties at the right time
+ */
+ object = object_info->object;
+ _gtk_builder_apply_properties (data->builder, object_info, error);
+ }
+
+ object_info->applied_properties = TRUE;
g_assert (G_IS_OBJECT (object));
@@ -412,6 +425,93 @@ parse_object (GMarkupParseContext *context,
}
static void
+parse_template (GMarkupParseContext *context,
+ ParserData *data,
+ const gchar *element_name,
+ const gchar **names,
+ const gchar **values,
+ GError **error)
+{
+ ObjectInfo *object_info;
+ int i;
+ gchar *object_class = NULL;
+ gchar *object_id = NULL;
+ gint line, line2;
+ GType template_type = _gtk_builder_get_template_type (data->builder);
+ GType parsed_type;
+
+ if (template_type == 0)
+ {
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_UNHANDLED_TAG,
+ "Encountered template definition but not parsing a template.");
+ return;
+ }
+ else if (state_peek (data) != NULL)
+ {
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_UNHANDLED_TAG,
+ "Encountered template definition that is not at the top level.");
+ return;
+ }
+
+ for (i = 0; names[i] != NULL; i++)
+ {
+ if (strcmp (names[i], "class") == 0)
+ object_class = g_strdup (values[i]);
+ else if (strcmp (names[i], "parent") == 0)
+ /* Ignore 'parent' attribute, however it's needed by Glade */;
+ else
+ {
+ error_invalid_attribute (data, element_name, names[i], error);
+ return;
+ }
+ }
+
+ if (!object_class)
+ {
+ error_missing_attribute (data, element_name, "class", error);
+ return;
+ }
+
+ parsed_type = g_type_from_name (object_class);
+ if (template_type != parsed_type)
+ {
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_TEMPLATE_MISMATCH,
+ "Parsed template definition for type `%s', expected type `%s'.",
+ object_class, g_type_name (template_type));
+ return;
+ }
+
+ ++data->cur_object_level;
+
+ object_info = g_slice_new0 (ObjectInfo);
+ object_info->class_name = object_class;
+ object_info->id = g_strdup (object_class);
+ object_info->object = gtk_builder_get_object (data->builder, object_class);
+ state_push (data, object_info);
+ object_info->tag.name = element_name;
+
+ g_markup_parse_context_get_position (context, &line, NULL);
+ line2 = GPOINTER_TO_INT (g_hash_table_lookup (data->object_ids, object_id));
+ if (line2 != 0)
+ {
+ g_set_error (error, GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_DUPLICATE_ID,
+ _("Duplicate object ID '%s' on line %d (previously on line %d)"),
+ object_id, line, line2);
+ return;
+ }
+
+ g_hash_table_insert (data->object_ids, g_strdup (object_id), GINT_TO_POINTER (line));
+}
+
+
+static void
free_object_info (ObjectInfo *info)
{
/* Do not free the signal items, which GtkBuilder takes ownership of */
@@ -877,6 +977,8 @@ start_element (GMarkupParseContext *context,
parse_requires (data, element_name, names, values, error);
else if (strcmp (element_name, "object") == 0)
parse_object (context, data, element_name, names, values, error);
+ else if (strcmp (element_name, "template") == 0)
+ parse_template (context, data, element_name, names, values, error);
else if (data->requested_objects && !data->inside_requested_object)
{
/* If outside a requested object, simply ignore this tag */
diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h
index 3667ed5..9c70636 100644
--- a/gtk/gtkbuilderprivate.h
+++ b/gtk/gtkbuilderprivate.h
@@ -38,6 +38,7 @@ typedef struct {
GSList *signals;
GObject *object;
CommonInfo *parent;
+ gboolean applied_properties;
} ObjectInfo;
typedef struct {
@@ -121,6 +122,9 @@ void _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
GObject * _gtk_builder_construct (GtkBuilder *builder,
ObjectInfo *info,
GError **error);
+void _gtk_builder_apply_properties (GtkBuilder *builder,
+ ObjectInfo *info,
+ GError **error);
void _gtk_builder_add_object (GtkBuilder *builder,
const gchar *id,
GObject *object);
@@ -159,5 +163,6 @@ void _gtk_builder_menu_start (ParserData *parser_data,
GError **error);
void _gtk_builder_menu_end (ParserData *parser_data);
+GType _gtk_builder_get_template_type (GtkBuilder *builder);
#endif /* __GTK_BUILDER_PRIVATE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]