]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkbuilder.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkbuilder.c
index 7ed026c0a7350b43fd6be207ab6641697f462c8b..3d946053c669ad670ae417ae822b54ad83f52f50 100644 (file)
@@ -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 <http://www.gnu.org/licenses/>.
  */
 
 /**
  * <title>GtkBuilder UI Definitions</title>
  * <para>
  * 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 <firstterm>GtkBuilder UI definitions</firstterm> or
- * just <firstterm>UI definitions</firstterm> 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 <firstterm>GtkBuilder UI definitions</firstterm>
+ * or just <firstterm>UI definitions</firstterm> if the context is clear. Do not
  * confuse GtkBuilder UI Definitions with
  * <link linkend="XML-UI">GtkUIManager UI Definitions</link>, which are more
- * limited in scope.
+ * limited in scope. It is common to use <filename>.ui</filename> as the filename extension for files containing GtkBuilder UI definitions.
  * </para>
- * <programlisting><![CDATA[
- * <!ELEMENT interface (requires|object)* >
- * <!ELEMENT object    (property|signal|child|ANY)* >
- * <!ELEMENT property  PCDATA >
- * <!ELEMENT signal    EMPTY >
- * <!ELEMENT requires  EMPTY >
- * <!ELEMENT child     (object|ANY*) >
- *
- * <!ATTLIST interface  domain                     #IMPLIED >
- * <!ATTLIST object     id                         #REQUIRED
- *                      class                      #REQUIRED
- *                      type-func                  #IMPLIED
- *                      constructor                #IMPLIED >
- * <!ATTLIST requires   lib                        #REQUIRED
- *                      version                    #REQUIRED >
- * <!ATTLIST property   name                       #REQUIRED
- *                      translatable               #IMPLIED
- *                      comments               #IMPLIED
- *                      context                #IMPLIED >
- * <!ATTLIST signal     name                       #REQUIRED
- *                      handler                    #REQUIRED
- *                      after                      #IMPLIED
- *                      swapped                    #IMPLIED
- *                      object                     #IMPLIED
- *                      last_modification_time #IMPLIED >
- * <!ATTLIST child      type                       #IMPLIED
- *                      internal-child             #IMPLIED >
- * ]]></programlisting>
+ * <programlisting>
+ * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gtk/gtkbuilder.rnc">
+ *   <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
+ * </xi:include>
+ * </programlisting>
  * <para>
  * The toplevel element is &lt;interface&gt;. It optionally takes a "domain"
  * attribute, which will make the builder look for translated strings using
  * (can be specified by their name, nick or integer value), flags (can be
  * specified by their name, nick, integer value, optionally combined with "|",
  * e.g. "GTK_VISIBLE|GTK_REALIZED")  and colors (in a format understood by
- * gdk_color_parse()). Objects can be referred to by their name. Pixbufs can be
- * specified as a filename of an image file to load. In general, GtkBuilder
- * allows forward references to objects &mdash; an object doesn't have to be
- * constructed before it can be referred to. The exception to this rule is that
- * an object has to be constructed before it can be used as the value of a
- * construct-only property.
+ * gdk_color_parse()). Pixbufs can be specified as a filename of an image file to load. 
+ * Objects can be referred to by their name and by default refer to objects declared
+ * in the local xml fragment and objects exposed via gtk_builder_expose_object().
+ * 
+ * In general, GtkBuilder allows forward references to objects &mdash declared
+ * in the local xml; an object doesn't have to be constructed before it can be referred to. 
+ * The exception to this rule is that an object has to be constructed before 
+ * it can be used as the value of a construct-only property.
  *
  * Signal handlers are set up with the &lt;signal&gt; element. The "name"
  * attribute specifies the name of the signal, and the "handler" attribute
  * <link linkend="GtkUIManager-BUILDER-UI">GtkUIManager</link>,
  * <link linkend="GtkActionGroup-BUILDER-UI">GtkActionGroup</link>.
  * <link linkend="GtkMenuItem-BUILDER-UI">GtkMenuItem</link>,
+ * <link linkend="GtkMenuToolButton-BUILDER-UI">GtkMenuToolButton</link>,
  * <link linkend="GtkAssistant-BUILDER-UI">GtkAssistant</link>,
- * <link linkend="GtkScale-BUILDER-UI">GtkScale</link>.
+ * <link linkend="GtkScale-BUILDER-UI">GtkScale</link>,
+ * <link linkend="GtkComboBoxText-BUILDER-UI">GtkComboBoxText</link>,
+ * <link linkend="GtkRecentFilter-BUILDER-UI">GtkRecentFilter</link>,
+ * <link linkend="GtkFileFilter-BUILDER-UI">GtkFileFilter</link>,
+ * <link linkend="GtkTextTagTable-BUILDER-UI">GtkTextTagTable</link>.
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Embedding other XML</title>
+ * <para>
+ * Apart from the language for UI descriptions that has been explained
+ * in the previous section, GtkBuilder can also parse XML fragments
+ * of <link linkend="gio-GMenu-Markup">GMenu markup</link>. The resulting
+ * #GMenu object and its named submenus are available via
+ * gtk_builder_get_object() like other constructed objects.
  * </para>
  * </refsect2>
  */
