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
This commit is contained in:
Tristan Van Berkom
2010-06-07 14:30:28 -04:00
committed by Juan Pablo Ugarte
parent d8fae21b1c
commit 4d2bf8ef4a
5 changed files with 453 additions and 109 deletions

View File

@@ -1130,7 +1130,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

View File

@@ -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 (&parameter.value, G_OBJECT_TYPE (object));
g_value_set_object (&parameter.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 (&parameter.value, G_OBJECT_TYPE (object));
g_value_set_object (&parameter.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, &parameter.value, &error))
prop->data, &parameter.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,
&parameters,
&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,29 +759,16 @@ _gtk_builder_construct (GtkBuilder *builder,
g_value_unset (&param->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)
@@ -872,6 +915,31 @@ guint
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;
@@ -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);
@@ -1000,6 +1068,31 @@ guint
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;
@@ -1007,12 +1100,12 @@ gtk_builder_add_from_resource (GtkBuilder *builder,
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);
@@ -1152,6 +1243,34 @@ gtk_builder_add_from_string (GtkBuilder *builder,
const gchar *buffer,
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;
@@ -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);
}

View File

@@ -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);
@@ -130,22 +132,38 @@ guint gtk_builder_add_objects_from_file (GtkBuilder *builder,
GError **error);
GDK_AVAILABLE_IN_3_4
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);
@@ -153,15 +171,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:

View File

@@ -64,6 +64,32 @@ state_pop (ParserData *data)
#define state_peek_info(data, st) ((st*)state_peek(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,
@@ -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)
{

View File

@@ -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__ */