X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbuilder.c;h=3d946053c669ad670ae417ae822b54ad83f52f50;hb=HEAD;hp=096d0b44b7e0886172b6fa4753d9706d01615723;hpb=031a09225461eb53f44d27d993c46dc9a11e2cc6;p=~andy%2Fgtk diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c index 096d0b44b..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_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; } } @@ -540,7 +529,7 @@ gtk_builder_get_parameters (GtkBuilder *builder, continue; } - if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) + if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) g_array_append_val (*construct_parameters, parameter); else g_array_append_val (*parameters, parameter); @@ -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 */ @@ -1354,7 +1549,7 @@ gtk_builder_connect_signals_full (GtkBuilder *builder, * initialised beforehand. * * This function can handle char, uchar, boolean, int, uint, long, - * ulong, enum, flags, float, double, string, #GdkColor and + * ulong, enum, flags, float, double, string, #GdkColor, #GdkRGBA and * #GtkAdjustment type values. Support for #GtkWidget type values is * still to come. * @@ -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) {