X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbuilder.c;h=3d946053c669ad670ae417ae822b54ad83f52f50;hb=5bbbc47a4c306653e8347f7afb85a940a503f755;hp=3e970e037a77ebcf45a772d02dbcfc4337e4df14;hpb=f430a76f10d1b7790d83006f9817890960063ed8;p=~andy%2Fgtk
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index 3e970e037..3d946053c 100644
--- a/gtk/gtkbuilder.c
+++ b/gtk/gtkbuilder.c
@@ -15,9 +15,7 @@
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see .
*/
/**
@@ -56,41 +54,18 @@
*
GtkBuilder UI Definitions
*
* GtkBuilder parses textual descriptions of user interfaces which are specified
- * in an XML format which can be roughly described by the DTD below. We refer to
- * these descriptions as GtkBuilder UI definitions or
- * just UI definitions if the context is clear. Do not
+ * in an XML format which can be roughly described by the RELAX NG schema below.
+ * We refer to these descriptions as GtkBuilder UI definitions
+ * or just UI definitions if the context is clear. Do not
* confuse GtkBuilder UI Definitions with
* GtkUIManager UI Definitions, which are more
- * limited in scope.
+ * limited in scope. It is common to use .ui as the filename extension for files containing GtkBuilder UI definitions.
*
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- * ]]>
+ *
+ *
+ * FIXME: MISSING XINCLUDE CONTENT
+ *
+ *
*
* The toplevel element is <interface>. It optionally takes a "domain"
* attribute, which will make the builder look for translated strings using
@@ -148,12 +123,14 @@
* (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 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 <signal> element. The "name"
* attribute specifies the name of the signal, and the "handler" attribute
@@ -240,6 +217,16 @@
* GtkTextTagTable.
*
*
+ *
+ * Embedding other XML
+ *
+ * Apart from the language for UI descriptions that has been explained
+ * in the previous section, GtkBuilder can also parse XML fragments
+ * of GMenu markup. The resulting
+ * #GMenu object and its named submenus are available via
+ * gtk_builder_get_object() like other constructed objects.
+ *
+ *
*/
#include "config.h"
@@ -286,6 +273,7 @@ struct _GtkBuilderPrivate
GSList *delayed_properties;
GSList *signals;
gchar *filename;
+ gchar *resource_prefix;
};
G_DEFINE_TYPE (GtkBuilder, gtk_builder, G_TYPE_OBJECT)
@@ -346,6 +334,7 @@ gtk_builder_finalize (GObject *object)
g_free (priv->domain);
g_free (priv->filename);
+ g_free (priv->resource_prefix);
g_hash_table_destroy (priv->objects);
@@ -503,29 +492,29 @@ gtk_builder_get_parameters (GtkBuilder *builder,
if (G_IS_PARAM_SPEC_OBJECT (pspec) &&
(G_PARAM_SPEC_VALUE_TYPE (pspec) != GDK_TYPE_PIXBUF))
{
- if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
+ GObject *object = gtk_builder_get_object (builder, prop->data);
+
+ if (object)
+ {
+ g_value_init (¶meter.value, G_OBJECT_TYPE (object));
+ g_value_set_object (¶meter.value, object);
+ }
+ else
{
- GObject *object;
- object = gtk_builder_get_object (builder, prop->data);
- if (!object)
+ if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
{
g_warning ("Failed to get constuct only property "
"%s of %s with value `%s'",
prop->name, object_name, prop->data);
continue;
}
- g_value_init (¶meter.value, G_OBJECT_TYPE (object));
- g_value_set_object (¶meter.value, object);
- }
- else
- {
+ /* Delay setting property */
property = g_slice_new (DelayedProperty);
property->object = g_strdup (object_name);
property->name = g_strdup (prop->name);
property->value = g_strdup (prop->data);
builder->priv->delayed_properties =
g_slist_prepend (builder->priv->delayed_properties, property);
-
continue;
}
}
@@ -587,6 +576,24 @@ 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));
+}
+
GObject *
_gtk_builder_construct (GtkBuilder *builder,
ObjectInfo *info,
@@ -716,22 +723,16 @@ _gtk_builder_construct (GtkBuilder *builder,
g_value_unset (¶m->value);
}
g_array_free (parameters, TRUE);
-
- if (GTK_IS_BUILDABLE (obj))
- gtk_buildable_set_name (buildable, info->id);
- else
- g_object_set_data_full (obj,
- "gtk-builder-name",
- g_strdup (info->id),
- g_free);
- /* we already own a reference to obj. put it in the hash table. */
- g_hash_table_insert (builder->priv->objects, g_strdup (info->id), obj);
+ /* put it in the hash table. */
+ _gtk_builder_add_object (builder, info->id, obj);
+
+ /* we already own a reference to obj. */
+ g_object_unref (obj);
return obj;
}
-
void
_gtk_builder_add (GtkBuilder *builder,
ChildInfo *child_info)
@@ -896,7 +897,9 @@ gtk_builder_add_from_file (GtkBuilder *builder,
}
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,
@@ -963,7 +966,9 @@ gtk_builder_add_objects_from_file (GtkBuilder *builder,
}
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,
@@ -981,6 +986,157 @@ gtk_builder_add_objects_from_file (GtkBuilder *builder,
return 1;
}
+/**
+ * gtk_builder_add_from_resource:
+ * @builder: a #GtkBuilder
+ * @resource_path: the path of the resource file to parse
+ * @error: (allow-none): return location for an error, or %NULL
+ *
+ * Parses a resource file containing a GtkBuilder
+ * UI definition and merges it with the current contents of @builder.
+ *
+ * Upon errors 0 will be returned and @error will be assigned a
+ * #GError from the #GTK_BUILDER_ERROR, #G_MARKUP_ERROR or #G_RESOURCE_ERROR
+ * domain.
+ *
+ * Returns: A positive value on success, 0 if an error occurred
+ *
+ * Since: 3.4
+ **/
+guint
+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_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;
+}
+
+/**
+ * gtk_builder_add_objects_from_resource:
+ * @builder: a #GtkBuilder
+ * @resource_path: the path of the resource file to parse
+ * @object_ids: (array zero-terminated=1) (element-type utf8): nul-terminated array of objects to build
+ * @error: (allow-none): return location for an error, or %NULL
+ *
+ * Parses a resource file containing a GtkBuilder
+ * UI definition building only the requested objects and merges
+ * them with the current contents of @builder.
+ *
+ * Upon errors 0 will be returned and @error will be assigned a
+ * #GError from the #GTK_BUILDER_ERROR, #G_MARKUP_ERROR or #G_RESOURCE_ERROR
+ * domain.
+ *
+ *
+ * If you are adding an object that depends on an object that is not
+ * its child (for instance a #GtkTreeView that depends on its
+ * #GtkTreeModel), you have to explicitely list all of them in @object_ids.
+ *
+ *
+ * Returns: A positive value on success, 0 if an error occurred
+ *
+ * Since: 3.4
+ **/
+guint
+gtk_builder_add_objects_from_resource (GtkBuilder *builder,
+ const gchar *resource_path,
+ 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;
+
+ 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_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)
+ {
+ g_propagate_error (error, tmp_error);
+ return 0;
+ }
+
+ return 1;
+}
+
/**
* gtk_builder_add_from_string:
* @builder: a #GtkBuilder
@@ -1013,7 +1169,9 @@ gtk_builder_add_from_string (GtkBuilder *builder,
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, "",
buffer, length,
@@ -1070,7 +1228,9 @@ gtk_builder_add_objects_from_string (GtkBuilder *builder,
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, "",
buffer, length,
@@ -1187,6 +1347,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
+ * <external-object> is defined. It is similar to <object> 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;
@@ -1226,7 +1419,8 @@ gtk_builder_connect_signals_default (GtkBuilder *builder,
* It uses #GModule's introspective features (by opening the module %NULL)
* to look at the application's symbol table. From here it tries to match
* the signal handler names given in the interface description with
- * symbols in the application and connects the signals.
+ * symbols in the application and connects the signals. Note that this
+ * function can only be called once, subsequent calls will do nothing.
*
* Note that this function will not work correctly if #GModule is not
* supported on the platform.
@@ -1276,7 +1470,8 @@ gtk_builder_connect_signals (GtkBuilder *builder,
* by the gtk_builder_connect_signals() and gtk_builder_connect_signals_full()
* methods. It is mainly intended for interpreted language bindings, but
* could be useful where the programmer wants more control over the signal
- * connection process.
+ * connection process. Note that this function can only be called once,
+ * subsequent calls will do nothing.
*
* Since: 2.12
*/
@@ -1392,6 +1587,31 @@ gtk_builder_value_from_string (GtkBuilder *builder,
return TRUE;
}
+ /*
+ * GParamSpecVariant can specify a GVariantType which can help with
+ * parsing, so we need to take care of that here.
+ */
+ if (G_IS_PARAM_SPEC_VARIANT (pspec))
+ {
+ GParamSpecVariant *variant_pspec = G_PARAM_SPEC_VARIANT (pspec);
+ const GVariantType *type;
+ GVariant *variant;
+
+ g_value_init (value, G_TYPE_VARIANT);
+
+ /* The GVariant parser doesn't deal with indefinite types */
+ if (g_variant_type_is_definite (variant_pspec->type))
+ type = variant_pspec->type;
+ else
+ type = NULL;
+
+ variant = g_variant_parse (type, string, NULL, NULL, error);
+ if (variant == NULL)
+ return FALSE;
+ g_value_take_variant (value, variant);
+ return TRUE;
+ }
+
return gtk_builder_value_from_string_type (builder,
G_PARAM_SPEC_VALUE_TYPE (pspec),
string, value, error);
@@ -1435,7 +1655,7 @@ gtk_builder_value_from_string_type (GtkBuilder *builder,
switch (G_TYPE_FUNDAMENTAL (type))
{
case G_TYPE_CHAR:
- g_value_set_char (value, string[0]);
+ g_value_set_schar (value, string[0]);
break;
case G_TYPE_UCHAR:
g_value_set_uchar (value, (guchar)string[0]);
@@ -1547,6 +1767,17 @@ gtk_builder_value_from_string_type (GtkBuilder *builder,
case G_TYPE_STRING:
g_value_set_string (value, string);
break;
+ case G_TYPE_VARIANT:
+ {
+ GVariant *variant;
+
+ variant = g_variant_parse (NULL, string, NULL, NULL, error);
+ if (value != NULL)
+ g_value_take_variant (value, variant);
+ else
+ ret = FALSE;
+ }
+ break;
case G_TYPE_BOXED:
if (G_VALUE_HOLDS (value, GDK_TYPE_COLOR))
{
@@ -1600,7 +1831,7 @@ gtk_builder_value_from_string_type (GtkBuilder *builder,
{
gchar *filename;
GError *tmp_error = NULL;
- GdkPixbuf *pixbuf;
+ GdkPixbuf *pixbuf = NULL;
if (gtk_builder_get_object (builder, string))
{
@@ -1613,8 +1844,21 @@ gtk_builder_value_from_string_type (GtkBuilder *builder,
return FALSE;
}
- filename = _gtk_builder_get_absolute_filename (builder, string);
- pixbuf = gdk_pixbuf_new_from_file (filename, &tmp_error);
+ filename = _gtk_builder_get_resource_path (builder, string);
+ if (filename != NULL)
+ {
+ GInputStream *stream = g_resources_open_stream (filename, 0, &tmp_error);
+ if (stream != NULL)
+ {
+ pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, &tmp_error);
+ g_object_unref (stream);
+ }
+ }
+ else
+ {
+ filename = _gtk_builder_get_absolute_filename (builder, string);
+ pixbuf = gdk_pixbuf_new_from_file (filename, &tmp_error);
+ }
if (pixbuf == NULL)
{
@@ -1843,6 +2087,19 @@ gtk_builder_error_quark (void)
return g_quark_from_static_string ("gtk-builder-error-quark");
}
+gchar *
+_gtk_builder_get_resource_path (GtkBuilder *builder, const gchar *string)
+{
+ if (g_str_has_prefix (string, "resource:///"))
+ return g_uri_unescape_string (string + 11, "/");
+
+ if (g_path_is_absolute (string) ||
+ builder->priv->resource_prefix == NULL)
+ return NULL;
+
+ return g_build_path ("/", builder->priv->resource_prefix, string, NULL);
+}
+
gchar *
_gtk_builder_get_absolute_filename (GtkBuilder *builder, const gchar *string)
{