Compare commits

...

19 Commits

Author SHA1 Message Date
Juan Pablo Ugarte
1cffd1de56 Added parent property to external-object tag 2013-02-13 19:05:02 -03:00
Juan Pablo Ugarte
6b141bff77 Updated to new external-object/template syntaxt 2012-12-10 11:38:49 -03:00
Juan Pablo Ugarte
13870e733c Renamed <template> tag with <external-object> and keep <template> tag for new inline type definitions 2012-12-10 11:24:47 -03:00
Juan Pablo Ugarte
e0ce659f34 Removed return value of gtk_builder_expose_object()
and added check to notify when the object exist
2012-12-10 11:24:47 -03:00
Juan Pablo Ugarte
dc78589ce2 Added inline template creation support. Now builder creates new types dinamically when needed.
that is when it finds a template element without an asociated external object.
2012-12-10 11:24:47 -03:00
Juan Pablo Ugarte
e6007f10ac Added template_id parameter to gtk_container_class_set_template_from_*() functions
This way you can call your template "this" or "that" or as you wish.
2012-12-10 11:24:46 -03:00
Juan Pablo Ugarte
2b422848a3 Added return value to gtk_builder_expose_object() and made it ignore if the object id is in use. 2012-12-10 11:24:46 -03:00
Juan Pablo Ugarte
b39ae75a0e Merge external object namespace with builder built objects 2012-12-10 11:24:46 -03:00
Juan Pablo Ugarte
828338fbf8 Added signal data test to test_expose_object() 2012-12-10 11:24:46 -03:00
Juan Pablo Ugarte
329335132c Removed gtk_builder_add_to_parent_*() functions, instead you can use gtk_builder_expose_object()
to expose the template object and simply call gtk_builder_add_from_*()

Added test_expose_object() test case
2012-12-10 11:24:46 -03:00
Juan Pablo Ugarte
7d53df8ccb Removed bogus lines changes like spaces and such. 2012-12-10 11:24:46 -03:00
Juan Pablo Ugarte
bfdf7571b1 Added gtk_builder_add_to_parent() test case. 2012-12-10 11:24:46 -03:00
Juan Pablo Ugarte
976b14cb63 Added template_id parameter to gtk_builder_add_to_parent_*() functions
This makes it more consistent since now you have to name the template
and let you have more than one template in a file.
2012-12-10 11:24:46 -03:00
Juan Pablo Ugarte
af394406f7 gtk_container_class_set_template() Clear list of classes with templates since
this function in rare occacions can be called more than once. (Glade)
2012-12-10 11:24:46 -03:00
Juan Pablo Ugarte
a6685346f1 Revert "Implemented GtkDialog and GtkMessageDialog using new template API."
This reverts commit a095cc6ac56876a55614a5fbb57548fefcc66191.
2012-12-10 11:24:46 -03:00
Juan Pablo Ugarte
0f257f1892 Added gtk_container_class_set_template_from_string() 2012-12-10 11:24:46 -03:00
Juan Pablo Ugarte
04444664a4 Implemented GtkDialog and GtkMessageDialog using new template API. 2012-12-10 11:24:46 -03:00
Juan Pablo Ugarte
a3b93112bb Composite Widgets using GtkBuilder scripts
Added new API to enable composite widget implementations using builder scripts
  gtk_container_class_set_template_from_resource()
  gtk_container_class_declare_internal_child()
  gtk_container_class_set_connect_func()
2012-12-10 11:24:46 -03:00
Tristan Van Berkom
4d2bf8ef4a 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
2012-12-10 11:24:46 -03:00
10 changed files with 1273 additions and 268 deletions

View File

