diff options
author | Tristan Van Berkom <tristanvb@openismus.com> | 2013-03-20 16:33:52 +0900 |
---|---|---|
committer | Tristan Van Berkom <tristanvb@openismus.com> | 2013-04-08 21:19:27 +0900 |
commit | b7da0d21f8ca2f9ec4eccf3a96c57327e0a5d601 (patch) | |
tree | 44238d9bbf5c01671314428cdcedee9c2a92bbab /gtk | |
parent | 82583640a275c7d95c185e4ad9eaa95bc37aba1f (diff) | |
download | gtk+-b7da0d21f8ca2f9ec4eccf3a96c57327e0a5d601.tar.gz |
GtkBuilder: Add private _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. A couple of error codes are also added
to handle a few new possible failure cases.
DTD Files gtkbuilder.rnc and gtkbuilder.rng have been updated to include
the new <template> tag and it's attributes.
Diffstat (limited to 'gtk')
-rw-r--r-- | gtk/gtkbuilder.c | 137 | ||||
-rw-r--r-- | gtk/gtkbuilder.h | 7 | ||||
-rw-r--r-- | gtk/gtkbuilder.rnc | 10 | ||||
-rw-r--r-- | gtk/gtkbuilder.rng | 19 | ||||
-rw-r--r-- | gtk/gtkbuilderparser.c | 133 | ||||
-rw-r--r-- | gtk/gtkbuilderprivate.h | 11 |
6 files changed, 297 insertions, 20 deletions
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c index 9e2f29466e..7e0880240d 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) { @@ -987,6 +1064,48 @@ gtk_builder_add_objects_from_file (GtkBuilder *builder, return 1; } +/* Main private entry point for building composite container + * components from template XML + */ +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); + + if (tmp_error != NULL) + { + g_propagate_error (error, tmp_error); + return 0; + } + + return 1; +} + /** * gtk_builder_add_from_resource: * @builder: a #GtkBuilder @@ -2141,6 +2260,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 5b22eb13cb..f002353d36 100644 --- a/gtk/gtkbuilder.h +++ b/gtk/gtkbuilder.h @@ -59,6 +59,9 @@ 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: A specified object type is of the same type or + * derived from the type of the composite class being extended with builder XML. + * @GTK_BUILDER_ERROR_TEMPLATE_MISMATCH: The wrong type was specified in a composite class's template XML * * Error codes that identify various errors that can occur while using * #GtkBuilder. @@ -73,7 +76,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); diff --git a/gtk/gtkbuilder.rnc b/gtk/gtkbuilder.rnc index a2b40320b9..635fd4f289 100644 --- a/gtk/gtkbuilder.rnc +++ b/gtk/gtkbuilder.rnc @@ -1,6 +1,6 @@ start = element interface { attribute domain { text } ?, - ( requires | object | menu ) * + ( requires | object | template | menu ) * } requires = element requires { @@ -16,6 +16,12 @@ object = element object { (property | signal | child | ANY) * } +template = element template { + attribute class { text }, + attribute parent { text }, + (property | signal | child | ANY) * +} + property = element property { attribute name { text }, attribute translatable { "yes" | "no" } ?, @@ -76,7 +82,7 @@ section = element section { (attribute_ | item | submenu | section) * } -ANY = element * - (interface | requires | object | property | signal | child | menu | item | attribute | link | submenu | section) { +ANY = element * - (interface | requires | object | template | property | signal | child | menu | item | attribute | link | submenu | section) { attribute * { text } *, (ALL * & text ?) } diff --git a/gtk/gtkbuilder.rng b/gtk/gtkbuilder.rng index e36d05194a..eee698a607 100644 --- a/gtk/gtkbuilder.rng +++ b/gtk/gtkbuilder.rng @@ -11,6 +11,7 @@ <choice> <ref name="requires"/> <ref name="object"/> + <ref name="template"/> <ref name="menu"/> </choice> </zeroOrMore> @@ -54,6 +55,24 @@ </zeroOrMore> </element> </define> + <define name="template"> + <element name="template"> + <attribute name="class"> + <text/> + </attribute> + <attribute name="parent"> + <text/> + </attribute> + <zeroOrMore> + <choice> + <ref name="property"/> + <ref name="signal"/> + <ref name="child"/> + <ref name="ANY"/> + </choice> + </zeroOrMore> + </element> + </define> <define name="property"> <element name="property"> <attribute name="name"> diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c index 6344258905..9a4b4062e2 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,92 @@ 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; + 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_class)); + 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_class, line, line2); + return; + } + + g_hash_table_insert (data->object_ids, g_strdup (object_class), GINT_TO_POINTER (line)); +} + + +static void free_object_info (ObjectInfo *info) { /* Do not free the signal items, which GtkBuilder takes ownership of */ @@ -446,7 +545,9 @@ parse_child (ParserData *data, guint i; object_info = state_peek_info (data, ObjectInfo); - if (!object_info || strcmp (object_info->tag.name, "object") != 0) + if (!object_info || + !(strcmp (object_info->tag.name, "object") == 0 || + strcmp (object_info->tag.name, "template") == 0)) { error_invalid_tag (data, element_name, NULL, error); return; @@ -493,7 +594,9 @@ parse_property (ParserData *data, int i; object_info = state_peek_info (data, ObjectInfo); - if (!object_info || strcmp (object_info->tag.name, "object") != 0) + if (!object_info || + !(strcmp (object_info->tag.name, "object") == 0 || + strcmp (object_info->tag.name, "template") == 0)) { error_invalid_tag (data, element_name, NULL, error); return; @@ -566,7 +669,9 @@ parse_signal (ParserData *data, int i; object_info = state_peek_info (data, ObjectInfo); - if (!object_info || strcmp (object_info->tag.name, "object") != 0) + if (!object_info || + !(strcmp (object_info->tag.name, "object") == 0 || + strcmp (object_info->tag.name, "template") == 0)) { error_invalid_tag (data, element_name, NULL, error); return; @@ -784,7 +889,8 @@ parse_custom (GMarkupParseContext *context, if (!parent_info) return FALSE; - if (strcmp (parent_info->tag.name, "object") == 0) + if (strcmp (parent_info->tag.name, "object") == 0 || + strcmp (parent_info->tag.name, "template") == 0) { ObjectInfo* object_info = (ObjectInfo*)parent_info; if (!object_info->object) @@ -877,6 +983,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 */ @@ -972,7 +1080,8 @@ end_element (GMarkupParseContext *context, { _gtk_builder_menu_end (data); } - else if (strcmp (element_name, "object") == 0) + else if (strcmp (element_name, "object") == 0 || + strcmp (element_name, "template") == 0) { ObjectInfo *object_info = state_pop_info (data, ObjectInfo); ChildInfo* child_info = state_peek_info (data, ChildInfo); @@ -1012,7 +1121,8 @@ end_element (GMarkupParseContext *context, CommonInfo *info = state_peek_info (data, CommonInfo); /* Normal properties */ - if (strcmp (info->tag.name, "object") == 0) + if (strcmp (info->tag.name, "object") == 0 || + strcmp (info->tag.name, "template") == 0) { ObjectInfo *object_info = (ObjectInfo*)info; @@ -1101,7 +1211,8 @@ text (GMarkupParseContext *context, static void free_info (CommonInfo *info) { - if (strcmp (info->tag.name, "object") == 0) + if (strcmp (info->tag.name, "object") == 0 || + strcmp (info->tag.name, "template") == 0) free_object_info ((ObjectInfo *)info); else if (strcmp (info->tag.name, "child") == 0) free_child_info ((ChildInfo *)info); diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h index 3667ed52d0..9af96d9865 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,12 @@ 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); +guint _gtk_builder_extend_with_template (GtkBuilder *builder, + GtkWidget *widget, + GType template_type, + const gchar *buffer, + gsize length, + GError **error); #endif /* __GTK_BUILDER_PRIVATE_H__ */ |