X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbuilder.c;h=3d946053c669ad670ae417ae822b54ad83f52f50;hb=79695ee64d41c9aadfe2c6f18dc7dd1e3fd44852;hp=9dff20e38a03eea0baa3c2d9f28eeb6a53ca4e21;hpb=45894c9fad16886bd9c97adca150dd693ba0c806;p=~andy%2Fgtk diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c index 9dff20e38..3d946053c 100644 --- a/gtk/gtkbuilder.c +++ b/gtk/gtkbuilder.c @@ -15,19 +15,229 @@ * 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 . + */ + +/** + * SECTION:gtkbuilder + * @Short_description: Build an interface from an XML UI definition + * @Title: GtkBuilder + * + * A GtkBuilder is an auxiliary object that reads textual descriptions + * of a user interface and instantiates the described objects. To pass a + * description to a GtkBuilder, call gtk_builder_add_from_file() or + * gtk_builder_add_from_string(). These functions can be called multiple + * times; the builder merges the content of all descriptions. + * + * A GtkBuilder holds a reference to all objects that it has constructed + * and drops these references when it is finalized. This finalization can + * cause the destruction of non-widget objects or widgets which are not + * contained in a toplevel window. For toplevel windows constructed by a + * builder, it is the responsibility of the user to call gtk_widget_destroy() + * to get rid of them and all the widgets they contain. + * + * The functions gtk_builder_get_object() and gtk_builder_get_objects() + * can be used to access the widgets in the interface by the names assigned + * to them inside the UI description. Toplevel windows returned by these + * functions will stay around until the user explicitly destroys them + * with gtk_widget_destroy(). Other widgets will either be part of a + * larger hierarchy constructed by the builder (in which case you should + * not have to worry about their lifecycle), or without a parent, in which + * case they have to be added to some container to make use of them. + * Non-widget objects need to be reffed with g_object_ref() to keep them + * beyond the lifespan of the builder. + * + * The function gtk_builder_connect_signals() and variants thereof can be + * used to connect handlers to the named signals in the description. + * + * + * GtkBuilder UI Definitions + * + * GtkBuilder parses textual descriptions of user interfaces which are specified + * 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. 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 + * dgettext() in the domain specified. This can also be done by calling + * gtk_builder_set_translation_domain() on the builder. Objects are described by + * <object> elements, which can contain <property> elements to set + * properties, <signal> elements which connect signals to handlers, and + * <child> elements, which describe child objects (most often widgets + * inside a container, but also e.g. actions in an action group, or columns in a + * tree model). A <child> element contains an <object> element which + * describes the child object. The target toolkit version(s) are described by + * <requires> elements, the "lib" attribute specifies the widget library + * in question (currently the only supported value is "gtk+") and the "version" + * attribute specifies the target version in the form + * "<major>.<minor>". The builder will error out if the version + * requirements are not met. + * + * Typically, the specific kind of object represented by an <object> + * element is specified by the "class" attribute. If the type has not been + * loaded yet, GTK+ tries to find the _get_type() from the + * class name by applying heuristics. This works in most cases, but if + * necessary, it is possible to specify the name of the + * _get_type() explictly with the "type-func" attribute. + * As a special case, GtkBuilder allows to use an object that has been + * constructed by a #GtkUIManager in another part of the UI definition by + * specifying the id of the #GtkUIManager in the "constructor" attribute and the + * name of the object in the "id" attribute. + * + * Objects must be given a name with the "id" attribute, which allows the + * application to retrieve them from the builder with gtk_builder_get_object(). + * An id is also necessary to use the object as property value in other parts of + * the UI definition. + * + * + * Prior to 2.20, GtkBuilder was setting the "name" property of constructed widgets to the + * "id" attribute. In GTK+ 2.20 or newer, you have to use gtk_buildable_get_name() instead + * of gtk_widget_get_name() to obtain the "id", or set the "name" property in your UI + * definition. + * + * + * Setting properties of objects is pretty straightforward with the + * <property> element: the "name" attribute specifies the name of the + * property, and the content of the element specifies the value. If the + * "translatable" attribute is set to a true value, GTK+ uses gettext() (or + * dgettext() if the builder has a translation domain set) to find a translation + * for the value. This happens before the value is parsed, so it can be used for + * properties of any type, but it is probably most useful for string properties. + * It is also possible to specify a context to disambiguate short strings, and + * comments which may help the translators. + * + * GtkBuilder can parse textual representations for the most common property + * types: characters, strings, integers, floating-point numbers, booleans + * (strings like "TRUE", "t", "yes", "y", "1" are interpreted as %TRUE, strings + * like "FALSE, "f", "no", "n", "0" are interpreted as %FALSE), enumerations + * (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()). 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 + * 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 + * 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. + * + * 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 + * or to add further children (e.g. the @vbox of a #GtkDialog). This can be + * achieved by setting the "internal-child" propery of the <child> element + * to a true value. Note that GtkBuilder still requires an <object> + * element for the internal child, even if it has already been constructed. + * + * A number of widgets have different places where a child can be added (e.g. + * tabs vs. page content in notebooks). This can be reflected in a UI definition + * by specifying the "type" attribute on a <child>. The possible values + * for the "type" attribute are described in the sections describing the + * widget-specific portions of UI definitions. + * + * + * A GtkBuilder UI Definition + * + * + * + * + * 10 + * + * + * 20 + * + * + * gtk-ok + * TRUE + * + * + * + * + * + * + * + * + * + * ]]> + * + * + * Beyond this general structure, several object classes define their own XML + * DTD fragments for filling in the ANY placeholders in the DTD above. Note that + * a custom element in a <child> element gets parsed by the custom tag + * handler of the parent object, while a custom element in an <object> + * element gets parsed by the custom tag handler of the object. + * + * These XML fragments are explained in the documentation of the respective + * objects, see + * GtkWidget, + * GtkLabel, + * GtkWindow, + * GtkContainer, + * GtkDialog, + * GtkCellLayout, + * GtkColorSelectionDialog, + * GtkFontSelectionDialog, + * GtkExpander, + * GtkFrame, + * GtkListStore, + * GtkTreeStore, + * GtkNotebook, + * GtkSizeGroup, + * GtkTreeView, + * GtkUIManager, + * GtkActionGroup. + * GtkMenuItem, + * GtkMenuToolButton, + * GtkAssistant, + * GtkScale, + * GtkComboBoxText, + * GtkRecentFilter, + * GtkFileFilter, + * 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" #include /* errno */ -#include /* strtol, strtoul */ +#include #include /* strlen */ #include "gtkbuilder.h" #include "gtkbuildable.h" #include "gtkbuilderprivate.h" +#include "gtkdebug.h" #include "gtkmain.h" #include "gtkintl.h" #include "gtkprivate.h" @@ -35,7 +245,7 @@ #include "gtkwindow.h" #include "gtkicontheme.h" #include "gtkstock.h" -#include "gtkalias.h" + static void gtk_builder_class_init (GtkBuilderClass *klass); static void gtk_builder_init (GtkBuilder *builder); @@ -63,6 +273,7 @@ struct _GtkBuilderPrivate GSList *delayed_properties; GSList *signals; gchar *filename; + gchar *resource_prefix; }; G_DEFINE_TYPE (GtkBuilder, gtk_builder, G_TYPE_OBJECT) @@ -123,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); @@ -280,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; } } @@ -317,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); @@ -364,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, @@ -483,7 +713,7 @@ _gtk_builder_construct (GtkBuilder *builder, g_object_set_property (obj, param->name, ¶m->value); #if G_ENABLE_DEBUG - if (gtk_debug_flags & GTK_DEBUG_BUILDER) + if (gtk_get_debug_flags () & GTK_DEBUG_BUILDER) { gchar *str = g_strdup_value_contents ((const GValue*)¶m->value); g_print ("set %s: %s = %s\n", info->id, param->name, str); @@ -493,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) @@ -638,11 +862,15 @@ gtk_builder_new (void) * gtk_builder_add_from_file: * @builder: a #GtkBuilder * @filename: the name of the file to parse - * @error: return location for an error, or %NULL + * @error: (allow-none): return location for an error, or %NULL * * Parses a 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_FILE_ERROR + * domain. + * * Returns: A positive value on success, 0 if an error occurred * * Since: 2.12 @@ -669,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, @@ -691,13 +921,17 @@ gtk_builder_add_from_file (GtkBuilder *builder, * gtk_builder_add_objects_from_file: * @builder: a #GtkBuilder * @filename: the name of the file to parse - * @object_ids: nul-terminated array of objects to build - * @error: return location for an error, or %NULL + * @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 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_FILE_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 @@ -732,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, @@ -750,16 +986,170 @@ 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 * @buffer: the string to parse * @length: the length of @buffer (may be -1 if @buffer is nul-terminated) - * @error: return location for an error, or %NULL + * @error: (allow-none): return location for an error, or %NULL * * Parses a string 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 or #G_MARKUP_ERROR domain. + * * Returns: A positive value on success, 0 if an error occurred * * Since: 2.12 @@ -779,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, @@ -799,12 +1191,15 @@ gtk_builder_add_from_string (GtkBuilder *builder, * @builder: a #GtkBuilder * @buffer: the string to parse * @length: the length of @buffer (may be -1 if @buffer is nul-terminated) - * @object_ids: nul-terminated array of objects to build - * @error: return location for an error, or %NULL + * @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 string 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 or #G_MARKUP_ERROR domain. * * * If you are adding an object that depends on an object that is not @@ -833,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, @@ -857,8 +1254,8 @@ gtk_builder_add_objects_from_string (GtkBuilder *builder, * Gets the object named @name. Note that this function does not * increment the reference count of the returned object. * - * Return value: the object named @name or %NULL if it could not be - * found in the object tree. + * Return value: (transfer none): the object named @name or %NULL if + * it could not be found in the object tree. * * Since: 2.12 **/ @@ -888,7 +1285,7 @@ object_add_to_list (gchar *object_id, * this function does not increment the reference counts of the returned * objects. * - * Return value: a newly-allocated #GSList containing all the objects + * Return value: (element-type GObject) (transfer container): a newly-allocated #GSList containing all the objects * constructed by the #GtkBuilder instance. It should be freed by * g_slist_free() * @@ -909,7 +1306,7 @@ gtk_builder_get_objects (GtkBuilder *builder) /** * gtk_builder_set_translation_domain: * @builder: a #GtkBuilder - * @domain: the translation domain or %NULL + * @domain: (allow-none): the translation domain or %NULL * * Sets the translation domain of @builder. * See #GtkBuilder:translation-domain. @@ -950,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; @@ -989,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. @@ -1039,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 */ @@ -1047,7 +1479,7 @@ gtk_builder_connect_signals (GtkBuilder *builder, /** * gtk_builder_connect_signals_full: * @builder: a #GtkBuilder - * @func: the function used to connect the signals + * @func: (scope call): the function used to connect the signals * @user_data: arbitrary data that will be passed to the connection function * * This function can be thought of the interpreted language binding @@ -1109,18 +1541,21 @@ gtk_builder_connect_signals_full (GtkBuilder *builder, * @builder: a #GtkBuilder * @pspec: the #GParamSpec for the property * @string: the string representation of the value - * @value: the #GValue to store the result in - * @error: return location for an error, or %NULL + * @value: (out): the #GValue to store the result in + * @error: (allow-none): return location for an error, or %NULL * * This function demarshals a value from a string. This function * calls g_value_init() on the @value argument, so it need not be * 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. * + * Upon errors %FALSE will be returned and @error will be assigned a + * #GError from the #GTK_BUILDER_ERROR domain. + * * Returns: %TRUE on success * * Since: 2.12 @@ -1152,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); @@ -1162,14 +1622,17 @@ gtk_builder_value_from_string (GtkBuilder *builder, * @builder: a #GtkBuilder * @type: the #GType of the value * @string: the string representation of the value - * @value: the #GValue to store the result in - * @error: return location for an error, or %NULL + * @value: (out): the #GValue to store the result in + * @error: (allow-none): return location for an error, or %NULL * * Like gtk_builder_value_from_string(), this function demarshals * a value from a string, but takes a #GType instead of #GParamSpec. * This function calls g_value_init() on the @value argument, so it * need not be initialised beforehand. * + * Upon errors %FALSE will be returned and @error will be assigned a + * #GError from the #GTK_BUILDER_ERROR domain. + * * Returns: %TRUE on success * * Since: 2.12 @@ -1192,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]); @@ -1213,9 +1676,9 @@ gtk_builder_value_from_string_type (GtkBuilder *builder, case G_TYPE_LONG: { long l; - gchar *endptr; + gchar *endptr = NULL; errno = 0; - l = strtol (string, &endptr, 0); + l = g_ascii_strtoll (string, &endptr, 0); if (errno || endptr == string) { g_set_error (error, @@ -1236,9 +1699,9 @@ gtk_builder_value_from_string_type (GtkBuilder *builder, case G_TYPE_ULONG: { gulong ul; - gchar *endptr; + gchar *endptr = NULL; errno = 0; - ul = strtoul (string, &endptr, 0); + ul = g_ascii_strtoull (string, &endptr, 0); if (errno || endptr == string) { g_set_error (error, @@ -1282,7 +1745,7 @@ gtk_builder_value_from_string_type (GtkBuilder *builder, case G_TYPE_DOUBLE: { gdouble d; - gchar *endptr; + gchar *endptr = NULL; errno = 0; d = g_ascii_strtod (string, &endptr); if (errno || endptr == string) @@ -1304,15 +1767,24 @@ 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)) { - GdkColor colour = { 0, }; + GdkColor color = { 0, }; - if (gdk_color_parse (string, &colour) && - gdk_colormap_alloc_color (gtk_widget_get_default_colormap (), - &colour, FALSE, TRUE)) - g_value_set_boxed (value, &colour); + if (gdk_color_parse (string, &color)) + g_value_set_boxed (value, &color); else { g_set_error (error, @@ -1323,6 +1795,22 @@ gtk_builder_value_from_string_type (GtkBuilder *builder, ret = FALSE; } } + else if (G_VALUE_HOLDS (value, GDK_TYPE_RGBA)) + { + GdkRGBA rgba = { 0 }; + + if (gdk_rgba_parse (&rgba, string)) + g_value_set_boxed (value, &rgba); + else + { + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_VALUE, + "Could not parse RGBA color '%s'", + string); + ret = FALSE; + } + } else if (G_VALUE_HOLDS (value, G_TYPE_STRV)) { gchar **vector = g_strsplit (string, "\n", 0); @@ -1343,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)) { @@ -1356,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) { @@ -1390,14 +1891,19 @@ gtk_builder_value_from_string_type (GtkBuilder *builder, ret = FALSE; break; default: - g_set_error (error, - GTK_BUILDER_ERROR, - GTK_BUILDER_ERROR_INVALID_VALUE, - "Unsupported GType `%s'", - g_type_name (type)); ret = FALSE; break; } + + /* Catch unassigned error for object types as well as any unsupported types. + * While parsing GtkBuilder; object types are deserialized + * without calling gtk_builder_value_from_string_type(). + */ + if (!ret && error && *error == NULL) + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_VALUE, + "Unsupported GType `%s'", g_type_name (type)); return ret; } @@ -1419,8 +1925,10 @@ _gtk_builder_enum_from_string (GType type, ret = TRUE; - value = strtoul (string, &endptr, 0); - if (endptr != string) /* parsed a number */ + endptr = NULL; + errno = 0; + value = g_ascii_strtoull (string, &endptr, 0); + if (errno == 0 && endptr != string) /* parsed a number */ *enum_value = value; else { @@ -1466,9 +1974,11 @@ _gtk_builder_flags_from_string (GType type, g_return_val_if_fail (string != 0, FALSE); ret = TRUE; - - value = strtoul (string, &endptr, 0); - if (endptr != string) /* parsed a number */ + + endptr = NULL; + errno = 0; + value = g_ascii_strtoull (string, &endptr, 0); + if (errno == 0 && endptr != string) /* parsed a number */ *flags_value = value; else { @@ -1577,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) { @@ -1605,6 +2128,3 @@ _gtk_builder_get_absolute_filename (GtkBuilder *builder, const gchar *string) return filename; } - -#define __GTK_BUILDER_C__ -#include "gtkaliasdef.c"