]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkbuilder.c
Implement GtkBuildable on GtkIconFactory, to make it possible to register
[~andy/gtk] / gtk / gtkbuilder.c
index 5cf0e97224988465e55300d40ca6ef9dd782aea5..11641035dcc53bc50bde9876f5ca07a0ad92e60b 100644 (file)
@@ -21,7 +21,6 @@
  */
 
 #include <config.h>
-#include <ctype.h> /* tolower, toupper */
 #include <errno.h> /* errno */
 #include <stdlib.h> /* strtol, strtoul */
 #include <string.h> /* strlen */
@@ -33,6 +32,9 @@
 #include "gtkintl.h"
 #include "gtkprivate.h"
 #include "gtktypebuiltins.h"
+#include "gtkwindow.h"
+#include "gtkicontheme.h"
+#include "gtkstock.h"
 #include "gtkalias.h"
 
 static void gtk_builder_class_init     (GtkBuilderClass *klass);
@@ -46,9 +48,12 @@ static void gtk_builder_get_property   (GObject         *object,
                                         guint            prop_id,
                                         GValue          *value,
                                         GParamSpec      *pspec);
-static GType gtk_builder_real_get_type_from_name   (GtkBuilder      *builder,
-                                                    const char      *type_name);
-static gint _gtk_builder_enum_from_string (GType type, const char *string);
+static GType gtk_builder_real_get_type_from_name (GtkBuilder  *builder,
+                                                  const gchar *type_name);
+static gboolean _gtk_builder_enum_from_string (GType         type, 
+                                              const gchar  *string,
+                                              gint         *enum_value,
+                                              GError      **error);
 
 
 enum {
@@ -60,9 +65,9 @@ struct _GtkBuilderPrivate
 {
   gchar *domain;
   GHashTable *objects;
-  GHashTable *delayed_properties;
+  GSList *delayed_properties;
   GSList *signals;
-  gchar *current_toplevel;
+  gchar *filename;
 };
 
 G_DEFINE_TYPE (GtkBuilder, gtk_builder, G_TYPE_OBJECT)
@@ -80,6 +85,16 @@ gtk_builder_class_init (GtkBuilderClass *klass)
 
   klass->get_type_from_name = gtk_builder_real_get_type_from_name;
 
+ /** 
+  * GtkBuilder:translation-domain:
+  *
+  * The translation domain used when translating property values that
+  * have been marked as translatable in interface descriptions.
+  * If the translation domain is %NULL, #GtkBuilder uses gettext(),
+  * otherwise dgettext().
+  *
+  * Since: 2.12
+  */
   g_object_class_install_property (gobject_class,
                                    PROP_TRANSLATION_DOMAIN,
                                    g_param_spec_string ("translation-domain",
@@ -98,10 +113,7 @@ gtk_builder_init (GtkBuilder *builder)
                                                GtkBuilderPrivate);
   builder->priv->domain = NULL;
   builder->priv->objects = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                                  g_free, NULL);
-  builder->priv->delayed_properties = g_hash_table_new_full (g_str_hash,
-                                                             g_str_equal,
-                                                             g_free, NULL);
+                                                  g_free, g_object_unref);
 }
 
 
@@ -112,16 +124,17 @@ gtk_builder_init (GtkBuilder *builder)
 static void
 gtk_builder_finalize (GObject *object)
 {
-  GtkBuilder *builder = GTK_BUILDER (object);
+  GtkBuilderPrivate *priv = GTK_BUILDER (object)->priv;
   
-  g_free (builder->priv->domain);
+  g_free (priv->domain);
+  g_free (priv->filename);
+  
+  g_hash_table_destroy (priv->objects);
 
-  g_free (builder->priv->current_toplevel);
-  g_hash_table_destroy (builder->priv->delayed_properties);
-  builder->priv->delayed_properties = NULL;
-  g_slist_foreach (builder->priv->signals, (GFunc)_free_signal_info, NULL);
-  g_slist_free (builder->priv->signals);
-  g_hash_table_destroy (builder->priv->objects);
+  g_slist_foreach (priv->signals, (GFunc) _free_signal_info, NULL);
+  g_slist_free (priv->signals);
+  
+  G_OBJECT_CLASS (gtk_builder_parent_class)->finalize (object);
 }
 
 static void
@@ -189,13 +202,13 @@ _gtk_builder_resolve_type_lazily (const gchar *name)
     {
       c = name[i];
       /* skip if uppercase, first or previous is uppercase */
-      if ((c == toupper (c) &&
-           i > 0 && name[i-1] != toupper (name[i-1])) ||
-          (i > 2 && name[i]   == toupper (name[i]) &&
-           name[i-1] == toupper (name[i-1]) &&
-           name[i-2] == toupper (name[i-2])))
+      if ((c == g_ascii_toupper (c) &&
+           i > 0 && name[i-1] != g_ascii_toupper (name[i-1])) ||
+          (i > 2 && name[i]   == g_ascii_toupper (name[i]) &&
+           name[i-1] == g_ascii_toupper (name[i-1]) &&
+           name[i-2] == g_ascii_toupper (name[i-2])))
         g_string_append_c (symbol_name, '_');
-      g_string_append_c (symbol_name, tolower (c));
+      g_string_append_c (symbol_name, g_ascii_tolower (c));
     }
   g_string_append (symbol_name, "_get_type");
   