@@ -281,6 +273,7 @@ struct _GtkBuilderPrivate
   GSList *delayed_properties;
   GSList *signals;
   gchar *filename;
+  gchar *resource_prefix;
 };
 
 G_DEFINE_TYPE (GtkBuilder, gtk_builder, G_TYPE_OBJECT)
@@ -341,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);
 
@@ -498,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 (&parameter.value, G_OBJECT_TYPE (object));
+              g_value_set_object (&parameter.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 (&parameter.value, G_OBJECT_TYPE (object));
-              g_value_set_object (&parameter.value, object);
-            }
-          else
-            {
+              /* Delay setting property */
               property = g_slice_new (DelayedProperty);
               property->object = g_strdup (object_name);
               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;
             }
         }
@@ -535,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);
@@ -582,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,
@@ -711,22 +723,16 @@ _gtk_builder_construct (GtkBuilder *builder,
       g_value_unset (&param->value);
     }
   g_array_free (parameters, TRUE);
-  
-  if (GTK_IS_BUILDABLE (obj))
-    gtk_buildable_set_name (buildable, info->id);
-  else
-    g_object_set_data_full (obj,
-                            "gtk-builder-name",
-                            g_strdup (info->id),
-                            g_free);
 
-  /* we already own a reference to obj.  put it in the hash table. */
-  g_hash_table_insert (builder->priv->objects, g_strdup (info->id), obj);
+  /* put it in the hash table. */
+  _gtk_builder_add_object (builder, info->id, obj);
+  
+  /* we already own a reference to obj. */ 
+  g_object_unref (obj);
   
   return obj;
 }
 
-
 void
 _gtk_builder_add (GtkBuilder *builder,
                   ChildInfo  *child_info)
@@ -891,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,
@@ -913,7 +921,7 @@ 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
+ * @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 <link linkend="BUILDER-UI">GtkBuilder 
@@ -958,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,
@@ -976,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 <link linkend="BUILDER-UI">GtkBuilder
+ * UI definition</link> 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>", 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 <link linkend="BUILDER-UI">GtkBuilder
+ * UI definition</link> 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.
+ *
+ * <note><para>
+ * 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.
+ * </para></note>
+ *
+ * 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>", 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
@@ -1008,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, "<input>",
                                     buffer, length,
@@ -1065,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, "<input>",
                                     buffer, length,
@@ -1182,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
+ * &lt;external-object&gt; is defined. It is similar to &lt;object&gt; but has 
+ * to reference an external object exposed with this function.
+ * This way you can change properties and even add children to an
+ * external object using builder, not just reference it.
+ * 
+ * Since: 3.8
+ **/
+void
+gtk_builder_expose_object (GtkBuilder    *builder,
+                           const gchar   *name,
+                           GObject       *object)
+{
+  g_return_if_fail (GTK_IS_BUILDER (builder));
+  g_return_if_fail (name && name[0]);
+  g_return_if_fail (gtk_builder_get_object (builder, name) == NULL);
+
+  object_set_name (object, name);
+  g_hash_table_insert (builder->priv->objects,
+                       g_strdup (name),
+                       g_object_ref (object));
+}
+
+
 typedef struct {
   GModule *module;
   gpointer data;
@@ -1221,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.
@@ -1271,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
  */
@@ -1341,7 +1541,7 @@ 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
+ * @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
@@ -1349,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.
  *
@@ -1387,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);
@@ -1397,7 +1622,7 @@ 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
+ * @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 
@@ -1430,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]);
@@ -1542,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))
         {
@@ -1563,7 +1799,7 @@ gtk_builder_value_from_string_type (GtkBuilder   *builder,
         {
           GdkRGBA rgba = { 0 };
 
-          if (gdk_rgba_parse (string, &rgba))
+          if (gdk_rgba_parse (&rgba, string))
             g_value_set_boxed (value, &rgba);
           else
             {
@@ -1595,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))
             {
@@ -1608,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)
             {
@@ -1838,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)
 {