@@ -536,6 +536,7 @@ gtk_builder_add_objects_from_string
gtk_builder_add_objects_from_resource
gtk_builder_get_object
gtk_builder_get_objects
gtk_builder_expose_object
gtk_builder_connect_signals
gtk_builder_connect_signals_full
gtk_builder_set_translation_domain

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,24 @@
* (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 &mdash; 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 and objects exposed via gtk_builder_expose_object().
*
* 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 &lt;signal&gt; 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. 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
@@ -272,6 +274,8 @@ struct _GtkBuilderPrivate
GSList *signals;
gchar *filename;
gchar *resource_prefix;
GType ignore_type;
};
G_DEFINE_TYPE (GtkBuilder, gtk_builder, G_TYPE_OBJECT)
@@ -490,22 +494,24 @@ 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 (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
{
g_value_init (&parameter.value, G_OBJECT_TYPE (object));
g_value_set_object (&parameter.value, object);
}
else
{
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);
@@ -562,7 +568,7 @@ gtk_builder_get_internal_child (GtkBuilder *builder,
obj = gtk_buildable_get_internal_child (GTK_BUILDABLE (info->object),
builder,
childname);
};
}
if (!obj)
{
@@ -574,6 +580,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 +620,32 @@ _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,
/* Safeguard to avoid recursion if we are building a new type with builder */
if (object_type == builder->priv->ignore_type)
return NULL;
gtk_builder_get_parameters (builder, info->object_type,
info->id,
info->properties,
&parameters,
&construct_parameters);
if (info->constructor)
if (info->object)
{
/* external_object */
obj = g_object_ref (info->object);
}
else if (info->constructor)
{
GObject *constructor;
@@ -662,7 +702,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 +743,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)
@@ -851,6 +878,41 @@ gtk_builder_new (void)
return g_object_new (GTK_TYPE_BUILDER, NULL);
}
static guint
gtk_builder_add_from_file_real (GtkBuilder *builder,
const gchar *filename,
gchar **object_ids,
GError **error)
{
GError *tmp_error = NULL;
gchar *buffer;
gsize length;
if (!g_file_get_contents (filename, &buffer, &length, &tmp_error))
{
g_propagate_error (error, tmp_error);
return 0;
}
g_free (builder->priv->filename);
g_free (builder->priv->resource_prefix);
builder->priv->filename = g_strdup (filename);
builder->priv->resource_prefix = NULL;
_gtk_builder_parser_parse_buffer (builder, filename, buffer, length,
object_ids, &tmp_error);
g_free (buffer);
if (tmp_error != NULL)
{
g_propagate_error (error, tmp_error);
return 0;
}
return 1;
}
/**
* gtk_builder_add_from_file:
* @builder: a #GtkBuilder
@@ -873,41 +935,11 @@ gtk_builder_add_from_file (GtkBuilder *builder,
const gchar *filename,
GError **error)
{
gchar *buffer;
gsize length;
GError *tmp_error;
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
g_return_val_if_fail (filename != NULL, 0);
g_return_val_if_fail (error == NULL || *error == NULL, 0);
tmp_error = NULL;
if (!g_file_get_contents (filename, &buffer, &length, &tmp_error))
{
g_propagate_error (error, tmp_error);
return 0;
}
g_free (builder->priv->filename);
g_free (builder->priv->resource_prefix);
builder->priv->filename = g_strdup (filename);
builder->priv->resource_prefix = NULL;
_gtk_builder_parser_parse_buffer (builder, filename,
buffer, length,
NULL,
&tmp_error);
g_free (buffer);
if (tmp_error != NULL)
{
g_propagate_error (error, tmp_error);
return 0;
}
return 1;
return gtk_builder_add_from_file_real (builder, filename, NULL, error);
}
/**
@@ -941,34 +973,52 @@ gtk_builder_add_objects_from_file (GtkBuilder *builder,
gchar **object_ids,
GError **error)
{
gchar *buffer;
gsize length;
GError *tmp_error;
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
g_return_val_if_fail (filename != NULL, 0);
g_return_val_if_fail (object_ids != NULL && object_ids[0] != NULL, 0);
g_return_val_if_fail (error == NULL || *error == NULL, 0);
return gtk_builder_add_from_file_real (builder, filename, object_ids, error);
}
static guint
gtk_builder_add_from_resource_real (GtkBuilder *builder,
const gchar *path,
gchar **object_ids,
GError **error)
{
GError *tmp_error;
GBytes *data;
char *filename_for_errors;
char *slash;
tmp_error = NULL;
if (!g_file_get_contents (filename, &buffer, &length, &tmp_error))
data = g_resources_lookup_data (path, 0, &tmp_error);
if (data == NULL)
{
g_propagate_error (error, tmp_error);
return 0;
}
g_free (builder->priv->filename);
g_free (builder->priv->resource_prefix);
builder->priv->filename = g_strdup (filename);
builder->priv->resource_prefix = NULL;
builder->priv->filename = g_strdup (".");
_gtk_builder_parser_parse_buffer (builder, filename,
buffer, length,
object_ids,
&tmp_error);
slash = strrchr (path, '/');
if (slash != NULL)
builder->priv->resource_prefix = g_strndup (path, slash - path + 1);
else
builder->priv->resource_prefix = g_strdup ("/");
g_free (buffer);
filename_for_errors = g_strconcat ("<resource>", path, NULL);
_gtk_builder_parser_parse_buffer (builder, filename_for_errors,
g_bytes_get_data (data, NULL), g_bytes_get_size (data),
object_ids, &tmp_error);
g_free (filename_for_errors);
g_bytes_unref (data);
if (tmp_error != NULL)
{
@@ -1001,53 +1051,11 @@ gtk_builder_add_from_resource (GtkBuilder *builder,
const gchar *resource_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 (error == NULL || *error == NULL, 0);
tmp_error = NULL;
data = g_resources_lookup_data (resource_path, 0, &tmp_error);
if (data == NULL)
{
g_propagate_error (error, tmp_error);
return 0;
}
g_free (builder->priv->filename);
g_free (builder->priv->resource_prefix);
builder->priv->filename = g_strdup (".");
slash = strrchr (resource_path, '/');
if (slash != NULL)
builder->priv->resource_prefix =
g_strndup (resource_path, slash - resource_path + 1);
else
builder->priv->resource_prefix =
g_strdup ("/");
filename_for_errors = g_strconcat ("<resource>", resource_path, NULL);
_gtk_builder_parser_parse_buffer (builder, filename_for_errors,
g_bytes_get_data (data, NULL), g_bytes_get_size (data),
NULL,
&tmp_error);
g_free (filename_for_errors);
g_bytes_unref (data);
if (tmp_error != NULL)
{
g_propagate_error (error, tmp_error);
return 0;
}
return 1;
return gtk_builder_add_from_resource_real (builder, resource_path, NULL, error);
}
/**
@@ -1081,46 +1089,31 @@ gtk_builder_add_objects_from_resource (GtkBuilder *builder,
gchar **object_ids,
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 (object_ids != NULL && object_ids[0] != NULL, 0);
g_return_val_if_fail (error == NULL || *error == NULL, 0);
tmp_error = NULL;
return gtk_builder_add_from_resource_real (builder, resource_path,
object_ids, error);
}
data = g_resources_lookup_data (resource_path, 0, &tmp_error);
if (data == NULL)
{
g_propagate_error (error, tmp_error);
return 0;
}
static guint
gtk_builder_add_from_string_real (GtkBuilder *builder,
const gchar *buffer,
gsize length,
gchar **object_ids,
GError **error)
{
GError *tmp_error = NULL;
g_free (builder->priv->filename);
g_free (builder->priv->resource_prefix);
builder->priv->filename = g_strdup (".");
builder->priv->resource_prefix = NULL;
slash = strrchr (resource_path, '/');
if (slash != NULL)
builder->priv->resource_prefix =
g_strndup (resource_path, slash - resource_path + 1);
else
builder->priv->resource_prefix =
g_strdup ("/");
filename_for_errors = g_strconcat ("<resource>", resource_path, NULL);
_gtk_builder_parser_parse_buffer (builder, filename_for_errors,
g_bytes_get_data (data, NULL), g_bytes_get_size (data),
object_ids,
&tmp_error);
g_free (filename_for_errors);
g_bytes_unref (data);
_gtk_builder_parser_parse_buffer (builder, "<input>", buffer, length,
object_ids, &tmp_error);
if (tmp_error != NULL)
{
g_propagate_error (error, tmp_error);
@@ -1153,30 +1146,11 @@ gtk_builder_add_from_string (GtkBuilder *builder,
gsize length,
GError **error)
{
GError *tmp_error;
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
g_return_val_if_fail (buffer != NULL, 0);
g_return_val_if_fail (error == NULL || *error == NULL, 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;
_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;
return gtk_builder_add_from_string_real (builder, buffer, length, NULL, error);
}
/**
@@ -1211,32 +1185,13 @@ gtk_builder_add_objects_from_string (GtkBuilder *builder,
gchar **object_ids,
GError **error)
{
GError *tmp_error;
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
g_return_val_if_fail (buffer != NULL, 0);
g_return_val_if_fail (object_ids != NULL && object_ids[0] != NULL, 0);
g_return_val_if_fail (error == NULL || *error == NULL, 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;
_gtk_builder_parser_parse_buffer (builder, "<input>",
buffer, length,
object_ids,
&tmp_error);
if (tmp_error != NULL)
{
g_propagate_error (error, tmp_error);
return 0;
}
return 1;
return gtk_builder_add_from_string_real (builder, buffer, length,
object_ids, error);
}
/**
@@ -1340,6 +1295,39 @@ 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
*
* Add @object to the @builder object pool so it can be referenced just like any
* other object built by builder.
*
* To make this function even more useful a new special entry point element
* &lt;external-object&gt; is defined. It is similar to &lt;object&gt; but has
* to reference an external object exposed with this function.
* This way you can change properties and even add children to an
* external object using builder, not just reference it.
*
* Since: 3.8
**/
void
gtk_builder_expose_object (GtkBuilder *builder,
const gchar *name,
GObject *object)
{
g_return_if_fail (GTK_IS_BUILDER (builder));
g_return_if_fail (name && name[0]);
g_return_if_fail (gtk_builder_get_object (builder, name) == NULL);
object_set_name (object, name);
g_hash_table_insert (builder->priv->objects,
g_strdup (name),
g_object_ref (object));
}
typedef struct {
GModule *module;
gpointer data;
@@ -1480,7 +1468,8 @@ 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);
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,
@@ -2088,3 +2077,15 @@ _gtk_builder_get_absolute_filename (GtkBuilder *builder, const gchar *string)
return filename;
}
void
_gtk_builder_set_ignore_type (GtkBuilder *builder, GType ignore_type)
{
builder->priv->ignore_type = ignore_type;
}
GType
_gtk_builder_get_ignore_type (GtkBuilder *builder)
{
return builder->priv->ignore_type;
}

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);
@@ -141,6 +143,9 @@ guint gtk_builder_add_objects_from_string (GtkBuilder *builder,
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);
void gtk_builder_connect_signals_full (GtkBuilder *builder,

View File

@@ -1,6 +1,6 @@
start = element interface {
attribute domain { text } ?,
( requires | object | menu ) *
( requires | object | external-object | template | menu ) *
}
requires = element requires {
@@ -16,9 +16,23 @@ object = element object {
(property | signal | child | ANY) *
}
object = element external-object {
attribute id { xsd:ID },
attribute class { text },
(property | signal | child | ANY) *
}
object = element template {
attribute id { xsd:ID },
attribute class { text },
attribute parent { text },
(property | signal | child | ANY) *
}
property = element property {
attribute name { text },
attribute translatable { "yes" | "no" } ?,
attribute external-object { "yes" | "no" } ?,
attribute comments { text } ?,
attribute context { text } ?,
text ?

View File

@@ -29,7 +29,7 @@
#include "gtkversion.h"
#include "gtktypebuiltins.h"
#include "gtkintl.h"
#include "gtkcontainer.h"
static void free_property_info (PropertyInfo *info);
static void free_object_info (ObjectInfo *info);
@@ -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,
@@ -190,6 +216,13 @@ builder_construct (ParserData *data,
if (object_info->object)
return object_info->object;
/* Safeguard to avoid recursion if we are building a new type with builder
* _gtk_builder_construct() also checks for this, but there is no need to
* reverse the property list if we are not going to build the object.
*/
if (object_info->object_type == _gtk_builder_get_ignore_type (data->builder))
return NULL;
object_info->properties = g_slist_reverse (object_info->properties);
object = _gtk_builder_construct (data->builder, object_info, error);
@@ -203,24 +236,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
@@ -295,6 +323,29 @@ is_requested_object (const gchar *object,
return FALSE;
}
static gboolean
parser_add_object_id (ParserData *data,
const gchar *object_id,
GError **error)
{
gint line, line2;
g_markup_parse_context_get_position (data->ctx, &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 TRUE;
}
g_hash_table_insert (data->object_ids, g_strdup (object_id), GINT_TO_POINTER (line));
return FALSE;
}
static void
parse_object (GMarkupParseContext *context,
ParserData *data,
@@ -303,13 +354,13 @@ 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;
gchar *object_id = NULL;
gchar *constructor = NULL;
gint line, line2;
ChildInfo *child_info;
const gchar *object_class = NULL;
const gchar *object_id = NULL;
const gchar *constructor = NULL;
gint i, line;
child_info = state_peek_info (data, ChildInfo);
if (child_info && strcmp (child_info->tag.name, "object") == 0)
@@ -321,19 +372,19 @@ parse_object (GMarkupParseContext *context,
for (i = 0; names[i] != NULL; i++)
{
if (strcmp (names[i], "class") == 0)
object_class = g_strdup (values[i]);
object_class = values[i];
else if (strcmp (names[i], "id") == 0)
object_id = g_strdup (values[i]);
object_id = values[i];
else if (strcmp (names[i], "constructor") == 0)
constructor = g_strdup (values[i]);
constructor = values[i];
else if (strcmp (names[i], "type-func") == 0)
{
/* Call the GType function, and return the name of the GType,
* 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 +401,7 @@ parse_object (GMarkupParseContext *context,
}
}
if (!object_class)
if (object_type == G_TYPE_INVALID && !object_class)
{
error_missing_attribute (data, element_name, "class", error);
return;
@@ -362,6 +413,20 @@ parse_object (GMarkupParseContext *context,
return;
}
if (object_type == G_TYPE_INVALID)
{
/* Make sure the class is initialized so we have intern string available */
object_type = gtk_builder_get_type_from_name (data->builder, object_class);
if (object_type == G_TYPE_INVALID)
{
g_set_error (error, GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_VALUE,
_("Invalid class: '%s'"), object_class);
return;
}
}
++data->cur_object_level;
/* check if we reached a requested object (if it is specified) */
@@ -379,36 +444,24 @@ parse_object (GMarkupParseContext *context,
}
else
{
g_free (object_class);
g_free (object_id);
g_free (constructor);
return;
}
}
else if ((data->in_external_object && !data->external_object) ||
data->template_level)
return;
object_info = g_slice_new0 (ObjectInfo);
object_info->class_name = object_class;
object_info->id = object_id;
object_info->constructor = constructor;
object_info->object_type = object_type;
object_info->id = g_strdup (object_id);
object_info->constructor = g_strdup (constructor);
state_push (data, object_info);
object_info->tag.name = element_name;
if (child_info)
object_info->parent = (CommonInfo*)child_info;
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));
parser_add_object_id (data, object_id, error);
}
static void
@@ -420,7 +473,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);
}
@@ -531,20 +583,21 @@ 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 ("");
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,7 +609,7 @@ parse_signal (ParserData *data,
GError **error)
{
SignalInfo *info;
gchar *name = NULL;
const gchar *name = NULL;
gchar *handler = NULL;
gchar *object = NULL;
gboolean after = FALSE;
@@ -575,7 +628,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)
@@ -617,7 +670,7 @@ 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;
@@ -629,12 +682,303 @@ parse_signal (ParserData *data,
info->tag.name = element_name;
}
typedef struct
{
const gchar *tmpl_class;
gboolean found, done, in_tmpl;
GString *xml;
} TemplateParseData;
static void
extract_template_start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error)
{
TemplateParseData *state = user_data;
gint i;
if (state->done) return;
if (g_strcmp0 (element_name, "template") == 0)
{
for (i = 0; attribute_names[i]; i++)
{
if (!g_strcmp0 (attribute_names[i], "class"))
state->found = (g_strcmp0 (attribute_values[i], state->tmpl_class) == 0);
}
if (state->found)
{
state->in_tmpl = TRUE;
g_string_append_printf (state->xml, "<external-object class=\"%s\" id=\"%s\">",
state->tmpl_class,
state->tmpl_class);
return;
}
else
g_string_append_printf (state->xml, "<%s", element_name);
}
else
g_string_append_printf (state->xml, "<%s", element_name);
for (i = 0; attribute_names[i]; i++)
{
g_string_append_printf (state->xml, " %s=\"%s\"",
attribute_names[i], attribute_values[i]);
}
g_string_append_printf (state->xml, ">");
}
static void
extract_template_end_element (GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data,
GError **error)
{
TemplateParseData *state = user_data;
if (state->done) return;
if (g_strcmp0 (element_name, "template") == 0 && state->in_tmpl)
{
state->in_tmpl = FALSE;
g_string_append (state->xml, "</external-object>");
}
else
g_string_append_printf (state->xml, "</%s>", element_name);
if (g_strcmp0 (element_name, "interface") == 0 && state->found)
state->done = TRUE;
}
static void
extract_template_text (GMarkupParseContext *context,
const gchar *text,
gsize text_len,
gpointer user_data,
GError **error)
{
TemplateParseData *state = user_data;
if (state->done) return;
g_string_append_len (state->xml, text, text_len);
}
static gchar *
extract_template (const gchar *buffer, const gchar *class_name)
{
GMarkupParser parser = { extract_template_start_element, extract_template_end_element, extract_template_text };
TemplateParseData state = { class_name, FALSE, FALSE, FALSE, g_string_new ("")};
GMarkupParseContext *context;
context = g_markup_parse_context_new (&parser,
G_MARKUP_TREAT_CDATA_AS_TEXT |
G_MARKUP_PREFIX_ERROR_POSITION,
&state, NULL);
g_markup_parse_context_parse (context, buffer, -1, NULL);
g_markup_parse_context_end_parse (context, NULL);
g_markup_parse_context_free (context);
if (state.done)
{
gchar *retval = state.xml->str;
g_string_free (state.xml, FALSE);
g_file_set_contents ("/tmp/a.dump", retval, -1, NULL);
return retval;
}
g_string_free (state.xml, TRUE);
return NULL;
}
typedef struct
{
GTypeInfo *info;
gchar *tmpl;
} TmplClassData;
static void
composite_template_derived_class_init (gpointer g_class, gpointer class_data)
{
TmplClassData *data = class_data;
gtk_container_class_set_template_from_string (g_class, data->tmpl,
G_OBJECT_CLASS_NAME (g_class));
}
static GType
create_inline_type (GType parent_type,
const gchar *class_name,
const gchar *template_xml)
{
TmplClassData *tmpl;
GTypeQuery query;
GTypeInfo *info;
g_type_query (parent_type, &query);
tmpl = g_new0 (TmplClassData, 1);
tmpl->info = info = g_new0 (GTypeInfo, 1);
tmpl->tmpl = extract_template (template_xml, class_name);
info->class_size = query.class_size;
info->class_init = composite_template_derived_class_init;
info->class_data = tmpl; /* Let it leak! */
info->instance_size = query.instance_size;
return g_type_register_static (parent_type, class_name, info, 0);
}
static void
parse_template (ParserData *data,
const gchar *element_name,
const gchar **names,
const gchar **values,
GError **error)
{
const gchar *parent_class = NULL;
const gchar *class_name = NULL;
const GSList *l, *p;
GType parent_type;
gint i;
data->template_level++;
if (data->template_level > 1 ||
!((l = g_markup_parse_context_get_element_stack (data->ctx)) &&
(p = g_slist_next (l)) && g_strcmp0 (p->data, "interface") == 0))
{
error_generic (error, GTK_BUILDER_ERROR_INVALID_TAG, data,
element_name, "non toplevel template found");
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
{
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 (g_type_from_name (class_name))
{
error_generic (error, GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH, data,
element_name, "template class '%s' already registered", class_name);
return;
}
if (!(parent_type = gtk_builder_get_type_from_name (data->builder, parent_class)))
{
error_generic (error, GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH, data,
element_name, "invalid parent class type found '%s'", parent_class);
return;
}
/* Generate inline type */
create_inline_type (parent_type, class_name, data->buffer);
}
static void
parse_external_object (ParserData *data,
const gchar *element_name,
const gchar **names,
const gchar **values,
GError **error)
{
const gchar *class_name = NULL;
const gchar *id = NULL;
GObject *parent;
gint i;
data->in_external_object = TRUE;
for (i = 0; names[i] != NULL; i++)
{
if (strcmp (names[i], "class") == 0)
class_name = values[i];
else if (strcmp (names[i], "id") == 0)
id = values[i];
else if (strcmp (names[i], "parent") == 0);
else
{
error_invalid_attribute (data, element_name, names[i], error);
return;
}
}
if (!class_name)
{
error_missing_attribute (data, element_name, "class", error);
return;
}
if (!id)
{
error_missing_attribute (data, element_name, "id", error);
return;
}
if (data->requested_objects == NULL &&
(parent = gtk_builder_get_object (data->builder, id)))
{
GType class_type, parent_type = G_OBJECT_TYPE (parent);
ObjectInfo *object_info;
if (!(class_type = g_type_from_name (class_name)))
{
error_generic (error, GTK_BUILDER_ERROR_TEMPLATE_CLASS_MISMATCH, data,
element_name, "invalid class type found '%s'", class_name);
return;
}
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;
}
/* push parent to build its children from the template */
object_info = g_slice_new0 (ObjectInfo);
object_info->object = data->external_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);
parser_add_object_id (data, object_info->id, error);
}
}
/* 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);
@@ -875,9 +1219,15 @@ start_element (GMarkupParseContext *context,
if (strcmp (element_name, "requires") == 0)
parse_requires (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, "external-object") == 0)
parse_external_object (data, element_name, names, values, error);
else if (strcmp (element_name, "object") == 0)
parse_object (context, data, element_name, names, values, error);
else if (data->requested_objects && !data->inside_requested_object)
else if ((data->requested_objects && !data->inside_requested_object) ||
(data->in_external_object && !data->external_object) ||
data->template_level)
{
/* If outside a requested object, simply ignore this tag */
return;
@@ -963,9 +1313,35 @@ end_element (GMarkupParseContext *context,
else if (strcmp (element_name, "interface") == 0)
{
}
else if (data->requested_objects && !data->inside_requested_object)
else if (strcmp (element_name, "template") == 0)
{
/* If outside a requested object, simply ignore this tag */
data->template_level--;
}
else if (strcmp (element_name, "external-object") == 0)
{
data->in_external_object = FALSE;
if (data->external_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->external_object = NULL;
}
}
else if ((data->requested_objects && !data->inside_requested_object) ||
(data->in_external_object && !data->external_object) ||
data->template_level)
{
/* If outside a requested object or template, simply ignore this tag */
return;
}
else if (strcmp (element_name, "menu") == 0)
@@ -1144,6 +1520,7 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
domain = gtk_builder_get_translation_domain (builder);
data = g_new0 (ParserData, 1);
data->buffer = buffer;
data->builder = builder;
data->filename = filename;
data->domain = g_strdup (domain);

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,17 +58,17 @@ typedef struct {
typedef struct {
TagInfo tag;
gchar *name;
const gchar *name; /* Intern string */
GString *text;
gchar *data;
gboolean translatable;
gchar *context;
guint8 translatable : 1;
} PropertyInfo;
typedef struct {
TagInfo tag;
gchar *object_name;
gchar *name;
const gchar *name; /* Intern string */
gchar *handler;
GConnectFlags flags;
gchar *connect_object_name;
@@ -91,6 +91,7 @@ typedef struct {
} SubParser;
typedef struct {
const gchar *buffer;
const gchar *last_element;
GtkBuilder *builder;
gchar *domain;
@@ -107,17 +108,21 @@ typedef struct {
gint cur_object_level;
GHashTable *object_ids;
GObject *external_object;
gint in_external_object;
gint template_level;
} ParserData;
typedef GType (*GTypeGetFunc) (void);
/* Things only GtkBuilder should use */
void _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
const gchar *filename,
const gchar *buffer,
gsize length,
gchar **requested_objs,
GError **error);
void _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
const gchar *filename,
const gchar *buffer,
gsize length,
gchar **requested_objs,
GError **error);
GObject * _gtk_builder_construct (GtkBuilder *builder,
ObjectInfo *info,
GError **error);
@@ -159,5 +164,9 @@ 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);
void _gtk_builder_set_ignore_type (GtkBuilder *builder, GType ignore_type);
GType _gtk_builder_get_ignore_type (GtkBuilder *builder);
#endif /* __GTK_BUILDER_PRIVATE_H__ */

View File

@@ -231,6 +231,36 @@
* </refsect2>
*/
typedef struct
{
gchar *name;
GType type;
guint offset;
gboolean private;
} InternalChildData;
typedef struct
{
gchar *name;
GObject *object;
} InternalChild;
typedef enum
{
TMPL_STRING,
TMPL_RESOURCE
} GtkContainerTemplateType;
struct _GtkContainerClassPrivate
{
GSList *tmpl_classes;
const gchar *tmpl, *tmpl_id;
glong tmpl_len;
GtkContainerTemplateType tmpl_type;
GtkBuilderConnectFunc connect_func;
GList *internal_children; /* InternalChildData list */
};
struct _GtkContainerPrivate
{
@@ -244,6 +274,8 @@ struct _GtkContainerPrivate
guint restyle_pending : 1;
guint resize_mode : 2;
guint request_mode : 2;
GArray *internal_children; /* InternalChild array */
};
enum {
@@ -271,6 +303,9 @@ static void gtk_container_base_class_finalize (GtkContainerClass *klass);
static void gtk_container_class_init (GtkContainerClass *klass);
static void gtk_container_init (GtkContainer *container);
static void gtk_container_destroy (GtkWidget *widget);
static GObject *gtk_container_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties);
static void gtk_container_set_property (GObject *object,
guint prop_id,
const GValue *value,
@@ -390,6 +425,8 @@ gtk_container_get_type (void)
GTK_TYPE_BUILDABLE,
&buildable_info);
g_type_add_class_private (container_type, sizeof (GtkContainerClassPrivate));
}
return container_type;
@@ -398,7 +435,19 @@ gtk_container_get_type (void)
static void
gtk_container_base_class_init (GtkContainerClass *class)
{
GtkContainerClassPrivate *priv;
/* reset instance specifc class fields that don't get inherited */
class->priv = priv = G_TYPE_CLASS_GET_PRIVATE (class,
GTK_TYPE_CONTAINER,
GtkContainerClassPrivate);
priv->tmpl = NULL;
priv->tmpl_id = NULL;
priv->tmpl_classes = NULL;
priv->connect_func = NULL;
priv->internal_children = NULL;
class->set_child_property = NULL;
class->get_child_property = NULL;
}
@@ -406,6 +455,7 @@ gtk_container_base_class_init (GtkContainerClass *class)
static void
gtk_container_base_class_finalize (GtkContainerClass *class)
{
GtkContainerClassPrivate *priv = class->priv;
GList *list, *node;
list = g_param_spec_pool_list_owned (_gtk_widget_child_property_pool, G_OBJECT_CLASS_TYPE (class));
@@ -418,6 +468,8 @@ gtk_container_base_class_finalize (GtkContainerClass *class)
g_param_spec_unref (pspec);
}
g_list_free (list);
g_slist_free (priv->tmpl_classes);
}
static void
@@ -431,6 +483,7 @@ gtk_container_class_init (GtkContainerClass *class)
vadjustment_key_id = g_quark_from_static_string (vadjustment_key);
hadjustment_key_id = g_quark_from_static_string (hadjustment_key);
gobject_class->constructor = gtk_container_constructor;
gobject_class->set_property = gtk_container_set_property;
gobject_class->get_property = gtk_container_get_property;
@@ -520,10 +573,37 @@ gtk_container_class_init (GtkContainerClass *class)
gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_CONTAINER_ACCESSIBLE);
}
static GObject *
gtk_container_buildable_get_internal_child (GtkBuildable *buildable,
GtkBuilder *builder,
const gchar *childname)
{
GArray *internal_children;
g_return_val_if_fail (childname && childname[0], NULL);
if ((internal_children = GTK_CONTAINER (buildable)->priv->internal_children))
{
gint i, len;
for (i = 0, len = internal_children->len; i < len; i++)
{
InternalChild *data = &g_array_index (internal_children, InternalChild, i);
if (g_strcmp0 (data->name, childname) == 0) return data->object;
}
}
return parent_buildable_iface->get_internal_child (buildable,
builder,
childname);
}
static void
gtk_container_buildable_init (GtkBuildableIface *iface)
{
parent_buildable_iface = g_type_interface_peek_parent (iface);
iface->get_internal_child = gtk_container_buildable_get_internal_child;
iface->add_child = gtk_container_buildable_add_child;
iface->custom_tag_start = gtk_container_buildable_custom_tag_start;
iface->custom_tag_end = gtk_container_buildable_custom_tag_end;
@@ -1319,6 +1399,152 @@ gtk_container_class_list_child_properties (GObjectClass *cclass,
return pspecs;
}
static void
gtk_container_class_set_template (GtkContainerClass *container_class,
const gchar *tmpl,
const gchar *template_id,
GtkContainerTemplateType tmpl_type)
{
GtkContainerClassPrivate *priv = container_class->priv;
GObjectClass *oclass;
priv->tmpl = tmpl;
priv->tmpl_len = g_utf8_strlen (tmpl, -1);
priv->tmpl_id = template_id;
priv->tmpl_type = tmpl_type;
if (priv->tmpl_classes)
{
g_slist_free (priv->tmpl_classes);
priv->tmpl_classes = NULL;
}
/* Collect an ordered list of class which have templates to build */
for (oclass = G_OBJECT_CLASS (container_class);
GTK_IS_CONTAINER_CLASS (oclass);
oclass = g_type_class_peek_parent (oclass))
{
GtkContainerClassPrivate *cpriv = GTK_CONTAINER_CLASS (oclass)->priv;
if (cpriv->tmpl)
priv->tmpl_classes = g_slist_prepend (priv->tmpl_classes, oclass);
}
}
/**
* gtk_container_class_set_template_from_string:
* @container_class: a #GtkContainerClass
* @template_string: the #GtkBuilder xml string
* @template_id: the template id
*
* For type implementations it is recommended to use #gtk_container_class_set_template_from_resource
* instead of this function.
*
* Since: 3.8
*/
void
gtk_container_class_set_template_from_string (GtkContainerClass *container_class,
const gchar *template_string,
const gchar *template_id)
{
g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
g_return_if_fail (template_string && template_string[0]);
g_return_if_fail (template_id && template_id[0]);
gtk_container_class_set_template (container_class, template_string,
template_id, TMPL_STRING);
}
/**
* gtk_container_class_set_template_from_resource:
* @container_class: a #GtkContainerClass
* @resource_path: the #GtkBuilder xml resource path
* @template_id: the template id
*
* This is used when implementing new composite widget types
* to specify a UI template for instances of this type.
*
* Templates are in the <link linkend="BUILDER-UI">GtkBuilder UI description</link>
* format and are used to implement composite widget types in
* an automated way.
*
* Instances with an assigned template will have their children built at object
* construct time.
*
* The provided xml is expected to have a <external-object> tag instead of
* <object> with id=@template_id.
*
* Since: 3.8
*/
void
gtk_container_class_set_template_from_resource (GtkContainerClass *container_class,
const gchar *resource_path,
const gchar *template_id)
{
g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
g_return_if_fail (resource_path && resource_path[0]);
g_return_if_fail (template_id && template_id[0]);
gtk_container_class_set_template (container_class, resource_path,
template_id, TMPL_RESOURCE);
}
/**
* gtk_container_class_set_connect_func:
* @container_class: a #GtkContainerClass
* @connect_func: the #GtkBuilderConnectFunc to use when connecting signals internally.
*
* Sets the function to be used when automatically connecting signals
* defined by this class's GtkBuilder template.
*
* Since: 3.6
*/
void
gtk_container_class_set_connect_func (GtkContainerClass *container_class,
GtkBuilderConnectFunc connect_func)
{
g_return_if_fail (GTK_IS_CONTAINER_CLASS(container_class));
g_return_if_fail (connect_func != NULL);
container_class->priv->connect_func = connect_func;
}
/**
* gtk_container_class_declare_internal_child:
* @container_class: a #GtkContainerClass
* @use_private: True if struct_offset refers to the instance private struct
* @struct_offset: offset where to save composite children pointer
* @name: the name of the composite children to declare
*
* Declare a child defined in the template as an internal children.
* Use #G_STRUCT_OFFSET to pass in the struct_offset of the pointer that will be set automatically on construction.
* If you do not need to keep a pointer set use_private to FALSE and struct_offset to 0.
*
* Since: 3.6
*/
void
gtk_container_class_declare_internal_child (GtkContainerClass *container_class,
gboolean use_private,
guint struct_offset,
const gchar *name)
{
GtkContainerClassPrivate *priv;
InternalChildData *child;
g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
g_return_if_fail (name);
priv = container_class->priv;
child = g_new0 (InternalChildData, 1);
child->name = g_strdup (name);
child->private = use_private;
child->type = G_TYPE_FROM_CLASS (container_class);
child->offset = struct_offset;
priv->internal_children = g_list_prepend (priv->internal_children, child);
}
static void
gtk_container_add_unimplemented (GtkContainer *container,
GtkWidget *widget)
@@ -1347,6 +1573,7 @@ gtk_container_init (GtkContainer *container)
priv->border_width = 0;
priv->resize_mode = GTK_RESIZE_PARENT;
priv->reallocate_redraws = FALSE;
priv->internal_children = NULL;
}
static void
@@ -1375,11 +1602,133 @@ gtk_container_destroy (GtkWidget *widget)
if (priv->has_focus_chain)
gtk_container_unset_focus_chain (container);
if (priv->internal_children)
{
GArray *internal_children = priv->internal_children;
gint i, len = internal_children->len;
for (i = 0; i < len; i++)
{
InternalChild *data = &g_array_index (internal_children, InternalChild, i);
g_object_unref (data->object);
}
g_array_unref (internal_children);
priv->internal_children = NULL;
}
gtk_container_foreach (container, (GtkCallback) gtk_widget_destroy, NULL);
GTK_WIDGET_CLASS (parent_class)->destroy (widget);
}
static void
gtk_container_child_set_internal (GtkContainer *container,
InternalChildData *child,
GObject *internal)
{
GtkContainerPrivate *priv = container->priv;
InternalChild data;
GObject **retval;
if (!priv->internal_children)
priv->internal_children = g_array_new (FALSE, FALSE, sizeof (InternalChild));
if (GTK_IS_WIDGET (internal))
gtk_widget_set_composite_name (GTK_WIDGET (internal), child->name);
data.name = child->name;
data.object = g_object_ref (internal);
g_array_append_val (priv->internal_children, data);
if (child->private)
{
gpointer pstruct = G_TYPE_INSTANCE_GET_PRIVATE (container, child->type, gpointer);
retval = G_STRUCT_MEMBER_P (pstruct, child->offset);
}
else
retval = G_STRUCT_MEMBER_P (container, child->offset);
*retval = internal;
}
static GObject *
gtk_container_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GtkContainerClassPrivate *priv;
GtkContainer *container;
GError *error = NULL;
GtkBuilder *builder;
GObject *object;
GSList *l;
object = G_OBJECT_CLASS (parent_class)->constructor (type,
n_construct_properties,
construct_properties);
priv = GTK_CONTAINER_CLASS (G_OBJECT_GET_CLASS (object))->priv;
container = GTK_CONTAINER (object);
gtk_widget_push_composite_child ();
/* Build the templates for each class starting with the superclass descending */
for (l = priv->tmpl_classes; l; l = g_slist_next (l))
{
GtkContainerClassPrivate *cpriv = GTK_CONTAINER_CLASS (l->data)->priv;
GList *children;
guint ret;
builder = gtk_builder_new ();
gtk_builder_expose_object (builder, cpriv->tmpl_id, object);
/* Safeguard to avoid recursion */
_gtk_builder_set_ignore_type (builder, type);
if (cpriv->tmpl_type == TMPL_STRING)
ret = gtk_builder_add_from_string (builder, cpriv->tmpl, cpriv->tmpl_len, &error);
else if (cpriv->tmpl_type == TMPL_RESOURCE)
ret = gtk_builder_add_from_resource (builder, cpriv->tmpl, &error);
else
ret = 0;
if (ret)
{
/* Setup internal children */
for (children = cpriv->internal_children; children; children = g_list_next (children))
{
InternalChildData *child = children->data;
GObject *internal;
if ((internal = gtk_builder_get_object (builder, child->name)))
gtk_container_child_set_internal (container, child, internal);
else
{
g_warning ("Unable to setup internal child %s while building GtkContainer class %s",
g_type_name (type), child->name);
continue;
}
}
if (cpriv->connect_func)
gtk_builder_connect_signals_full (builder, cpriv->connect_func, container);
else
gtk_builder_connect_signals (builder, container);
}
else
{
g_critical ("Unable to build GtkContainer class %s from template: %s",
g_type_name (type), error->message);
g_error_free (error);
}
g_object_unref (builder);
}
gtk_widget_pop_composite_child ();
return object;
}
static void
gtk_container_set_property (GObject *object,
guint prop_id,

View File

@@ -31,6 +31,8 @@
#include <gtk/gtkwidget.h>
#include <gtk/gtkadjustment.h>
#include <gtk/gtkbuilder.h>
G_BEGIN_DECLS
@@ -46,6 +48,7 @@ G_BEGIN_DECLS
typedef struct _GtkContainer GtkContainer;
typedef struct _GtkContainerPrivate GtkContainerPrivate;
typedef struct _GtkContainerClass GtkContainerClass;
typedef struct _GtkContainerClassPrivate GtkContainerClassPrivate;
struct _GtkContainer
{
@@ -91,6 +94,8 @@ struct _GtkContainerClass
unsigned int _handle_border_width : 1;
GtkContainerClassPrivate *priv;
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
@@ -99,7 +104,6 @@ struct _GtkContainerClass
void (*_gtk_reserved5) (void);
void (*_gtk_reserved6) (void);
void (*_gtk_reserved7) (void);
void (*_gtk_reserved8) (void);
};
@@ -219,6 +223,20 @@ void gtk_container_forall (GtkContainer *container,
void gtk_container_class_handle_border_width (GtkContainerClass *klass);
/* Class-level functions */
void gtk_container_class_set_template_from_string (GtkContainerClass *container_class,
const gchar *template_string,
const gchar *template_id);
void gtk_container_class_set_template_from_resource (GtkContainerClass *container_class,
const gchar *resource_path,
const gchar *template_id);
void gtk_container_class_set_connect_func (GtkContainerClass *container_class,
GtkBuilderConnectFunc connect_func);
void gtk_container_class_declare_internal_child (GtkContainerClass *container_class,
gboolean use_private,
guint struct_offset,
const gchar *name);
GtkWidgetPath * gtk_container_get_path_for_child (GtkContainer *container,
GtkWidget *child);

View File

@@ -2715,6 +2715,233 @@ test_level_bar (void)
g_object_unref (builder);
}
static GObject *external_object = NULL, *external_object_swapped = NULL;
void
on_button_clicked (GtkButton *button, GObject *data)
{
external_object = data;
}
void
on_button_clicked_swapped (GObject *data, GtkButton *button)
{
external_object_swapped = data;
}
static void
test_expose_object (void)
{
GtkBuilder *builder;
GError *error = NULL;
GtkWidget *image;
GObject *obj;
const gchar buffer[] =
"<interface>"
" <object class=\"GtkButton\" id=\"button\">"
" <property name=\"image\">external_image</property>"
" <signal name=\"clicked\" handler=\"on_button_clicked\" object=\"builder\" swapped=\"no\"/>"
" <signal name=\"clicked\" handler=\"on_button_clicked_swapped\" object=\"builder\"/>"
" </object>"
"</interface>";
image = gtk_image_new ();
builder = gtk_builder_new ();
gtk_builder_expose_object (builder, "external_image", G_OBJECT (image));
gtk_builder_expose_object (builder, "builder", G_OBJECT (builder));
gtk_builder_add_from_string (builder, buffer, -1, &error);
g_assert (error == NULL);
obj = gtk_builder_get_object (builder, "button");
g_assert (GTK_IS_BUTTON (obj));
g_assert (gtk_button_get_image (GTK_BUTTON (obj)) == image);
/* Connect signals and fake clicked event */
gtk_builder_connect_signals (builder, NULL);
gtk_button_clicked (GTK_BUTTON (obj));
g_assert (external_object == G_OBJECT (builder));
g_assert (external_object_swapped == G_OBJECT (builder));
}
static void
test_external_object_real (gboolean use_add_objects)
{
GError *error = NULL;
GtkBuilder *builder;
GtkWidget *mybox, *mygrid;
gboolean expand = FALSE, fill = FALSE;
GtkPackType pack_type = GTK_PACK_START;
guint padding = 0;
const gchar buffer[] =
"<interface>\n"
" <external-object class=\"GtkGrid\" id=\"mygrid\">\n"
" <property name=\"visible\">True</property>\n"
" <child>\n"
" <object class=\"GtkLabel\" id=\"gridlabel\">\n"
" <property name=\"visible\">True</property>\n"
" </object>\n"
" </child>\n"
" </external-object>\n"
/* This external_object should get ignored */
" <external-object class=\"GtkGrid\" id=\"mygrid2\">\n"
" <property name=\"visible\">True</property>\n"
" <child>\n"
" <object class=\"GtkLabel\" id=\"grid2label\">\n"
" <property name=\"visible\">True</property>\n"
" </object>\n"
" </child>\n"
" </external-object>\n"
/* This is the external_object we are interested in! */
" <external-object class=\"GtkBox\" id=\"mybox\">\n"
" <property name=\"visible\">True</property>\n"
" <property name=\"can_focus\">False</property>\n"
" <property name=\"orientation\">vertical</property>\n"
" <property name=\"spacing\">8</property>\n"
" <child>\n"
" <object class=\"GtkLabel\" id=\"boxlabel\">\n"
" <property name=\"visible\">True</property>\n"
" <property name=\"can_focus\">False</property>\n"
" <property name=\"label\" translatable=\"yes\">label</property>\n"
" </object>\n"
" <packing>\n"
" <property name=\"expand\">True</property>\n"
" <property name=\"fill\">True</property>\n"
" <property name=\"padding\">16</property>\n"
" <property name=\"pack-type\">GTK_PACK_END</property>\n"
" <property name=\"position\">0</property>\n"
" </packing>\n"
" </child>\n"
" <child>\n"
" <object class=\"GtkButton\" id=\"button\">\n"
" <property name=\"label\" translatable=\"yes\">button</property>\n"
" <property name=\"visible\">True</property>\n"
" <property name=\"can_focus\">True</property>\n"
" <property name=\"receives_default\">True</property>\n"
" </object>\n"
" <packing>\n"
" <property name=\"expand\">False</property>\n"
" <property name=\"fill\">True</property>\n"
" <property name=\"position\">1</property>\n"
" </packing>\n"
" </child>\n"
" </external-object>\n"
" <object class=\"GtkWindow\" id=\"window\">"
" <child>\n"
" <object class=\"GtkLabel\" id=\"winlabel\">\n"
" <property name=\"visible\">True</property>\n"
" </object>\n"
" </child>\n"
" </object>"
"</interface>";
builder = gtk_builder_new ();
mybox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
g_object_ref_sink (mybox);
mygrid = gtk_grid_new ();
g_object_ref_sink (mygrid);
gtk_builder_expose_object (builder, "mybox", G_OBJECT (mybox));
if (use_add_objects)
{
gchar *objs[] = {"window", NULL};
gtk_builder_add_objects_from_string (builder, buffer, -1, objs, &error);
if (error) g_warning ("%s", error->message);
/* Check if requested object creation works */
g_assert (GTK_IS_WINDOW (gtk_builder_get_object (builder, "window")));
/* external objects should be ignored if we requested a specific object */
g_assert (gtk_builder_get_object (builder, "winlabel"));
}
else
{
gtk_builder_add_from_string (builder, buffer, -1, &error);
if (error) g_warning ("%s", error->message);
g_assert (error == NULL);
/* Check template properties */
g_assert (gtk_box_get_spacing (GTK_BOX (mybox)) == 8);
g_assert (gtk_orientable_get_orientation (GTK_ORIENTABLE (mybox)) == GTK_ORIENTATION_VERTICAL);
/* Check if children are built */
g_assert (GTK_IS_LABEL (gtk_builder_get_object (builder, "boxlabel")));
g_assert (GTK_IS_BUTTON (gtk_builder_get_object (builder, "button")));
/* Check child packing properties */
gtk_box_query_child_packing (GTK_BOX (mybox),
GTK_WIDGET (gtk_builder_get_object (builder, "boxlabel")),
&expand, &fill, &padding, &pack_type);
g_assert (expand);
g_assert (fill);
g_assert (padding == 16);
g_assert (pack_type == GTK_PACK_END);
}
g_object_unref (builder);
g_object_unref (mybox);
g_object_unref (mygrid);
}
static void
test_external_object (void)
{
test_external_object_real (FALSE);
test_external_object_real (TRUE);
}
static void
test_template ()
{
GError *error = NULL;
GtkBuilder *builder;
const gchar buffer[] =
"<interface>\n"
" <template class=\"MyGtkGrid\" parent=\"GtkGrid\">\n"
" <property name=\"visible\">True</property>\n"
" <child>\n"
" <object class=\"GtkLabel\" id=\"gridlabel\">\n"
" <property name=\"visible\">True</property>\n"
" </object>\n"
" </child>\n"
" </template>\n"
"</interface>\n"
"<interface>\n"
" <object class=\"GtkWindow\" id=\"window\">"
" <child>\n"
" <object class=\"MyGtkGrid\" id=\"mygrid\">\n"
" <property name=\"visible\">True</property>\n"
" </object>\n"
" </child>\n"
" </object>\n"
"</interface>\n";
builder = gtk_builder_new ();
/* make sure the type we are trying to register does not exist */
g_assert (!g_type_from_name ("MyGtkGrid"));
gtk_builder_add_from_string (builder, buffer, -1, &error);
if (error) g_warning ("%s", error->message);
g_assert (error == NULL);
/* Check if new type was registered on the fly! */
g_assert (g_type_from_name ("MyGtkGrid"));
g_assert (GTK_IS_WINDOW (gtk_builder_get_object (builder, "window")));
/* Check if inline derived child was built */
g_assert (GTK_IS_GRID (gtk_builder_get_object (builder, "mygrid")));
g_object_unref (builder);
}
int
main (int argc, char **argv)
{
@@ -2763,6 +2990,9 @@ main (int argc, char **argv)
g_test_add_func ("/Builder/MessageDialog", test_message_dialog);
g_test_add_func ("/Builder/GMenu", test_gmenu);
g_test_add_func ("/Builder/LevelBar", test_level_bar);
g_test_add_func ("/Builder/Expose Object", test_expose_object);
g_test_add_func ("/Builder/External Object", test_external_object);
g_test_add_func ("/Builder/Template", test_template);
return g_test_run();
}