@@ -214,7 +227,8 @@ _gtk_builder_resolve_type_lazily (const gchar *name)
  */
 
 static GType
-gtk_builder_real_get_type_from_name (GtkBuilder *builder, const char *type_name)
+gtk_builder_real_get_type_from_name (GtkBuilder  *builder, 
+                                     const gchar *type_name)
 {
   GType gtype;
 
@@ -244,7 +258,8 @@ gtk_builder_get_parameters (GtkBuilder  *builder,
   GParamSpec *pspec;
   GObjectClass *oclass;
   DelayedProperty *property;
-
+  GError *error = NULL;
+  
   oclass = g_type_class_ref (object_type);
   g_assert (oclass != NULL);
 
@@ -260,14 +275,15 @@ gtk_builder_get_parameters (GtkBuilder  *builder,
                                             prop->name);
       if (!pspec)
         {
-          g_warning ("Unknown property: %s.%s\n",
+          g_warning ("Unknown property: %s.%s",
                      g_type_name (object_type), prop->name);
           continue;
         }
 
       parameter.name = prop->name;
 
-      if (G_IS_PARAM_SPEC_OBJECT (pspec))
+      if (G_IS_PARAM_SPEC_OBJECT (pspec) &&
+          (G_PARAM_SPEC_VALUE_TYPE (pspec) != GDK_TYPE_PIXBUF))
         {
           if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
             {
@@ -275,35 +291,34 @@ gtk_builder_get_parameters (GtkBuilder  *builder,
               object = gtk_builder_get_object (builder, prop->data);
               if (!object)
                 {
-                  g_warning ("failed to get constuct only property %s of %s "
-                             "with value `%s'",
+                  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, g_object_ref (object));
+              g_value_set_object (&parameter.value, object);
             }
           else
             {
-              GSList *delayed_properties;
-              
-              delayed_properties = g_hash_table_lookup (builder->priv->delayed_properties,
-                                                        builder->priv->current_toplevel);
               property = g_slice_new (DelayedProperty);
               property->object = g_strdup (object_name);
               property->name = g_strdup (prop->name);
               property->value = g_strdup (prop->data);
-              delayed_properties = g_slist_prepend (delayed_properties, property);
-              g_hash_table_insert (builder->priv->delayed_properties,
-                                   g_strdup (builder->priv->current_toplevel),
-                                   delayed_properties);
+              builder->priv->delayed_properties =
+                g_slist_prepend (builder->priv->delayed_properties, property);
+
               continue;
             }
         }
-      else if (!gtk_builder_value_from_string (pspec, prop->data, &parameter.value))
+      else if (!gtk_builder_value_from_string (builder, pspec,
+                                              prop->data, &parameter.value, &error))
         {
-          g_warning ("failed to set property %s.%s to %s",
-                     g_type_name (object_type), prop->name, prop->data);
+          g_warning ("Failed to set property %s.%s to %s: %s",
+                     g_type_name (object_type), prop->name, prop->data,
+                    error->message);
+         g_error_free (error);
+         error = NULL;
           continue;
         }
 
@@ -319,7 +334,8 @@ gtk_builder_get_parameters (GtkBuilder  *builder,
 static GObject *
 gtk_builder_get_internal_child (GtkBuilder  *builder,
                                 ObjectInfo  *info,
-                                const gchar *childname)
+                                const gchar *childname,
+                               GError      **error)
 {
   GObject *obj = NULL;
 
@@ -344,14 +360,19 @@ gtk_builder_get_internal_child (GtkBuilder  *builder,
     };
 
   if (!obj)
-    g_error ("Unknown internal child: %s\n", childname);
-
+    {
+      g_set_error (error,
+                  GTK_BUILDER_ERROR,
+                  GTK_BUILDER_ERROR_INVALID_VALUE,
+                  "Unknown internal child: %s", childname);
+    }
   return obj;
 }
 
 GObject *
 _gtk_builder_construct (GtkBuilder *builder,
-                        ObjectInfo *info)
+                        ObjectInfo *info,
+                       GError **error)
 {
   GArray *parameters, *construct_parameters;
   GType object_type;
@@ -364,7 +385,14 @@ _gtk_builder_construct (GtkBuilder *builder,
   g_assert (info->class_name != NULL);
   object_type = gtk_builder_get_type_from_name (builder, info->class_name);
   if (object_type == G_TYPE_INVALID)
-    g_error ("Invalid type: %s", info->class_name);
+    {
+      g_set_error (error,
+                  GTK_BUILDER_ERROR,
+                  GTK_BUILDER_ERROR_INVALID_VALUE,
+                  "Invalid object type `%s'",
+                  info->class_name);
+      return NULL;
+    }
 
   gtk_builder_get_parameters (builder, object_type,
                               info->id,
@@ -378,23 +406,37 @@ _gtk_builder_construct (GtkBuilder *builder,
 
       constructor = gtk_builder_get_object (builder, info->constructor);
       if (constructor == NULL)
-        g_error ("Unknown constructor for %s: %s\n", info->id,
-                 info->constructor);
-
+       {
+         g_set_error (error,
+                      GTK_BUILDER_ERROR,
+                      GTK_BUILDER_ERROR_INVALID_VALUE,
+                      "Unknown object constructor for %s: %s",
+                      info->id,
+                      info->constructor);
+         g_array_free (parameters, TRUE);
+         g_array_free (construct_parameters, TRUE);
+         return NULL;
+       }
       obj = gtk_buildable_construct_child (GTK_BUILDABLE (constructor),
                                            builder,
                                            info->id);
       g_assert (obj != NULL);
       if (construct_parameters->len)
         g_warning ("Can't pass in construct-only parameters to %s", info->id);
-
     }
   else if (info->parent && ((ChildInfo*)info->parent)->internal_child != NULL)
     {
       gchar *childname = ((ChildInfo*)info->parent)->internal_child;
-      obj = gtk_builder_get_internal_child (builder, info, childname);
+      obj = gtk_builder_get_internal_child (builder, info, childname, error);
+      if (!obj)
+       {
+         g_array_free (parameters, TRUE);
+         g_array_free (construct_parameters, TRUE);
+         return NULL;
+       }
       if (construct_parameters->len)
         g_warning ("Can't pass in construct-only parameters to %s", childname);
+      g_object_ref (obj);
     }
   else
     {
@@ -402,6 +444,18 @@ _gtk_builder_construct (GtkBuilder *builder,
                            construct_parameters->len,
                            (GParameter *)construct_parameters->data);
 
+      /* No matter what, make sure we have a reference.
+       *
+       * If it's an initially unowned object, sink it.
+       * If it's not initially unowned then we have the reference already.
+       *
+       * In the case that this is a window it will be sunk already and
+       * this is effectively a call to g_object_ref().  That's what
+       * we want.
+       */
+      if (G_IS_INITIALLY_UNOWNED (obj))
+        g_object_ref_sink (obj);
+
       GTK_NOTE (BUILDER,
                 g_print ("created %s of type %s\n", info->id, info->class_name));
 
@@ -453,15 +507,9 @@ _gtk_builder_construct (GtkBuilder *builder,
                             g_strdup (info->id),
                             g_free);
 
-  if (!info->parent)
-    {
-      g_free (builder->priv->current_toplevel);
-      builder->priv->current_toplevel = g_strdup (info->id);
-    }
+  /* 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);
   
-  builder->priv->signals = g_slist_concat (builder->priv->signals,
-                                           g_slist_copy (info->signals));
   return obj;
 }
 
@@ -487,7 +535,7 @@ _gtk_builder_add (GtkBuilder *builder,
 
   if (!child_info->parent)
     {
-      g_warning ("%s: Not adding, No parent\n",
+      g_warning ("%s: Not adding, No parent",
                  gtk_buildable_get_name (GTK_BUILDABLE (object)));
       return;
     }
@@ -508,20 +556,32 @@ _gtk_builder_add (GtkBuilder *builder,
   child_info->added = TRUE;
 }
 
+void
+_gtk_builder_add_signals (GtkBuilder *builder,
+                         GSList     *signals)
+{
+  builder->priv->signals = g_slist_concat (builder->priv->signals,
+                                           g_slist_copy (signals));
+}
+
 static void
-apply_delayed_properties (const gchar *window_name,
-                          GSList      *props,
-                          GtkBuilder  *builder)
+gtk_builder_apply_delayed_properties (GtkBuilder *builder)
 {
-  GSList *l;
+  GSList *l, *props;
   DelayedProperty *property;
   GObject *object;
   GType object_type;
   GObjectClass *oclass;
   GParamSpec *pspec;
 
-  g_assert (props != NULL);
-  props = g_slist_reverse (props);
+  /* take the list over from the builder->priv.
+   *
+   * g_slist_reverse does not copy the list, so the list now
+   * belongs to us (and we free it at the end of this function).
+   */
+  props = g_slist_reverse (builder->priv->delayed_properties);
+  builder->priv->delayed_properties = NULL;
+
   for (l = props; l; l = l->next)
     {
       property = (DelayedProperty*)l->data;
@@ -537,7 +597,7 @@ apply_delayed_properties (const gchar *window_name,
       pspec = g_object_class_find_property (G_OBJECT_CLASS (oclass),
                                             property->name);
       if (!pspec)
-        g_warning ("Unknown property: %s.%s\n", g_type_name (object_type),
+        g_warning ("Unknown property: %s.%s", g_type_name (object_type),
                    property->name);
       else
         {
@@ -545,7 +605,7 @@ apply_delayed_properties (const gchar *window_name,
 
           obj = g_hash_table_lookup (builder->priv->objects, property->value);
           if (!obj)
-            g_warning ("No object called: %s\n", property->object);
+            g_warning ("No object called: %s", property->value);
           else
             g_object_set (object, property->name, obj, NULL);
         }
@@ -559,11 +619,9 @@ apply_delayed_properties (const gchar *window_name,
 }
 
 void
-_gtk_builder_finish (GtkBuilder  *builder)
+_gtk_builder_finish (GtkBuilder *builder)
 {
-  if (builder->priv->delayed_properties)
-    g_hash_table_foreach (builder->priv->delayed_properties,
-                          (GHFunc)apply_delayed_properties, builder);
+  gtk_builder_apply_delayed_properties (builder);
 }
 
 /**
@@ -571,7 +629,7 @@ _gtk_builder_finish (GtkBuilder  *builder)
  *
  * Creates a new builder object.
  *
- * Return value: a new builder object.
+ * Return value: a new #GtkBuilder object
  *
  * Since: 2.12
  **/
@@ -585,10 +643,10 @@ 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
+ * @error: return location for an error, or %NULL
  *
- * Parses a string containing a <link linkend="BUILDER-UI">GtkBuilder UI definition</link> and 
- * merges it with the current contents of @builder. 
+ * Parses a file containing a <link linkend="BUILDER-UI">GtkBuilder 
+ * UI definition</link> and merges it with the current contents of @builder. 
  * 
  * Returns: A positive value on success, 0 if an error occurred
  *
@@ -599,12 +657,13 @@ gtk_builder_add_from_file (GtkBuilder   *builder,
                            const gchar  *filename,
                            GError      **error)
 {
-  char *buffer;
+  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, FALSE);
 
   tmp_error = NULL;
 
@@ -614,18 +673,21 @@ gtk_builder_add_from_file (GtkBuilder   *builder,
       return 0;
     }
   
+  g_free (builder->priv->filename);
+  builder->priv->filename = g_strdup (filename);
+
   _gtk_builder_parser_parse_buffer (builder, filename,
                                     buffer, length,
                                     &tmp_error);
 
+  g_free (buffer);
+
   if (tmp_error != NULL)
     {
       g_propagate_error (error, tmp_error);
       return 0;
     }
 
-  g_free (buffer);
-
   return 1;
 }
 
@@ -634,10 +696,10 @@ gtk_builder_add_from_file (GtkBuilder   *builder,
  * @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
+ * @error: return location for an error, or %NULL
  *
- * Parses a file containing a <link linkend="BUILDER-UI">GtkBuilder UI definition</link> and 
- * merges it with the current contents of @builder. 
+ * Parses a string containing a <link linkend="BUILDER-UI">GtkBuilder 
+ * UI definition</link> and merges it with the current contents of @builder. 
  * 
  * Returns: A positive value on success, 0 if an error occurred
  *
@@ -653,9 +715,13 @@ gtk_builder_add_from_string (GtkBuilder   *builder,
 
   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, FALSE);
 
   tmp_error = NULL;
 
+  g_free (builder->priv->filename);
+  builder->priv->filename = g_strdup (".");
+
   _gtk_builder_parser_parse_buffer (builder, "<input>",
                                     buffer, length,
                                     &tmp_error);
@@ -673,10 +739,11 @@ gtk_builder_add_from_string (GtkBuilder   *builder,
  * @builder: a #GtkBuilder
  * @name: name of object to get
  *
- * Gets the object named @name.
+ * 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
+ *    found in the object tree
  *
  * Since: 2.12
  **/
@@ -691,9 +758,9 @@ gtk_builder_get_object (GtkBuilder  *builder,
 }
 
 static void
-object_add_to_list (gchar *object_id,
-                    GObject *object,
-                    GSList **list)
+object_add_to_list (gchar    *object_id,
+                    GObject  *object,
+                    GSList  **list)
 {
   *list = g_slist_prepend (*list, object);
 }
@@ -702,10 +769,13 @@ object_add_to_list (gchar *object_id,
  * gtk_builder_get_objects:
  * @builder: a #GtkBuilder
  *
- * Gets all objects that have been constructed by @builder.
+ * Gets all objects that have been constructed by @builder. Note that 
+ * this function does not increment the reference counts of the returned
+ * objects.
  *
  * Return value: a newly-allocated #GSList containing all the objects
- *   constructed by the #GtkBuilder instance
+ *   constructed by the #GtkBuilder instance. It should be freed by
+ *   g_slist_free()
  *
  * Since: 2.12
  **/
@@ -726,10 +796,8 @@ gtk_builder_get_objects (GtkBuilder *builder)
  * @builder: a #GtkBuilder
  * @domain: the translation domain or %NULL
  *
- * Sets the translation domain and uses dgettext() for translating the
- * property values marked as translatable from an interface description.
- * You can also pass in %NULL to this method to use gettext() instead of
- * dgettext().
+ * Sets the translation domain of @builder. 
+ * See #GtkBuilder:translation-domain.
  *
  * Since: 2.12
  **/
@@ -752,7 +820,7 @@ gtk_builder_set_translation_domain (GtkBuilder  *builder,
  * gtk_builder_get_translation_domain:
  * @builder: a #GtkBuilder
  *
- * Gets the translation domain.
+ * Gets the translation domain of @builder.
  *
  * Return value: the translation domain. This string is owned
  * by the builder object and must not be modified or freed.
@@ -786,7 +854,7 @@ gtk_builder_connect_signals_default (GtkBuilder    *builder,
   
   if (!g_module_symbol (args->module, handler_name, (gpointer)&func))
     {
-      g_warning ("could not find signal handler '%s'", handler_name);
+      g_warning ("Could not find signal handler '%s'", handler_name);
       return;
     }
 
@@ -803,14 +871,20 @@ gtk_builder_connect_signals_default (GtkBuilder    *builder,
  * @user_data: a pointer to a structure sent in as user data to all signals
  *
  * This method is a simpler variation of gtk_builder_connect_signals_full().
- * 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
+ * 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.
  * 
  * Note that this function will not work correctly if #GModule is not
  * supported on the platform.
  *
+ * When compiling applications for Windows, you must declare signal callbacks
+ * with #G_MODULE_EXPORT, or they will not be put in the symbol table.
+ * On Linux and Unices, this is not necessary; applications should instead
+ * be compiled with the -Wl,--export-dynamic CFLAGS, and linked against
+ * gmodule-export-2.0.
+ *
  * Since: 2.12
  **/
 void
@@ -822,7 +896,7 @@ gtk_builder_connect_signals (GtkBuilder *builder,
   g_return_if_fail (GTK_IS_BUILDER (builder));
   
   if (!g_module_supported ())
-    g_error ("gtk_builder_connect_signals requires working GModule");
+    g_error ("gtk_builder_connect_signals() requires working GModule");
 
   args = g_slice_new0 (connect_args);
   args->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
@@ -839,10 +913,10 @@ gtk_builder_connect_signals (GtkBuilder *builder,
 /**
  * GtkBuilderConnectFunc:
  * @builder: a #GtkBuilder
- * @object: a GObject subclass to connect a signal to
+ * @object: object to connect a signal to
  * @signal_name: name of the signal
  * @handler_name: name of the handler
- * @connect_object: GObject, if non-%NULL, use g_signal_connect_object.
+ * @connect_object: a #GObject, if non-%NULL, use g_signal_connect_object()
  * @flags: #GConnectFlags to use
  * @user_data: user data
  *
@@ -858,12 +932,12 @@ gtk_builder_connect_signals (GtkBuilder *builder,
 /**
  * gtk_builder_connect_signals_full:
  * @builder: a #GtkBuilder
- * @func: the function used to connect the signals.
- * @user_data: arbitrary data that will be passed to the connection function.
+ * @func: 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
- * version of gtk_builder_signal_autoconnect(), except that it does not
- * require gmodule to function correctly.
+ * version of gtk_builder_connect_signals(), except that it does not
+ * require GModule to function correctly.
  *
  * Since: 2.12
  */
@@ -901,7 +975,7 @@ gtk_builder_connect_signals_full (GtkBuilder            *builder,
          connect_object = g_hash_table_lookup (builder->priv->objects,
                                                signal->connect_object_name);
          if (!connect_object)
-             g_warning ("could not lookup object %s on signal %s of object %s",
+             g_warning ("Could not lookup object %s on signal %s of object %s",
                         signal->connect_object_name, signal->name,
                         signal->object_name);
        }
@@ -916,29 +990,39 @@ gtk_builder_connect_signals_full (GtkBuilder            *builder,
 }
 
 /**
- * gtk_builder_value_from_string
- * @pspec: the GParamSpec for the property
- * @string: the string representation of the value.
- * @value: the GValue to store the result in.
+ * gtk_builder_value_from_string:
+ * @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
  *
- * This function demarshals a value from a string.  This function
+ * 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
- * GtkAdjustment type values.  Support for GtkWidget type values is
+ * ulong, enum, flags, float, double, string, #GdkColor and
+ * #GtkAdjustment type values. Support for #GtkWidget type values is
  * still to come.
  *
- * Returns: %TRUE on success.
+ * Returns: %TRUE on success
  *
  * Since: 2.12
  */
 gboolean
-gtk_builder_value_from_string (GParamSpec  *pspec,
-                               const gchar *string,
-                               GValue      *value)
+gtk_builder_value_from_string (GtkBuilder   *builder,
+                              GParamSpec   *pspec,
+                               const gchar  *string,
+                               GValue       *value,
+                              GError      **error)
 {
+  g_return_val_if_fail (GTK_IS_BUILDER (builder), FALSE);
+  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
+  g_return_val_if_fail (string != NULL, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
   /*
    * GParamSpecUnichar has the internal type G_TYPE_UINT,
    * so we cannot handle this in the switch, do it separately
@@ -953,31 +1037,40 @@ gtk_builder_value_from_string (GParamSpec  *pspec,
       return TRUE;
     }
 
-  return gtk_builder_value_from_string_type (G_PARAM_SPEC_VALUE_TYPE (pspec),
-                                             string, value);
+  return gtk_builder_value_from_string_type (builder,
+                                            G_PARAM_SPEC_VALUE_TYPE (pspec),
+                                             string, value, error);
 }
 
 /**
- * gtk_builder_value_from_string_type
- * @type: the GType of the value
- * @string: the string representation of the value.
- * @value: the GValue to store the result in.
+ * gtk_builder_value_from_string_type:
+ * @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
  *
- * Like gtk_builder_value_from_string(), but takes a #GType instead of #GParamSpec.
+ * 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.
  *
- * Returns: %TRUE on success.
+ * Returns: %TRUE on success
  *
  * Since: 2.12
  */
 gboolean
-gtk_builder_value_from_string_type (GType        type,
-                                    const gchar *string,
-                                    GValue      *value)
+gtk_builder_value_from_string_type (GtkBuilder   *builder,
+                                   GType         type,
+                                    const gchar  *string,
+                                    GValue       *value,
+                                   GError      **error)
 {
   gboolean ret = TRUE;
 
   g_return_val_if_fail (type != G_TYPE_INVALID, FALSE);
   g_return_val_if_fail (string != NULL, FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
   g_value_init (value, type);
 
@@ -993,29 +1086,10 @@ gtk_builder_value_from_string_type (GType        type,
       {
         gboolean b;
 
-        switch (g_ascii_tolower (string[0]))
-          {
-          case 't':
-          case 'y':
-            b = TRUE;
-            break;
-          case 'f':
-          case 'n':
-            b = FALSE;
-            break;
-          default:
-            {
-              gchar *endptr;
-              errno = 0;
-              b = strtol (string, &endptr, 0);
-              if (errno || endptr == string)
-                {
-                  g_warning ("could not parse int `%s'", string);
-                  ret = FALSE;
-                  break;
-                }
-            }
-            break;
+       if (!_gtk_builder_boolean_from_string (string, &b, error))
+         {
+           ret = FALSE;
+           break;
           }
         g_value_set_boolean (value, b);
         break;
@@ -1029,7 +1103,11 @@ gtk_builder_value_from_string_type (GType        type,
         l = strtol (string, &endptr, 0);
         if (errno || endptr == string)
           {
-            g_warning ("could not parse long `%s'", string);
+           g_set_error (error,
+                        GTK_BUILDER_ERROR,
+                        GTK_BUILDER_ERROR_INVALID_VALUE,
+                        "Could not parse integer `%s'",
+                        string);
             ret = FALSE;
             break;
           }
@@ -1048,7 +1126,11 @@ gtk_builder_value_from_string_type (GType        type,
         ul = strtoul (string, &endptr, 0);
         if (errno || endptr == string)
           {
-            g_warning ("could not parse ulong `%s'", string);
+           g_set_error (error,
+                        GTK_BUILDER_ERROR,
+                        GTK_BUILDER_ERROR_INVALID_VALUE,
+                        "Could not parse unsigned integer `%s'",
+                        string);
             ret = FALSE;
             break;
           }
@@ -1059,21 +1141,42 @@ gtk_builder_value_from_string_type (GType        type,
         break;
       }
     case G_TYPE_ENUM:
-      g_value_set_enum (value, _gtk_builder_enum_from_string (type, string));
-      break;
+      {
+       gint enum_value;
+       if (!_gtk_builder_enum_from_string (type, string, &enum_value, error))
+         {
+           ret = FALSE;
+           break;
+          }
+       g_value_set_enum (value, enum_value);
+       break;
+      }
     case G_TYPE_FLAGS:
-      g_value_set_flags (value, _gtk_builder_flags_from_string (type, string));
-      break;
+      {
+       guint flags_value;
+
+       if (!_gtk_builder_flags_from_string (type, string, &flags_value, error))
+         {
+           ret = FALSE;
+           break;
+          }
+       g_value_set_flags (value, flags_value);
+       break;
+      }
     case G_TYPE_FLOAT:
     case G_TYPE_DOUBLE:
       {
-        double d;
+        gdouble d;
         gchar *endptr;
         errno = 0;
         d = g_ascii_strtod (string, &endptr);
         if (errno || endptr == string)
           {
-            g_warning ("could not parse double `%s'", string);
+           g_set_error (error,
+                        GTK_BUILDER_ERROR,
+                        GTK_BUILDER_ERROR_INVALID_VALUE,
+                        "Could not parse double `%s'",
+                        string);
             ret = FALSE;
             break;
           }
@@ -1097,46 +1200,79 @@ gtk_builder_value_from_string_type (GType        type,
             g_value_set_boxed (value, &colour);
           else
             {
-              g_warning ("could not parse colour name `%s'", string);
+             g_set_error (error,
+                          GTK_BUILDER_ERROR,
+                          GTK_BUILDER_ERROR_INVALID_VALUE,
+                          "Could not parse color `%s'",
+                          string);
               ret = FALSE;
             }
         }
       else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
         {
-          char **vector = g_strsplit (string, "\n", 0);
+          gchar **vector = g_strsplit (string, "\n", 0);
           g_value_take_boxed (value, vector);
         }
       else
         ret = FALSE;
       break;
     case G_TYPE_OBJECT:
-#if 0
-        if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
-      {
-        gchar *filename;
-        GError *error = NULL;
-        GdkPixbuf *pixbuf;
+      if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
+        {
+          gchar *filename;
+          GError *tmp_error = NULL;
+          GdkPixbuf *pixbuf;
+       
+          if (gtk_builder_get_object (builder, string))
+            {
+              g_set_error (error,
+                           GTK_BUILDER_ERROR,
+                           GTK_BUILDER_ERROR_INVALID_VALUE,
+                           "Could not load image '%s': "
+                           " '%s' is already used as object id",
+                           string, string);
+              return FALSE;
+            }
 
-        filename = gtk_xml_relative_file (xml, string);
-        pixbuf = gdk_pixbuf_new_from_file (filename, &error);
-        if (pixbuf)
-          {
-            g_value_set_object (value, pixbuf);
-            g_object_unref (G_OBJECT (pixbuf));
-          }
-        else
-          {
-            g_warning ("Error loading image: %s", error->message);
-            g_error_free (error);
-            ret = FALSE;
-          }
-        g_free (filename);
-      }
-        else
-#endif
-          ret = FALSE;
+         filename = _gtk_builder_get_absolute_filename (builder, string);
+          pixbuf = gdk_pixbuf_new_from_file (filename, &tmp_error);
+
+          if (pixbuf == NULL)
+            {
+              GtkIconTheme *theme;
+
+              g_warning ("Could not load image '%s': %s", 
+                         string, tmp_error->message);
+              g_error_free (tmp_error);
+
+              /* fall back to a missing image */
+              theme = gtk_icon_theme_get_default ();
+              pixbuf = gtk_icon_theme_load_icon (theme, 
+                                                 GTK_STOCK_MISSING_IMAGE,
+                                                 16,
+                                                 GTK_ICON_LOOKUP_USE_BUILTIN,
+                                                 NULL);
+            }
+          if (pixbuf)
+            {
+              g_value_set_object (value, pixbuf);
+              g_object_unref (G_OBJECT (pixbuf));
+            }
+
+          g_free (filename);
+
+          ret = TRUE;
+        }
+      else
+        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;
     }
@@ -1144,131 +1280,168 @@ gtk_builder_value_from_string_type (GType        type,
   return ret;
 }
 
-static gint
-_gtk_builder_enum_from_string (GType type, const char *string)
+static gboolean
+_gtk_builder_enum_from_string (GType         type, 
+                               const gchar  *string,
+                              gint         *enum_value,
+                              GError      **error)
 {
   GEnumClass *eclass;
   GEnumValue *ev;
   gchar *endptr;
-  gint ret = 0;
-
-  g_return_val_if_fail (G_TYPE_IS_ENUM (type), 0);
-  g_return_val_if_fail (string != NULL, 0);
+  gint value;
+  gboolean ret;
   
-  ret = strtoul (string, &endptr, 0);
-  if (endptr != string) /* parsed a number */
-    return ret;
-
-  eclass = g_type_class_ref (type);
-  ev = g_enum_get_value_by_name (eclass, string);
-  if (!ev)
-    ev = g_enum_get_value_by_nick (eclass, string);
-
-  if (ev)
-    ret = ev->value;
+  g_return_val_if_fail (G_TYPE_IS_ENUM (type), FALSE);
+  g_return_val_if_fail (string != NULL, FALSE);
+  
+  ret = TRUE;
 
-  g_type_class_unref (eclass);
+  value = strtoul (string, &endptr, 0);
+  if (endptr != string) /* parsed a number */
+    *enum_value = value;
+  else
+    {
+      eclass = g_type_class_ref (type);
+      ev = g_enum_get_value_by_name (eclass, string);
+      if (!ev)
+       ev = g_enum_get_value_by_nick (eclass, string);
 
+      if (ev)
+       *enum_value = ev->value;
+      else
+       {
+         g_set_error (error,
+                      GTK_BUILDER_ERROR,
+                      GTK_BUILDER_ERROR_INVALID_VALUE,
+                      "Could not parse enum: `%s'",
+                      string);
+         ret = FALSE;
+       }
+      
+      g_type_class_unref (eclass);
+    }
+  
   return ret;
 }
 
-guint
-_gtk_builder_flags_from_string (GType type, const char *string)
+gboolean
+_gtk_builder_flags_from_string (GType         type, 
+                                const gchar  *string,
+                               guint        *flags_value,
+                               GError      **error)
 {
   GFlagsClass *fclass;
   gchar *endptr, *prevptr;
-  guint i, j, ret;
-  char *flagstr;
+  guint i, j, value;
+  gchar *flagstr;
   GFlagsValue *fv;
-  const char  *flag;
+  const gchar *flag;
   gunichar ch;
-  gboolean eos;
+  gboolean eos, ret;
 
-  g_return_val_if_fail (G_TYPE_IS_FLAGS (type), 0);
-  g_return_val_if_fail (string != 0, 0);
+  g_return_val_if_fail (G_TYPE_IS_FLAGS (type), FALSE);
+  g_return_val_if_fail (string != 0, FALSE);
 
-  ret = strtoul (string, &endptr, 0);
+  ret = TRUE;
+  
+  value = strtoul (string, &endptr, 0);
   if (endptr != string) /* parsed a number */
-    return ret;
-
-  fclass = g_type_class_ref (type);
-
-  flagstr = g_strdup (string);
-  for (ret = i = j = 0; ; i++)
+    *flags_value = value;
+  else
     {
+      fclass = g_type_class_ref (type);
 
-      eos = flagstr[i] == '\0';
-
-      if (!eos && flagstr[i] != '|')
-        continue;
-
-      flag = &flagstr[j];
-      endptr = &flagstr[i];
-
-      if (!eos)
-        {
-          flagstr[i++] = '\0';
-          j = i;
-        }
-
-      /* trim spaces */
-      for (;;)
-        {
-          ch = g_utf8_get_char (flag);
-          if (!g_unichar_isspace (ch))
-            break;
-          flag = g_utf8_next_char (flag);
-        }
-
-      while (endptr > flag)
-        {
-          prevptr = g_utf8_prev_char (endptr);
-          ch = g_utf8_get_char (prevptr);
-          if (!g_unichar_isspace (ch))
-            break;
-          endptr = prevptr;
-        }
-
-      if (endptr > flag)
-        {
-          *endptr = '\0';
-          fv = g_flags_get_value_by_name (fclass, flag);
-
-          if (!fv)
-            fv = g_flags_get_value_by_nick (fclass, flag);
-
-          if (fv)
-            ret |= fv->value;
-          else
-            g_warning ("Unknown flag: '%s'", flag);
-        }
-
-      if (eos)
-        break;
+      flagstr = g_strdup (string);
+      for (value = i = j = 0; ; i++)
+       {
+         
+         eos = flagstr[i] == '\0';
+         
+         if (!eos && flagstr[i] != '|')
+           continue;
+         
+         flag = &flagstr[j];
+         endptr = &flagstr[i];
+         
+         if (!eos)
+           {
+             flagstr[i++] = '\0';
+             j = i;
+           }
+         
+         /* trim spaces */
+         for (;;)
+           {
+             ch = g_utf8_get_char (flag);
+             if (!g_unichar_isspace (ch))
+               break;
+             flag = g_utf8_next_char (flag);
+           }
+         
+         while (endptr > flag)
+           {
+             prevptr = g_utf8_prev_char (endptr);
+             ch = g_utf8_get_char (prevptr);
+             if (!g_unichar_isspace (ch))
+               break;
+             endptr = prevptr;
+           }
+         
+         if (endptr > flag)
+           {
+             *endptr = '\0';
+             fv = g_flags_get_value_by_name (fclass, flag);
+             
+             if (!fv)
+               fv = g_flags_get_value_by_nick (fclass, flag);
+             
+             if (fv)
+               value |= fv->value;
+             else
+               {
+                 g_set_error (error,
+                              GTK_BUILDER_ERROR,
+                              GTK_BUILDER_ERROR_INVALID_VALUE,
+                              "Unknown flag: `%s'",
+                              flag);
+                 ret = FALSE;
+                 break;
+               }
+           }
+         
+         if (eos)
+           {
+             *flags_value = value;
+             break;
+           }
+       }
+      
+      g_free (flagstr);
+      
+      g_type_class_unref (fclass);
     }
 
-  g_free (flagstr);
-
-  g_type_class_unref (fclass);
-
   return ret;
 }
 
 /**
  * gtk_builder_get_type_from_name:
  * @builder: a #GtkBuilder
- * @type_name: Type name to lookup
+ * @type_name: type name to lookup
  *
- * This method is used to lookup a type. It can be implemented in a 
- * subclass to override the #GType of an object created by the builder.
+ * Looks up a type by name, using the virtual function that 
+ * #GtkBuilder has for that purpose. This is mainly used when
+ * implementing the #GtkBuildable interface on a type.
  *
- * Returns: the #GType found for @type_name or #G_TYPE_INVALID if no
- *   type was found
+ * Returns: the #GType found for @type_name or #G_TYPE_INVALID 
+ *   if no type was found
  *
- * Since 2.12
+ * Since: 2.12
  */
 GType
-gtk_builder_get_type_from_name (GtkBuilder *builder, const gchar *type_name)
+gtk_builder_get_type_from_name (GtkBuilder  *builder, 
+                                const gchar *type_name)
 {
   g_return_val_if_fail (GTK_IS_BUILDER (builder), G_TYPE_INVALID);
   g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID);
@@ -1276,21 +1449,32 @@ gtk_builder_get_type_from_name (GtkBuilder *builder, const gchar *type_name)
   return GTK_BUILDER_GET_CLASS (builder)->get_type_from_name (builder, type_name);
 }
 
-/**
- * gtk_builder_error_quark:
- *
- * Registers an error quark for #GtkBuilder if necessary.
- * 
- * Return value: The error quark used for #GtkBuilder errors.
- *
- * Since: 2.12
- **/
 GQuark
 gtk_builder_error_quark (void)
 {
   return g_quark_from_static_string ("gtk-builder-error-quark");
 }
 
+gchar *
+_gtk_builder_get_absolute_filename (GtkBuilder *builder, const gchar *string)
+{
+  gchar *filename;
+  gchar *dirname = NULL;
+  
+  if (g_path_is_absolute (string))
+    return g_strdup (string);
+
+  if (builder->priv->filename &&
+      strcmp (builder->priv->filename, ".") != 0)
+    dirname = g_path_get_dirname (builder->priv->filename);
+  else
+    dirname = g_get_current_dir ();
+    
+  filename = g_build_filename (dirname, string, NULL);
+  g_free (dirname);
+  
+  return filename;
+}
 
 #define __GTK_BUILDER_C__
 #include "gtkaliasdef.c"