[gtk+/composite-templates: 1/3] Added gtk_builder_expose_object(), gtk_builder_add_to_parent_from_file() and gtk_builder_add_to_pare
- From: Juan Pablo Ugarte <jpu src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/composite-templates: 1/3] Added gtk_builder_expose_object(), gtk_builder_add_to_parent_from_file() and gtk_builder_add_to_pare
- Date: Fri, 31 Aug 2012 21:03:27 +0000 (UTC)
commit c1760edb3dfdede9bc4cd2ab7dc75c0d277cf82c
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date: Mon Jun 7 14:30:28 2010 -0400
Added gtk_builder_expose_object(), gtk_builder_add_to_parent_from_file() and gtk_builder_add_to_parent_from_string().
Adding the needed GtkBuilder framework for composite containers. First the
expose_object() api is based on the work Marco Diego AurÃlio Mesquita did
in bug 447972 with some bugs fixed and things cleared up. Next the
gtk_builder_add_to_parent_from_[file/string]() variants are used to extend
containers inline by adding themselves and letting the builder add the
children defined in a template file.
Added semantics to build composite children automatically from an assigned template.
This patch adds gtk_container_class_set_template[_file](),
gtk_container_class_set_connect_func() and the GtkParamSpecComposite
type. Setting the class template will result in GtkContainer building
the instance's children from the template at construct time, composite
properties will be assigned to their composite children by matching
the property names with the child GtkBuildable ids. The exposed connect
function is there to allow language bindings to automatically assign
methods for the interface callbacks when they assign templates to classes.
Made GtkDialog and GtkMessageDialog composite widgets using gtk_container_class_set_template().
Enhanced documentation for gtk_container_class_set_template()
Fixing dialog separator property to be construct-only
Implemented gtk_container_buildable_get_internal_child() and removed GSEAL macros
Added missing parameter to _gtk_builder_parser_parse_buffer() calls
Fixed composite implementation
Added gtk_builder_add_to_parent_from_resource()
Used gtk_builder_add_to_parent_from_* as gtk_builder_add_from_* implementation.
Removed GtkParamSpecComposite
Added GtkContainerClassPrivate
Added gtk_container_class_declare_internal_child() funtion to make internal children declaration explicit
Added GtkContainerTemplateType type parameter to gtk_container_class_set_template()
Updated GtkDialog and GtkMessageDialog implementation to new GtkContainer template API
Reworked GtkDialog and GtkMessageDialog xml template into a GResource
Fixed property setting and signal connection for parent objects (ie: gtk_builder_add_to_parent_*())
Use intern strings for properties and signal names and a GType instead of a class name in GtkBuilder
Added new private function _gtk_builder_object_get_name()
Removed gtk_container_get_composite_child()
Get a reference to every internal object declared with gtk_container_class_declare_internal_child() which gets dropped on GtkContainer::destroy signal
Implemented "template" tag, this allow us to also build anarchist objects
Replaced GtkDialog and GtkMessageDialog template file extension with .ui
Implemented id tag for template and added cheack for id to be 'this'
Cleaned up template API, we only provided a function to set a resource as a template
Fixed identation
gtk/Makefile.am | 3 +-
gtk/gtkbuilder.c | 289 ++++++++++++++++++++++++++++++++++++-----------
gtk/gtkbuilder.h | 38 +++++--
gtk/gtkbuilderparser.c | 219 +++++++++++++++++++++++++++++++-----
gtk/gtkbuilderprivate.h | 13 ++-
5 files changed, 453 insertions(+), 109 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 397fa4f..b335c0f 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -1112,7 +1112,8 @@ gtktypebuiltins.c: @REBUILD@ $(gtk_public_h_sources) $(deprecated_h_sources) gtk
gtkresources.h: gtk.gresource.xml
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $(srcdir)/gtk.gresource.xml \
--target=$@ --sourcedir=$(srcdir) --c-name _gtk --generate-header --manual-register
-gtkresources.c: gtk.gresource.xml gtk-default.css gtk-win32.css gtk-win32-xp.css gtk-win32-base.css gtk-win32-classic.css $(DND_CURSORS)
+gtkresources.c: gtk.gresource.xml \
+ $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies gtk.gresource.xml)
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $(srcdir)/gtk.gresource.xml \
--target=$@ --sourcedir=$(srcdir) --c-name _gtk --generate-source --manual-register
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index bf75922..f306d75 100644
--- a/gtk/gtkbuilder.c
+++ b/gtk/gtkbuilder.c
@@ -123,22 +123,27 @@
* (can be specified by their name, nick or integer value), flags (can be
* specified by their name, nick, integer value, optionally combined with "|",
* e.g. "GTK_VISIBLE|GTK_REALIZED") and colors (in a format understood by
- * gdk_color_parse()). Objects can be referred to by their name. Pixbufs can be
- * specified as a filename of an image file to load. In general, GtkBuilder
- * allows forward references to objects — an object doesn't have to be
- * constructed before it can be referred to. The exception to this rule is that
- * an object has to be constructed before it can be used as the value of a
- * construct-only property.
+ * gdk_color_parse()). Pixbufs can be specified as a filename of an image file to load.
+ * Objects can be referred to by their name and by default refer to objects declared
+ * in the local xml fragment, however external objects exposed via gtk_builder_expose_object()
+ * can be referred to by specifying the "external-object" attribute.
+ *
+ * In general, GtkBuilder allows forward references to objects &mdash declared
+ * in the local xml; an object doesn't have to be constructed before it can be referred to.
+ * The exception to this rule is that an object has to be constructed before
+ * it can be used as the value of a construct-only property.
*
* Signal handlers are set up with the <signal> element. The "name"
* attribute specifies the name of the signal, and the "handler" attribute
* specifies the function to connect to the signal. By default, GTK+ tries to
* find the handler using g_module_symbol(), but this can be changed by passing
* a custom #GtkBuilderConnectFunc to gtk_builder_connect_signals_full(). The
- * remaining attributes, "after", "swapped" and "object", have the same meaning
+ * attributes "after", "swapped" and "object", have the same meaning
* as the corresponding parameters of the g_signal_connect_object() or
- * g_signal_connect_data() functions. A "last_modification_time" attribute
- * is also allowed, but it does not have a meaning to the builder.
+ * g_signal_connect_data() functions. Extenral objects can also be referred
+ * to by specifying the "external-object" attribute in the same way as described
+ * with the <property> element. A "last_modification_time" attribute is also
+ * allowed, but it does not have a meaning to the builder.
*
* Sometimes it is necessary to refer to widgets which have implicitly been
* constructed by GTK+ as part of a composite widget, to set properties on them
@@ -268,6 +273,7 @@ struct _GtkBuilderPrivate
{
gchar *domain;
GHashTable *objects;
+ GHashTable *external_objects;
GSList *delayed_properties;
GSList *signals;
gchar *filename;
@@ -318,6 +324,7 @@ gtk_builder_init (GtkBuilder *builder)
builder->priv->domain = NULL;
builder->priv->objects = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_object_unref);
+ builder->priv->external_objects = NULL;
}
@@ -335,6 +342,8 @@ gtk_builder_finalize (GObject *object)
g_free (priv->resource_prefix);
g_hash_table_destroy (priv->objects);
+ if (priv->external_objects)
+ g_hash_table_destroy (priv->external_objects);
g_slist_foreach (priv->signals, (GFunc) _free_signal_info, NULL);
g_slist_free (priv->signals);
@@ -464,7 +473,7 @@ gtk_builder_get_parameters (GtkBuilder *builder,
GObjectClass *oclass;
DelayedProperty *property;
GError *error = NULL;
-
+
oclass = g_type_class_ref (object_type);
g_assert (oclass != NULL);
@@ -490,22 +499,39 @@ gtk_builder_get_parameters (GtkBuilder *builder,
if (G_IS_PARAM_SPEC_OBJECT (pspec) &&
(G_PARAM_SPEC_VALUE_TYPE (pspec) != GDK_TYPE_PIXBUF))
{
- GObject *object = gtk_builder_get_object (builder, prop->data);
+ GObject *object;
- if (object)
+ if (prop->external)
{
+ object = g_hash_table_lookup (builder->priv->external_objects, prop->data);
+
+ if (!object)
+ {
+ g_warning ("Failed to get external object property "
+ "%s of %s with value `%s'",
+ prop->name, object_name, prop->data);
+ continue;
+ }
+
g_value_init (¶meter.value, G_OBJECT_TYPE (object));
g_value_set_object (¶meter.value, object);
}
- else
+ else if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
{
- if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
+ object = gtk_builder_get_object (builder, prop->data);
+
+ if (!object)
{
g_warning ("Failed to get constuct only property "
"%s of %s with value `%s'",
prop->name, object_name, prop->data);
continue;
}
+ g_value_init (¶meter.value, G_OBJECT_TYPE (object));
+ g_value_set_object (¶meter.value, object);
+ }
+ else
+ {
/* Delay setting property */
property = g_slice_new (DelayedProperty);
property->object = g_strdup (object_name);
@@ -517,13 +543,13 @@ gtk_builder_get_parameters (GtkBuilder *builder,
}
}
else if (!gtk_builder_value_from_string (builder, pspec,
- prop->data, ¶meter.value, &error))
+ prop->data, ¶meter.value, &error))
{
g_warning ("Failed to set property %s.%s to %s: %s",
g_type_name (object_type), prop->name, prop->data,
- error->message);
- g_error_free (error);
- error = NULL;
+ error->message);
+ g_error_free (error);
+ error = NULL;
continue;
}
@@ -540,7 +566,7 @@ static GObject *
gtk_builder_get_internal_child (GtkBuilder *builder,
ObjectInfo *info,
const gchar *childname,
- GError **error)
+ GError **error)
{
GObject *obj = NULL;
@@ -562,7 +588,7 @@ gtk_builder_get_internal_child (GtkBuilder *builder,
obj = gtk_buildable_get_internal_child (GTK_BUILDABLE (info->object),
builder,
childname);
- };
+ }
if (!obj)
{
@@ -574,6 +600,33 @@ gtk_builder_get_internal_child (GtkBuilder *builder,
return obj;
}
+static inline void
+object_set_name (GObject *object, const gchar *name)
+{
+ if (GTK_IS_BUILDABLE (object))
+ gtk_buildable_set_name (GTK_BUILDABLE (object), name);
+ else
+ g_object_set_data_full (object, "gtk-builder-name", g_strdup (name), g_free);
+}
+
+void
+_gtk_builder_add_object (GtkBuilder *builder,
+ const gchar *id,
+ GObject *object)
+{
+ object_set_name (object, id);
+ g_hash_table_insert (builder->priv->objects, g_strdup (id), g_object_ref (object));
+}
+
+const gchar *
+_gtk_builder_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");
+}
+
GObject *
_gtk_builder_construct (GtkBuilder *builder,
ObjectInfo *info,
@@ -587,25 +640,28 @@ _gtk_builder_construct (GtkBuilder *builder,
gboolean custom_set_property;
GtkBuildable *buildable;
- g_assert (info->class_name != NULL);
- object_type = gtk_builder_get_type_from_name (builder, info->class_name);
- if (object_type == G_TYPE_INVALID)
+ if ((object_type = info->object_type) == G_TYPE_INVALID)
{
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_VALUE,
"Invalid object type `%s'",
- info->class_name);
+ g_type_name (object_type));
return NULL;
}
- gtk_builder_get_parameters (builder, object_type,
+ gtk_builder_get_parameters (builder, info->object_type,
info->id,
info->properties,
¶meters,
&construct_parameters);
- if (info->constructor)
+ if (info->object)
+ {
+ /* template_object */
+ obj = g_object_ref (info->object);
+ }
+ else if (info->constructor)
{
GObject *constructor;
@@ -662,7 +718,7 @@ _gtk_builder_construct (GtkBuilder *builder,
g_object_ref_sink (obj);
GTK_NOTE (BUILDER,
- g_print ("created %s of type %s\n", info->id, info->class_name));
+ g_print ("created %s of type %s\n", info->id, g_type_name (object_type)));
for (i = 0; i < construct_parameters->len; i++)
{
@@ -703,30 +759,17 @@ _gtk_builder_construct (GtkBuilder *builder,
g_value_unset (¶m->value);
}
g_array_free (parameters, TRUE);
-
- if (GTK_IS_BUILDABLE (obj))
- gtk_buildable_set_name (buildable, info->id);
- else
- g_object_set_data_full (obj,
- "gtk-builder-name",
- g_strdup (info->id),
- g_free);
- /* we already own a reference to obj. put it in the hash table. */
- g_hash_table_insert (builder->priv->objects, g_strdup (info->id), obj);
+ /* put it in the hash table. */
+ _gtk_builder_add_object (builder, info->id, obj);
+
+ /* we already own a reference to obj. */
+ g_object_unref (obj);
return obj;
}
void
-_gtk_builder_add_object (GtkBuilder *builder,
- const gchar *id,
- GObject *object)
-{
- g_hash_table_insert (builder->priv->objects, g_strdup (id), g_object_ref (object));
-}
-
-void
_gtk_builder_add (GtkBuilder *builder,
ChildInfo *child_info)
{
@@ -873,6 +916,31 @@ gtk_builder_add_from_file (GtkBuilder *builder,
const gchar *filename,
GError **error)
{
+ return gtk_builder_add_to_parent_from_file (builder, NULL, filename, error);
+}
+
+/**
+ * gtk_builder_add_to_parent_from_file:
+ * @builder: a #GtkBuilder
+ * @parent: the parent object to be assumed in context while parsing the file
+ * @filename: the name of the file to parse
+ * @error: (allow-none): return location for an error, or %NULL
+ *
+ * Like gtk_builder_add_from_file() except the format will expect
+ * <child> instead of <object> as its first elements and expose
+ * @parent in the build context, children defined in the UI fragment
+ * will be added to @parent.
+ *
+ * Returns: A positive value on success, 0 if an error occurred
+ *
+ * Since: ...
+ **/
+guint
+gtk_builder_add_to_parent_from_file (GtkBuilder *builder,
+ GObject *parent,
+ const gchar *filename,
+ GError **error)
+{
gchar *buffer;
gsize length;
GError *tmp_error;
@@ -894,7 +962,7 @@ gtk_builder_add_from_file (GtkBuilder *builder,
builder->priv->filename = g_strdup (filename);
builder->priv->resource_prefix = NULL;
- _gtk_builder_parser_parse_buffer (builder, filename,
+ _gtk_builder_parser_parse_buffer (builder, parent, filename,
buffer, length,
NULL,
&tmp_error);
@@ -963,7 +1031,7 @@ gtk_builder_add_objects_from_file (GtkBuilder *builder,
builder->priv->filename = g_strdup (filename);
builder->priv->resource_prefix = NULL;
- _gtk_builder_parser_parse_buffer (builder, filename,
+ _gtk_builder_parser_parse_buffer (builder, NULL, filename,
buffer, length,
object_ids,
&tmp_error);
@@ -1001,18 +1069,43 @@ gtk_builder_add_from_resource (GtkBuilder *builder,
const gchar *resource_path,
GError **error)
{
+ return gtk_builder_add_to_parent_from_resource (builder, NULL, resource_path, error);
+}
+
+/**
+ * gtk_builder_add_to_parent_from_resource:
+ * @builder: a #GtkBuilder
+ * @parent: the parent object to be assumed in context while parsing the file
+ * @path: the resource path to parse
+ * @error: (allow-none): return location for an error, or %NULL
+ *
+ * Like gtk_builder_add_from_file() except the format will expect
+ * <child> instead of <object> as its first elements and expose
+ * @parent in the build context, children defined in the UI fragment
+ * will be added to @parent.
+ *
+ * Returns: A positive value on success, 0 if an error occurred
+ *
+ * Since: ...
+ **/
+guint
+gtk_builder_add_to_parent_from_resource (GtkBuilder *builder,
+ GObject *parent,
+ const gchar *path,
+ GError **error)
+{
GError *tmp_error;
GBytes *data;
char *filename_for_errors;
char *slash;
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
- g_return_val_if_fail (resource_path != NULL, 0);
+ g_return_val_if_fail (path != NULL, 0);
g_return_val_if_fail (error == NULL || *error == NULL, 0);
tmp_error = NULL;
- data = g_resources_lookup_data (resource_path, 0, &tmp_error);
+ data = g_resources_lookup_data (path, 0, &tmp_error);
if (data == NULL)
{
g_propagate_error (error, tmp_error);
@@ -1023,17 +1116,15 @@ gtk_builder_add_from_resource (GtkBuilder *builder,
g_free (builder->priv->resource_prefix);
builder->priv->filename = g_strdup (".");
- slash = strrchr (resource_path, '/');
+ slash = strrchr (path, '/');
if (slash != NULL)
- builder->priv->resource_prefix =
- g_strndup (resource_path, slash - resource_path + 1);
+ builder->priv->resource_prefix = g_strndup (path, slash - path + 1);
else
- builder->priv->resource_prefix =
- g_strdup ("/");
+ builder->priv->resource_prefix = g_strdup ("/");
- filename_for_errors = g_strconcat ("<resource>", resource_path, NULL);
+ filename_for_errors = g_strconcat ("<resource>", path, NULL);
- _gtk_builder_parser_parse_buffer (builder, filename_for_errors,
+ _gtk_builder_parser_parse_buffer (builder, parent, filename_for_errors,
g_bytes_get_data (data, NULL), g_bytes_get_size (data),
NULL,
&tmp_error);
@@ -1114,7 +1205,7 @@ gtk_builder_add_objects_from_resource (GtkBuilder *builder,
filename_for_errors = g_strconcat ("<resource>", resource_path, NULL);
- _gtk_builder_parser_parse_buffer (builder, filename_for_errors,
+ _gtk_builder_parser_parse_buffer (builder, NULL, filename_for_errors,
g_bytes_get_data (data, NULL), g_bytes_get_size (data),
object_ids,
&tmp_error);
@@ -1153,6 +1244,34 @@ gtk_builder_add_from_string (GtkBuilder *builder,
gsize length,
GError **error)
{
+ return gtk_builder_add_to_parent_from_string (builder, NULL, buffer, length, error);
+}
+
+
+/**
+ * gtk_builder_add_to_parent_from_string:
+ * @builder: a #GtkBuilder
+ * @parent: the parent object to be assumed in context while parsing
+ * @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
+ *
+ * Like gtk_builder_add_from_string() except the format will expect
+ * <child> instead of <object> as its first elements and expose
+ * @parent in the build context, children defined in the UI fragment
+ * will be added to @parent.
+ *
+ * Returns: A positive value on success, 0 if an error occurred
+ *
+ * Since: ...
+ **/
+guint
+gtk_builder_add_to_parent_from_string (GtkBuilder *builder,
+ GObject *parent,
+ const gchar *buffer,
+ gsize length,
+ GError **error)
+{
GError *tmp_error;
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
@@ -1166,7 +1285,7 @@ gtk_builder_add_from_string (GtkBuilder *builder,
builder->priv->filename = g_strdup (".");
builder->priv->resource_prefix = NULL;
- _gtk_builder_parser_parse_buffer (builder, "<input>",
+ _gtk_builder_parser_parse_buffer (builder, parent, "<input>",
buffer, length,
NULL,
&tmp_error);
@@ -1225,7 +1344,7 @@ gtk_builder_add_objects_from_string (GtkBuilder *builder,
builder->priv->filename = g_strdup (".");
builder->priv->resource_prefix = NULL;
- _gtk_builder_parser_parse_buffer (builder, "<input>",
+ _gtk_builder_parser_parse_buffer (builder, NULL, "<input>",
buffer, length,
object_ids,
&tmp_error);
@@ -1340,6 +1459,40 @@ gtk_builder_get_translation_domain (GtkBuilder *builder)
return builder->priv->domain;
}
+/**
+ * gtk_builder_expose_object:
+ * @builder: a #GtkBuilder
+ * @name: the name of the object exposed to the builder
+ * @object: the object to expose
+ *
+ * Adds @object to a pool of objects external to the
+ * objects built by builder. Objects exposed in the pool
+ * can be referred to by xml fragments in the builder.
+ */
+void
+gtk_builder_expose_object (GtkBuilder *builder,
+ const gchar *name,
+ GObject *object)
+{
+ GtkBuilderPrivate *priv;
+
+ g_return_if_fail (GTK_IS_BUILDER (builder));
+ g_return_if_fail (name && name[0]);
+ g_return_if_fail (G_IS_OBJECT (object));
+
+ priv = builder->priv;
+
+ /* We do not create the table in _init() because this is not used very much */
+ if (priv->external_objects == NULL)
+ priv->external_objects = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+
+ object_set_name (object, name);
+ g_hash_table_insert (priv->external_objects,
+ g_strdup (name), g_object_ref (object));
+}
+
+
typedef struct {
GModule *module;
gpointer data;
@@ -1369,7 +1522,6 @@ gtk_builder_connect_signals_default (GtkBuilder *builder,
g_signal_connect_data (object, signal_name, func, args->data, NULL, flags);
}
-
/**
* gtk_builder_connect_signals:
* @builder: a #GtkBuilder
@@ -1479,14 +1631,19 @@ gtk_builder_connect_signals_full (GtkBuilder *builder,
if (signal->connect_object_name)
{
- connect_object = g_hash_table_lookup (builder->priv->objects,
- signal->connect_object_name);
+ if (signal->external)
+ connect_object = g_hash_table_lookup (builder->priv->external_objects,
+ signal->connect_object_name);
+ else
+ connect_object = g_hash_table_lookup (builder->priv->objects,
+ signal->connect_object_name);
+
if (!connect_object)
- g_warning ("Could not lookup object %s on signal %s of object %s",
- signal->connect_object_name, signal->name,
- signal->object_name);
+ g_warning ("Could not lookup object %s on signal %s of object %s.",
+ signal->connect_object_name, signal->name,
+ signal->object_name);
}
-
+
func (builder, object, signal->name, signal->handler,
connect_object, signal->flags, user_data);
}
diff --git a/gtk/gtkbuilder.h b/gtk/gtkbuilder.h
index f4950eb..3588a39 100644
--- a/gtk/gtkbuilder.h
+++ b/gtk/gtkbuilder.h
@@ -59,6 +59,7 @@ 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_TEMPLATE_CLASS_MISMATCH: The Class template is designed for a different class.
*
* Error codes that identify various errors that can occur while using
* #GtkBuilder.
@@ -73,7 +74,8 @@ 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_TEMPLATE_CLASS_MISMATCH
} GtkBuilderError;
GQuark gtk_builder_error_quark (void);
@@ -129,22 +131,38 @@ guint gtk_builder_add_objects_from_file (GtkBuilder *builder,
gchar **object_ids,
GError **error);
guint gtk_builder_add_objects_from_resource(GtkBuilder *builder,
- const gchar *resource_path,
- gchar **object_ids,
- GError **error);
+ const gchar *resource_path,
+ gchar **object_ids,
+ GError **error);
guint gtk_builder_add_objects_from_string (GtkBuilder *builder,
const gchar *buffer,
gsize length,
gchar **object_ids,
GError **error);
+guint gtk_builder_add_to_parent_from_file (GtkBuilder *builder,
+ GObject *parent,
+ const gchar *filename,
+ GError **error);
+guint gtk_builder_add_to_parent_from_string (GtkBuilder *builder,
+ GObject *parent,
+ const gchar *buffer,
+ gsize length,
+ GError **error);
+guint gtk_builder_add_to_parent_from_resource (GtkBuilder *builder,
+ GObject *parent,
+ const gchar *path,
+ GError **error);
GObject* gtk_builder_get_object (GtkBuilder *builder,
const gchar *name);
GSList* gtk_builder_get_objects (GtkBuilder *builder);
+void gtk_builder_expose_object (GtkBuilder *builder,
+ const gchar *name,
+ GObject *object);
void gtk_builder_connect_signals (GtkBuilder *builder,
- gpointer user_data);
+ gpointer user_data);
void gtk_builder_connect_signals_full (GtkBuilder *builder,
GtkBuilderConnectFunc func,
- gpointer user_data);
+ gpointer user_data);
void gtk_builder_set_translation_domain (GtkBuilder *builder,
const gchar *domain);
const gchar* gtk_builder_get_translation_domain (GtkBuilder *builder);
@@ -152,15 +170,15 @@ GType gtk_builder_get_type_from_name (GtkBuilder *builder,
const char *type_name);
gboolean gtk_builder_value_from_string (GtkBuilder *builder,
- GParamSpec *pspec,
+ GParamSpec *pspec,
const gchar *string,
GValue *value,
- GError **error);
+ GError **error);
gboolean gtk_builder_value_from_string_type (GtkBuilder *builder,
- GType type,
+ GType type,
const gchar *string,
GValue *value,
- GError **error);
+ GError **error);
/**
* GTK_BUILDER_WARN_INVALID_CHILD_TYPE:
diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c
index 59c0721..ae3e8af 100644
--- a/gtk/gtkbuilderparser.c
+++ b/gtk/gtkbuilderparser.c
@@ -65,6 +65,32 @@ state_pop (ParserData *data)
#define state_pop_info(data, st) ((st*)state_pop(data))
static void
+error_generic (GError **error,
+ GtkBuilderError code,
+ ParserData *data,
+ const gchar *tag,
+ const gchar *format,
+ ...)
+{
+ gint line_number, char_number;
+ gchar *message;
+ va_list args;
+
+ g_markup_parse_context_get_position (data->ctx,
+ &line_number,
+ &char_number);
+
+ va_start (args, format);
+ message = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ g_set_error (error, GTK_BUILDER_ERROR, code, "%s:%d:%d <%s> %s",
+ data->filename, line_number, char_number, tag, message);
+
+ g_free (message);
+}
+
+static void
error_missing_attribute (ParserData *data,
const gchar *tag,
const gchar *attribute,
@@ -203,24 +229,19 @@ builder_construct (ParserData *data,
return object;
}
-static gchar *
+static GType
_get_type_by_symbol (const gchar* symbol)
{
static GModule *module = NULL;
GTypeGetFunc func;
- GType type;
if (!module)
module = g_module_open (NULL, 0);
if (!g_module_symbol (module, symbol, (gpointer)&func))
- return NULL;
+ return G_TYPE_INVALID;
- type = func ();
- if (type == G_TYPE_INVALID)
- return NULL;
-
- return g_strdup (g_type_name (type));
+ return func ();
}
static void
@@ -303,13 +324,12 @@ parse_object (GMarkupParseContext *context,
const gchar **values,
GError **error)
{
+ GType object_type = G_TYPE_INVALID;
ObjectInfo *object_info;
- ChildInfo* child_info;
- int i;
- gchar *object_class = NULL;
+ ChildInfo *child_info;
gchar *object_id = NULL;
gchar *constructor = NULL;
- gint line, line2;
+ gint i, line, line2;
child_info = state_peek_info (data, ChildInfo);
if (child_info && strcmp (child_info->tag.name, "object") == 0)
@@ -321,7 +341,8 @@ parse_object (GMarkupParseContext *context,
for (i = 0; names[i] != NULL; i++)
{
if (strcmp (names[i], "class") == 0)
- object_class = g_strdup (values[i]);
+ /* Make sure the class is initialized so we have intern string available */
+ object_type = gtk_builder_get_type_from_name (data->builder, values[i]);
else if (strcmp (names[i], "id") == 0)
object_id = g_strdup (values[i]);
else if (strcmp (names[i], "constructor") == 0)
@@ -332,8 +353,8 @@ parse_object (GMarkupParseContext *context,
* it's guaranteed afterwards that g_type_from_name on the name
* will return our GType
*/
- object_class = _get_type_by_symbol (values[i]);
- if (!object_class)
+ object_type = _get_type_by_symbol (values[i]);
+ if (object_type == G_TYPE_INVALID)
{
g_markup_parse_context_get_position (context, &line, NULL);
g_set_error (error, GTK_BUILDER_ERROR,
@@ -350,7 +371,7 @@ parse_object (GMarkupParseContext *context,
}
}
- if (!object_class)
+ if (object_type == G_TYPE_INVALID)
{
error_missing_attribute (data, element_name, "class", error);
return;
@@ -379,7 +400,6 @@ parse_object (GMarkupParseContext *context,
}
else
{
- g_free (object_class);
g_free (object_id);
g_free (constructor);
return;
@@ -387,7 +407,7 @@ parse_object (GMarkupParseContext *context,
}
object_info = g_slice_new0 (ObjectInfo);
- object_info->class_name = object_class;
+ object_info->object_type = object_type;
object_info->id = object_id;
object_info->constructor = constructor;
state_push (data, object_info);
@@ -420,7 +440,6 @@ free_object_info (ObjectInfo *info)
(GFunc)free_property_info, NULL);
g_slist_free (info->properties);
g_free (info->constructor);
- g_free (info->class_name);
g_free (info->id);
g_slice_free (ObjectInfo, info);
}
@@ -443,6 +462,7 @@ parse_child (ParserData *data,
{
ObjectInfo* object_info;
ChildInfo *child_info;
+ GObject *object;
guint i;
object_info = state_peek_info (data, ObjectInfo);
@@ -451,6 +471,9 @@ parse_child (ParserData *data,
error_invalid_tag (data, element_name, NULL, error);
return;
}
+
+ GTK_NOTE (BUILDER, g_print ("parsing child of parent type %s\n",
+ object_info->object ? G_OBJECT_TYPE_NAME (object_info->object) : "(none)"));
child_info = g_slice_new0 (ChildInfo);
state_push (data, child_info);
@@ -467,7 +490,9 @@ parse_child (ParserData *data,
child_info->parent = (CommonInfo*)object_info;
- object_info->object = builder_construct (data, object_info, error);
+ object = builder_construct (data, object_info, error);
+ object_info->object = object;
+
}
static void
@@ -489,6 +514,7 @@ parse_property (ParserData *data,
gchar *name = NULL;
gchar *context = NULL;
gboolean translatable = FALSE;
+ gboolean external = FALSE;
ObjectInfo *object_info;
int i;
@@ -517,6 +543,11 @@ parse_property (ParserData *data,
{
context = g_strdup (values[i]);
}
+ else if (strcmp (names[i], "external-object") == 0)
+ {
+ if (!_gtk_builder_boolean_from_string (values[i], &external, error))
+ return;
+ }
else
{
error_invalid_attribute (data, element_name, names[i], error);
@@ -531,20 +562,22 @@ parse_property (ParserData *data,
}
info = g_slice_new0 (PropertyInfo);
- info->name = name;
+ info->name = g_intern_string (name);
info->translatable = translatable;
- info->context = context;
- info->text = g_string_new ("");
+ info->context = context;
+ info->text = g_string_new ("");
+ info->external = external;
state_push (data, info);
info->tag.name = element_name;
+
+ g_free (name);
}
static void
free_property_info (PropertyInfo *info)
{
g_free (info->data);
- g_free (info->name);
g_slice_free (PropertyInfo, info);
}
@@ -556,12 +589,13 @@ parse_signal (ParserData *data,
GError **error)
{
SignalInfo *info;
- gchar *name = NULL;
+ const gchar *name = NULL;
gchar *handler = NULL;
gchar *object = NULL;
gboolean after = FALSE;
gboolean swapped = FALSE;
gboolean swapped_set = FALSE;
+ gboolean external = FALSE;
ObjectInfo *object_info;
int i;
@@ -575,7 +609,7 @@ parse_signal (ParserData *data,
for (i = 0; names[i] != NULL; i++)
{
if (strcmp (names[i], "name") == 0)
- name = g_strdup (values[i]);
+ name = values[i];
else if (strcmp (names[i], "handler") == 0)
handler = g_strdup (values[i]);
else if (strcmp (names[i], "after") == 0)
@@ -589,6 +623,11 @@ parse_signal (ParserData *data,
return;
swapped_set = TRUE;
}
+ else if (strcmp (names[i], "external-object") == 0)
+ {
+ if (!_gtk_builder_boolean_from_string (values[i], &external, error))
+ return;
+ }
else if (strcmp (names[i], "object") == 0)
object = g_strdup (values[i]);
else if (strcmp (names[i], "last_modification_time") == 0)
@@ -617,24 +656,120 @@ parse_signal (ParserData *data,
swapped = TRUE;
info = g_slice_new0 (SignalInfo);
- info->name = name;
+ info->name = g_intern_string (name);
info->handler = handler;
if (after)
info->flags |= G_CONNECT_AFTER;
if (swapped)
info->flags |= G_CONNECT_SWAPPED;
+ info->external = external;
info->connect_object_name = object;
state_push (data, info);
info->tag.name = element_name;
}
+static void
+parse_template (ParserData *data,
+ const gchar *element_name,
+ const gchar **names,
+ const gchar **values,
+ GError **error)
+{
+ GObject *parent = data->template_object;
+ const gchar *parent_class = NULL;
+ const gchar *class_name = NULL;
+ const gchar *id = NULL;
+ GType parent_type, class_type;
+ ObjectInfo *object_info;
+ gint i;
+
+ if (parent == NULL)
+ {
+ error_invalid_tag (data, element_name, NULL, error);
+ return;
+ }
+
+ for (i = 0; names[i] != NULL; i++)
+ {
+ if (strcmp (names[i], "class") == 0)
+ class_name = values[i];
+ else if (strcmp (names[i], "parent") == 0)
+ parent_class = values[i];
+ else if (strcmp (names[i], "id") == 0)
+ id = values[i];
+ else
+ {
+ error_invalid_attribute (data, element_name, names[i], error);
+ return;
+ }
+ }
+
+ if (!class_name)
+ {
+ error_missing_attribute (data, element_name, "class", error);
+ return;
+ }
+
+ if (!parent_class)
+ {
+ error_missing_attribute (data, element_name, "parent", error);
+ return;
+ }
+
+ if (!id)
+ {
+ error_missing_attribute (data, element_name, "id", error);
+ return;
+ }
+
+ if (strcmp (id, "this"))
+ {
+ error_generic (error, GTK_BUILDER_ERROR_INVALID_VALUE, data,
+ element_name, "%s template should be named 'this' not %s '%s'",
+ class_name, id);
+ return;
+ }
+
+ if ((class_type = g_type_from_name (class_name)) == G_TYPE_INVALID)
+ {
+ error_generic (error, GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH, data,
+ element_name, "invalid class type found '%s'", class_name);
+ return;
+ }
+
+ parent_type = G_OBJECT_TYPE (parent);
+
+ if (!g_type_is_a (parent_type, class_type))
+ {
+ error_generic (error, GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH, data,
+ element_name, "this template is for a class type %s not for %s",
+ class_name, G_OBJECT_TYPE_NAME (parent));
+ return;
+ }
+
+ if (!g_type_is_a (class_type, g_type_from_name (parent_class)))
+ {
+ error_generic (error, GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH, data,
+ element_name, "class %s should derive from parent %s",
+ class_name, parent_class);
+ return;
+ }
+
+ object_info = g_slice_new0 (ObjectInfo);
+ object_info->object = parent;
+ object_info->object_type = parent_type;
+ object_info->id = g_strdup (_gtk_builder_object_get_name (parent));
+ object_info->tag.name = "object";
+
+ state_push (data, object_info);
+}
+
/* Called by GtkBuilder */
void
_free_signal_info (SignalInfo *info,
gpointer user_data)
{
- g_free (info->name);
g_free (info->handler);
g_free (info->connect_object_name);
g_free (info->object_name);
@@ -892,6 +1027,8 @@ start_element (GMarkupParseContext *context,
parse_interface (data, element_name, names, values, error);
else if (strcmp (element_name, "menu") == 0)
_gtk_builder_menu_start (data, element_name, names, values, error);
+ else if (strcmp (element_name, "template") == 0)
+ parse_template (data, element_name, names, values, error);
else if (strcmp (element_name, "placeholder") == 0)
{
/* placeholder has no special treatmeant, but it needs an
@@ -1054,6 +1191,25 @@ end_element (GMarkupParseContext *context,
else if (strcmp (element_name, "placeholder") == 0)
{
}
+ else if (strcmp (element_name, "template") == 0)
+ {
+ if (data->template_object)
+ {
+ ObjectInfo *object_info = state_pop_info (data, ObjectInfo);
+
+ object_info->properties = g_slist_reverse (object_info->properties);
+
+ /* This is just to apply properties to the external object */
+ _gtk_builder_construct (data->builder, object_info, error);
+
+ if (object_info->signals)
+ _gtk_builder_add_signals (data->builder, object_info->signals);
+
+ free_object_info (object_info);
+
+ data->template_object = NULL;
+ }
+ }
else
{
g_assert_not_reached ();
@@ -1126,6 +1282,7 @@ static const GMarkupParser parser = {
void
_gtk_builder_parser_parse_buffer (GtkBuilder *builder,
+ GObject *parent,
const gchar *filename,
const gchar *buffer,
gsize length,
@@ -1150,6 +1307,12 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
data->object_ids = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify)g_free, NULL);
+ if (parent)
+ {
+ GTK_NOTE (BUILDER, g_print ("parsing with contextual parent %s ptr %p\n", G_OBJECT_TYPE_NAME (parent), parent));
+ data->template_object = parent;
+ }
+
data->requested_objects = NULL;
if (requested_objs)
{
diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h
index 3667ed5..9e4b292 100644
--- a/gtk/gtkbuilderprivate.h
+++ b/gtk/gtkbuilderprivate.h
@@ -31,7 +31,7 @@ typedef struct {
typedef struct {
TagInfo tag;
- gchar *class_name;
+ GType object_type;
gchar *id;
gchar *constructor;
GSList *properties;
@@ -58,18 +58,20 @@ typedef struct {
typedef struct {
TagInfo tag;
- gchar *name;
+ const gchar *name; /* Intern string */
GString *text;
gchar *data;
- gboolean translatable;
gchar *context;
+ guint8 translatable : 1;
+ guint8 external : 1;
} PropertyInfo;
typedef struct {
TagInfo tag;
gchar *object_name;
- gchar *name;
+ const gchar *name; /* Intern string */
gchar *handler;
+ gboolean external;
GConnectFlags flags;
gchar *connect_object_name;
} SignalInfo;
@@ -107,12 +109,14 @@ typedef struct {
gint cur_object_level;
GHashTable *object_ids;
+ GObject *template_object;
} ParserData;
typedef GType (*GTypeGetFunc) (void);
/* Things only GtkBuilder should use */
void _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
+ GObject *parent,
const gchar *filename,
const gchar *buffer,
gsize length,
@@ -159,5 +163,6 @@ void _gtk_builder_menu_start (ParserData *parser_data,
GError **error);
void _gtk_builder_menu_end (ParserData *parser_data);
+const gchar * _gtk_builder_object_get_name (GObject *object);
#endif /* __GTK_BUILDER_PRIVATE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]