Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Impressum gtkbuilder.c

  Sprache: C
 

/* GTK - The GIMP Toolkit
 * Copyright (C) 1998-2002 James Henstridge <james@daa.com.au>
 * Copyright (C) 2006-2007 Async Open Source,
 *                         Johan Dahlin <jdahlin@async.com.br>,
 *                         Henrique Romano <henrique@async.com.br>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * 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, see <http://www.gnu.org/licenses/>.
 */


/**
 * GtkBuilder:
 *
 * Reads XML descriptions of a user interface and instantiates the described objects.
 *
 * To create a `GtkBuilder` from a user interface description, call
 * [ctor@Gtk.Builder.new_from_file], [ctor@Gtk.Builder.new_from_resource]
 * or [ctor@Gtk.Builder.new_from_string].
 *
 * In the (unusual) case that you want to add user interface
 * descriptions from multiple sources to the same `GtkBuilder` you can
 * call [ctor@Gtk.Builder.new] to get an empty builder and populate it by
 * (multiple) calls to [method@Gtk.Builder.add_from_file],
 * [method@Gtk.Builder.add_from_resource] or
 * [method@Gtk.Builder.add_from_string].
 *
 * 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
 * [method@Gtk.Window.destroy] to get rid of them and all the widgets
 * they contain.
 *
 * The functions [method@Gtk.Builder.get_object] and
 * [method@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 [method@Gtk.Window.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.
 *
 * ## GtkBuilder UI Definitions
 *
 * `GtkBuilder` parses textual descriptions of user interfaces which are
 * specified in XML format. We refer to these descriptions as GtkBuilder
 * UI definitions or just UI definitions if the context is clear.
 *
 * ### Structure of UI definitions
 *
 * UI definition files are always encoded in UTF-8.
 *
 * 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 [method@Gtk.Builder.set_translation_domain] on the builder.
 * For example:
 *
 * ```xml
 * <?xml version="1.0" encoding="UTF-8"?>
 * <interface domain="your-app">
 *   ...
 * </interface>
 * ```
 *
 * ### Requirements
 *
 * 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>`. `GtkBuilder` will
 * error out if the version requirements are not met. For example:
 *
 * ```xml
 * <?xml version="1.0" encoding="UTF-8"?>
 * <interface domain="your-app">
 *   <requires lib="gtk" version="4.0" />
 * </interface>
 * ```
 *
 * ### Objects
 *
 * Objects are defined as children of the `<interface>` element.
 *
 * 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.
 *
 * 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()` function 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()`
 * function explicitly with the "type-func" attribute. If your UI definition
 * is referencing internal types, you should make sure to call
 * `g_type_ensure()` for each object type before parsing the UI definition.
 *
 * Objects may be given a name with the id attribute, which allows the
 * application to retrieve them from the builder with
 * [method@Gtk.Builder.get_object]. An id is also necessary to use the
 * object as property value in other parts of the UI definition. GTK
 * reserves ids starting and ending with `___` (three consecutive
 * underscores) for its own purposes.
 *
 * ### Properties
 *
 * 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:
 *
 * ```xml
 * <object class="GtkButton">
 *   <property name="label">Hello, world</property>
 * </object>
 * ```
 *
 * 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:
 *
 * ```xml
 * <object class="GtkButton">
 *   <property name="label"
 *             translatable="yes"
 *             context="button"
 *             comments="A classic">Hello, world</property>
 * </object>
 * ```
 *
 * The xgettext tool that is part of gettext can extract these strings,
 * but note that it only looks for translatable="yes".
 *
 * `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 values, strings like FALSE, f, no, n, 0 are interpreted
 *   as false values)
 * - string lists (separated by newlines)
 * - enumeration types (can be specified by their full C identifier their short
 *   name used when registering the enumeration type, or their integer value)
 * - flag types (can be specified by their C identifier or short name,
 *   optionally combined with | for bitwise OR, or a single integer value
 *   e.g., GTK_INPUT_HINT_EMOJI|GTK_INPUT_HINT_LOWERCASE, or emoji|lowercase or 520).
 * - colors (in the format understood by [method@Gdk.RGBA.parse])
 * - transforms (in the format understood by [func@Gsk.Transform.parse])
 * - Pango attribute lists (in the format understood by [method@Pango.AttrList.to_string])
 * - Pango tab arrays (in the format understood by [method@Pango.TabArray.to_string])
 * - Pango font descriptions (in the format understood by [func@Pango.FontDescription.from_string])
 * - `GVariant` (in the format understood by [func@GLib.Variant.parse])
 * - textures (can be specified as an object id, a resource path or a filename of an image file to load relative to the Builder file or the CWD if [method@Gtk.Builder.add_from_string] was used)
 * - GFile (like textures, can be specified as an object id, a URI or a filename of a file to load relative to the Builder file or the CWD if [method@Gtk.Builder.add_from_string] was used)
 *
 * Objects can be referred to by their name and by default refer to
 * objects declared in the local XML fragment and objects exposed via
 * [method@Gtk.Builder.expose_object]. In general, `GtkBuilder` allows
 * forward references to objects declared in the local XML; an object
 * doesnt 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.
 *
 * ### Child objects
 *
 * Many widgets have properties for child widgets, such as
 * [property@Gtk.Expander:child]. In this case, the preferred way to
 * specify the child widget in a ui file is to simply set the property:
 *
 * ```xml
 * <object class="GtkExpander">
 *   <property name="child">
 *     <object class="GtkLabel">
 *     ...
 *     </object>
 *   </property>
 * </object>
 * ```
 *
 * Generic containers that can contain an arbitrary number of children,
 * such as [class@Gtk.Box] instead use the `<child>` element. A `<child>`
 * element contains an `<object>` element which describes the child object.
 * Most often, child objects are widgets inside a container, but they can
 * also be, e.g., actions in an action group, or columns in a tree model.
 *
 * Any object type that implements the [iface@Gtk.Buildable] interface can
 * specify how children may be added to it. Since many objects and widgets that
 * are included with GTK already implement the `GtkBuildable` interface,
 * typically child objects can be added using the `<child>` element without
 * having to be concerned about the underlying implementation.
 *
 * See the [`GtkWidget` documentation](class.Widget.html#gtkwidget-as-gtkbuildable)
 * for many examples of using `GtkBuilder` with widgets, including setting
 * child objects using the `<child>` element.
 *
 * A noteworthy special case to the general rule that only objects implementing
 * `GtkBuildable` may specify how to handle the `<child>` element is that
 * `GtkBuilder` provides special support for adding objects to a
 * [class@Gio.ListStore] by using the `<child>` element. For instance:
 *
 * ```xml
 * <object class="GListStore">
 *   <property name="item-type">MyObject</property>
 *   <child>
 *     <object class="MyObject" />
 *   </child>
 *   ...
 * </object>
 * ```
 *
 * ### Property bindings
 *
 * It is also possible to bind a property value to another object's
 * property value using the attributes "bind-source" to specify the
 * source object of the binding, and optionally, "bind-property" and
 * "bind-flags" to specify the source property and source binding flags
 * respectively. Internally, `GtkBuilder` implements this using
 * [class@GObject.Binding] objects.
 *
 * For instance, in the example below the label property of the
 * `bottom_label` widget is bound to the label property of the
 * `top_button` widget:
 *
 * ```xml
 * <object class="GtkBox">
 *   <property name="orientation">vertical</property>
 *   <child>
 *     <object class="GtkButton" id="top_button">
 *       <property name="label">Hello, world</property>
 *     </object>
 *   </child>
 *   <child>
 *     <object class="GtkLabel" id="bottom_label">
 *       <property name="label"
 *                 bind-source="top_button"
 *                 bind-property="label"
 *                 bind-flags="sync-create" />
 *     </object>
 *   </child>
 * </object>
 * ```
 *
 * For more information, see the documentation of the
 * [method@GObject.Object.bind_property] method.
 *
 * Please note that another way to set up bindings between objects in .ui files
 * is to use the `GtkExpression` methodology. See the
 * [`GtkExpression` documentation](class.Expression.html#gtkexpression-in-ui-files)
 * for more information.
 *
 * ### Internal children
 *
 * 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 content area
 * of a `GtkDialog`). This can be achieved by setting the internal-child
 * property 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.
 *
 * ### Specialized children
 *
 * 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.
 *
 * ### Signal handlers and function pointers
 *
 * 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.
 *
 * ```xml
 * <object class="GtkButton" id="hello_button">
 *   <signal name="clicked" handler="hello_button__clicked" />
 * </object>
 * ```
 *
 * The remaining attributes, after, swapped and object, have the
 * same meaning as the corresponding parameters of the
 * [func@GObject.signal_connect_object] or [func@GObject.signal_connect_data]
 * functions:
 *
 * - after matches the `G_CONNECT_AFTER` flag, and will ensure that the
 *   handler is called after the default class closure for the signal
 * - swapped matches the `G_CONNECT_SWAPPED` flag, and will swap the
 *   instance and closure arguments when invoking the signal handler
 * - object will bind the signal handler to the lifetime of the object
 *   referenced by the attribute
 *
 * By default "swapped" will be set to "yes" if not specified otherwise, in
 * the case where "object" is set, for convenience. A last_modification_time
 * attribute is also allowed, but it does not have a meaning to the builder.
 *
 * When compiling applications for Windows, you must declare signal callbacks
 * with the `G_MODULE_EXPORT` decorator, or they will not be put in the symbol
 * table:
 *
 * ```c
 * G_MODULE_EXPORT void
 * hello_button__clicked (GtkButton *button,
 *                        gpointer data)
 * {
 *   // ...
 * }
 * ```
 *
 * On Linux and Unix, this is not necessary; applications should instead
 * be compiled with the `-Wl,--export-dynamic` argument inside their compiler
 * flags, and linked against `gmodule-export-2.0`.
 *
 * ## Example UI Definition
 *
 * ```xml
 * <interface>
 *   <object class="GtkDialog" id="dialog1">
 *     <child internal-child="content_area">
 *       <object class="GtkBox">
 *         <child internal-child="action_area">
 *           <object class="GtkBox">
 *             <child>
 *               <object class="GtkButton" id="ok_button">
 *                 <property name="label" translatable="yes">_Ok</property>
 *                 <property name="use-underline">True</property>
 *                 <signal name="clicked" handler="ok_button_clicked"/>
 *               </object>
 *             </child>
 *           </object>
 *         </child>
 *       </object>
 *     </child>
 *   </object>
 * </interface>
 * ```
 *
 * ## Using GtkBuildable for extending UI definitions
 *
 * Objects can implement the [iface@Gtk.Buildable] interface to add custom
 * elements and attributes to the XML. Typically, any extension will be
 * documented in each type that implements the interface.
 *
 * ## Menus
 *
 * In addition to objects with properties that are created with `<object>` and
 * `<property>` elements, `GtkBuilder` also allows to parse XML menu definitions
 * as used by [class@Gio.Menu] when exporting menu models over D-Bus, and as
 * described in the [class@Gtk.PopoverMenu] documentation. Menus can be defined
 * as toplevel elements, or as property values for properties of type `GMenuModel`.
 *
 * ## Templates
 *
 * When describing a [class@Gtk.Widget], you can use the `<template>` tag to
 * describe a UI bound to a specific widget type. GTK will automatically load
 * the UI definition when instantiating the type, and bind children and
 * signal handlers to instance fields and function symbols.
 *
 * For more information, see the [`GtkWidget` documentation](class.Widget.html#building-composite-widgets-from-template-xml)
 * for details.
 */


#include "config.h"
#include <errno.h> /* errno */
#include <stdlib.h>
#include <string.h> /* strlen */

#include "gtkbuilderprivate.h"

#include "gtkbuildableprivate.h"
#include "gtkbuilderlistitemfactory.h"
#include "gtkbuilderscopeprivate.h"
#include "gtkdebug.h"
#include "gtkexpression.h"
#include "gtkmain.h"
#include "gtkprivate.h"
#include "gtkshortcutactionprivate.h"
#include "gtkshortcuttrigger.h"
#include "gtktypebuiltins.h"
#include "gtkiconthemeprivate.h"
#include "gtkdebug.h"


static void gtk_builder_finalize       (GObject         *object);
static void gtk_builder_set_property   (GObject         *object,
                                        guint            prop_id,
                                        const GValue    *value,
                                        GParamSpec      *pspec);
static void gtk_builder_get_property   (GObject         *object,
                                        guint            prop_id,
                                        GValue          *value,
                                        GParamSpec      *pspec);

enum {
  PROP_0,
  PROP_CURRENT_OBJECT,
  PROP_SCOPE,
  PROP_TRANSLATION_DOMAIN,
  LAST_PROP
};

static GParamSpec *builder_props[LAST_PROP];

struct _GtkBuilder
{
  GObject parent_instance;
};

struct _GtkBuilderClass
{
  GObjectClass parent_class;
};

typedef struct
{
  char *domain;
  GHashTable *objects;
  GSList *delayed_properties;
  GPtrArray *signals;
  GSList *bindings;
  char *filename;
  char *resource_prefix;
  GType template_type;
  gboolean allow_template_parents;
  GObject *current_object;
  GtkBuilderScope *scope;
} GtkBuilderPrivate;

G_DEFINE_TYPE_WITH_PRIVATE (GtkBuilder, gtk_builder, G_TYPE_OBJECT)

static void
gtk_builder_dispose (GObject *object)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (GTK_BUILDER (object));

  g_clear_object (&priv->current_object);
  g_clear_object (&priv->scope);

  G_OBJECT_CLASS (gtk_builder_parent_class)->dispose (object);
}

static void
gtk_builder_class_init (GtkBuilderClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  gobject_class->dispose = gtk_builder_dispose;
  gobject_class->finalize = gtk_builder_finalize;
  gobject_class->set_property = gtk_builder_set_property;
  gobject_class->get_property = gtk_builder_get_property;

 /**
  * GtkBuilder:translation-domain:
  *
  * The translation domain used when translating property values that
  * have been marked as translatable.
  *
  * If the translation domain is %NULL, `GtkBuilder` uses gettext(),
  * otherwise g_dgettext().
  */

  builder_props[PROP_TRANSLATION_DOMAIN] =
      g_param_spec_string ("translation-domain", NULL, NULL,
                           NULL,
                           G_PARAM_READWRITE | G_PARAM_STATIC_NAME);

 /**
  * GtkBuilder:current-object:
  *
  * The object the builder is evaluating for.
  */

  builder_props[PROP_CURRENT_OBJECT] =
      g_param_spec_object ("current-object", NULL, NULL,
                           G_TYPE_OBJECT,
                           G_PARAM_READWRITE | G_PARAM_STATIC_NAME);

 /**
  * GtkBuilder:scope:
  *
  * The scope the builder is operating in
  */

  builder_props[PROP_SCOPE] =
      g_param_spec_object ("scope", NULL, NULL,
                           GTK_TYPE_BUILDER_SCOPE,
                           G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_CONSTRUCT);

  g_object_class_install_properties (gobject_class, LAST_PROP, builder_props);
}

static void
gtk_builder_init (GtkBuilder *builder)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  priv->domain = NULL;
  priv->objects = g_hash_table_new_full (g_str_hash, g_str_equal,
                                         g_free, g_object_unref);
}


/*
 * GObject virtual methods
 */


static void
gtk_builder_finalize (GObject *object)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (GTK_BUILDER (object));

  g_free (priv->domain);
  g_free (priv->filename);
  g_free (priv->resource_prefix);

  if (GTK_DEBUG_CHECK (BUILDER_OBJECTS))
    {
      GHashTableIter iter;
      gpointer key, value;

      g_hash_table_iter_init (&iter, priv->objects);
      while (g_hash_table_iter_next (&iter, &key, &value))
        {
          if (G_OBJECT (value)->ref_count == 1)
            g_message ("builder: %s with id %s unused",
                       G_OBJECT_TYPE_NAME (value), (const char *)key);
        }
    }

  g_hash_table_destroy (priv->objects);
  if (priv->signals)
    g_ptr_array_free (priv->signals, TRUE);

  G_OBJECT_CLASS (gtk_builder_parent_class)->finalize (object);
}

static void
gtk_builder_set_property (GObject      *object,
                          guint         prop_id,
                          const GValue *value,
                          GParamSpec   *pspec)
{
  GtkBuilder *builder = GTK_BUILDER (object);

  switch (prop_id)
    {
    case PROP_CURRENT_OBJECT:
      gtk_builder_set_current_object (builder, g_value_get_object (value));
      break;

    case PROP_SCOPE:
      gtk_builder_set_scope (builder, g_value_get_object (value));
      break;

    case PROP_TRANSLATION_DOMAIN:
      gtk_builder_set_translation_domain (builder, g_value_get_string (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_builder_get_property (GObject    *object,
                          guint       prop_id,
                          GValue     *value,
                          GParamSpec *pspec)
{
  GtkBuilder *builder = GTK_BUILDER (object);
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  switch (prop_id)
    {
    case PROP_CURRENT_OBJECT:
      g_value_set_object (value, priv->current_object);
      break;

    case PROP_SCOPE:
      g_value_set_object (value, priv->scope);
      break;

    case PROP_TRANSLATION_DOMAIN:
      g_value_set_string (value, priv->domain);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

/*
 * GtkBuilder virtual methods
 */


typedef struct
{
  char *object;
  GParamSpec *pspec;
  char *value;
  int line;
  int col;
} DelayedProperty;

typedef struct
{
  GPtrArray *names;
  GArray *values;
} ObjectProperties;


static void
object_properties_init (ObjectProperties *self)
{
  self->names = NULL;
  self->values = NULL;
}

static void
object_properties_destroy (ObjectProperties *self)
{
  if (self == NULL)
    return;

  if (self->names)
    g_ptr_array_unref (self->names);

  if (self->values)
    g_array_unref (self->values);
}

static void
object_properties_add (ObjectProperties *self,
                       const char       *name,
                       const GValue     *value)
{
  if (!self->names)
    {
      self->names = g_ptr_array_sized_new (8);
      self->values = g_array_sized_new (FALSEFALSEsizeof (GValue), 8);
      g_array_set_clear_func (self->values, (GDestroyNotify) g_value_unset);
    }

  g_ptr_array_add (self->names, (char *) name);
  g_array_append_vals (self->values, value, 1);

  g_assert (self->names->len == self->values->len);
}

static const char *
object_properties_get_name (const ObjectProperties *self,
                            guint                   idx)
{
  g_assert (self->names);

  return g_ptr_array_index (self->names, idx);
}

static GValue *
object_properties_get_value (const ObjectProperties *self,
                             guint                   idx)
{
  g_assert (self->values);

  return &g_array_index (self->values, GValue, idx);
}

static void
gtk_builder_get_parameters (GtkBuilder         *builder,
                            GType               object_type,
                            const char         *object_name,
                            GPtrArray          *properties,
                            GParamFlags         filter_flags,
                            ObjectProperties   *parameters,
                            ObjectProperties   *filtered_parameters)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  DelayedProperty *property;
  GError *error = NULL;
  GObject *object;

  if (!properties)
    return;

  for (guint i = 0; i < properties->len; i++)
    {
      PropertyInfo *prop = g_ptr_array_index (properties, i);
      const char *property_name = prop->pspec->name;
      GValue property_value = G_VALUE_INIT;
      ObjectProperties *params;

      if (prop->applied)
        continue;

      if ((prop->pspec->flags & filter_flags) != 0)
        params = filtered_parameters;
      else
        params = parameters;

      if (!params)
        continue;

      if (prop->value)
        {
          g_value_init (&property_value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec));

          if (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) == GTK_TYPE_EXPRESSION)
            gtk_value_set_expression (&property_value, prop->value);
          else
            g_assert_not_reached ();
        }
      else if (prop->bound && (!prop->text || prop->text->len == 0))
        {
          /* Ignore properties with a binding and no value since they are
           * only there for to express the binding.
           */

          continue;
        }
      else if (G_IS_PARAM_SPEC_OBJECT (prop->pspec) &&
               (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_PIXBUF) &&
               (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_TEXTURE) &&
               (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GTK_TYPE_SHORTCUT_TRIGGER) &&
               (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GTK_TYPE_SHORTCUT_ACTION) &&
               (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != G_TYPE_FILE))
        {
          object = g_hash_table_lookup (priv->objects,
                                        g_strstrip (prop->text->str));

          if (object)
            {
              g_value_init (&property_value, G_OBJECT_TYPE (object));
              g_value_set_object (&property_value, object);
            }
          else if (!gtk_builder_value_from_string (builder, prop->pspec,
                                                   prop->text->str,
                                                   &property_value,
                                                   NULL))
            {
              if (prop->pspec->flags & G_PARAM_CONSTRUCT_ONLY)
                {
                  g_warning ("Failed to get construct only property "
                             "%s of %s with value '%s'",
                             prop->pspec->name, object_name, prop->text->str);
                  continue;
                }
              /* Delay setting property */

              prop->applied = TRUE;

              property = g_new (DelayedProperty, 1);
              property->pspec = prop->pspec;
              property->object = g_strdup (object_name);
              property->value = g_strdup (prop->text->str);
              property->line = prop->line;
              property->col = prop->col;
              priv->delayed_properties = g_slist_prepend (priv->delayed_properties,
                                                          property);
              continue;
            }
        }
      else if (!gtk_builder_value_from_string (builder, prop->pspec,
                                               prop->text->str,
                                               &property_value,
                                               &error))
        {

          g_warning ("Failed to set property %s.%s to %s: %s",
                     g_type_name (object_type), prop->pspec->name, prop->text->str,
                     error->message);

          g_clear_error (&error);
          continue;
        }

      /* At this point, property_value has been set, and we need to either
       * copy it to one of the two arrays, or unset it.
       */

      g_assert (G_IS_VALUE (&property_value));
      object_properties_add (params, property_name, &property_value);
      prop->applied = TRUE;
    }
}

static const char *
object_get_id (GObject *object)
{
  if (GTK_IS_BUILDABLE (object))
    return gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
  else
    return g_object_get_data (object, "gtk-builder-id");
}

static GObject *
gtk_builder_get_internal_child (GtkBuilder   *builder,
                                ObjectInfo   *info,
                                const char   *childname,
                                GError      **error)
{
  GObject *obj = NULL;

  while (!obj)
    {
      if (!info->parent)
        break;

      info = (ObjectInfo*)((ChildInfo*)info->parent)->parent;
      if (!info)
        break;

      GTK_DEBUG (BUILDER_TRACE, "Trying to get internal child %s from %s",
                                childname, object_get_id (info->object));

      if (GTK_IS_BUILDABLE (info->object))
          obj = gtk_buildable_get_internal_child (GTK_BUILDABLE (info->object),
                                                  builder,
                                                  childname);
    };

  if (!obj)
    {
      g_set_error (error,
                   GTK_BUILDER_ERROR,
                   GTK_BUILDER_ERROR_INVALID_VALUE,
                   "Unknown internal child: %s", childname);
    }
  return obj;
}

static inline void
object_set_id (GObject     *object,
                 const char *id)
{
  if (GTK_IS_BUILDABLE (object))
    gtk_buildable_set_buildable_id (GTK_BUILDABLE (object), id);
  else
    g_object_set_data_full (object, "gtk-builder-id", g_strdup (id), g_free);
}

void
_gtk_builder_add_object (GtkBuilder  *builder,
                         const char *id,
                         GObject     *object)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  object_set_id (object, id);
  g_hash_table_insert (priv->objects, g_strdup (id), g_object_ref (object));
}

void
gtk_builder_take_bindings (GtkBuilder *builder,
                           GObject    *target,
                           GSList     *bindings)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GSList *l;

  for (l = bindings; l; l = l->next)
    {
      CommonInfo *common_info = l->data;

      if (common_info->tag_type == TAG_BINDING)
        {
          BindingInfo *info = l->data;
          info->target = target;
        }
      else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
        {
          BindingExpressionInfo *info = l->data;
          info->target = target;
        }
      else
        {
          g_assert_not_reached ();
        }
    }

  priv->bindings = g_slist_concat (priv->bindings, bindings);
}

static void
ensure_special_construct_parameters (GtkBuilder       *builder,
                                     GType             object_type,
                                     ObjectProperties *construct_parameters)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GValue value = G_VALUE_INIT;

  if (g_type_is_a (object_type, GTK_TYPE_BUILDER_LIST_ITEM_FACTORY))
    {
      g_value_init (&value, GTK_TYPE_BUILDER_SCOPE);
      g_value_set_object (&value, priv->scope);
      object_properties_add (construct_parameters, "scope", &value);
    }
}

GObject *
_gtk_builder_construct (GtkBuilder  *builder,
                        ObjectInfo  *info,
                        GError     **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  ObjectProperties parameters, construct_parameters;
  GObject *obj;
  int i;
  GParamFlags param_filter_flags;

  g_assert (info->type != G_TYPE_INVALID);

  if (priv->template_type != 0 &&
      g_type_is_a (info->type, priv->template_type))
    {
      g_set_error (error,
                   GTK_BUILDER_ERROR,
                   GTK_BUILDER_ERROR_OBJECT_TYPE_REFUSED,
                   "Refused to build object of type '%s' because it "
                   "conforms to the template type '%s', avoiding infinite recursion.",
                   g_type_name (info->type), g_type_name (priv->template_type));
      return NULL;
    }

  /* If there is a manual constructor (like UIManager), or if this is a
   * reference to an internal child, then we filter out construct-only
   * and warn that they cannot be set.
   *
   * Otherwise if we are calling g_object_new_with_properties(), we want
   * to pass both G_PARAM_CONSTRUCT and G_PARAM_CONSTRUCT_ONLY to the
   * object's constructor.
   *
   * Passing all construct properties to g_object_new_with_properties()
   * slightly improves performance as the construct properties will only
   * be set once.
   */

  if (info->constructor ||
      (info->parent &&
       info->parent->tag_type == TAG_CHILD &&
       ((ChildInfo*)info->parent)->internal_child != NULL))
    param_filter_flags = G_PARAM_CONSTRUCT_ONLY;
  else
    param_filter_flags = G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY;

  object_properties_init (¶meters);
  object_properties_init (&construct_parameters);

  gtk_builder_get_parameters (builder, info->type,
                              info->id,
                              info->properties,
                              param_filter_flags,
                              ¶meters,
                              &construct_parameters);

  if (info->constructor)
    {
      GObject *constructor;

      constructor = g_hash_table_lookup (priv->objects, info->constructor);
      if (constructor == NULL)
        {
          g_set_error (error,
                       GTK_BUILDER_ERROR,
                       GTK_BUILDER_ERROR_INVALID_VALUE,
                       "Unknown object constructor for %s: %s",
                       info->id,
                       info->constructor);
          object_properties_destroy (¶meters);
          object_properties_destroy (&construct_parameters);
          return NULL;
        }
      obj = gtk_buildable_construct_child (GTK_BUILDABLE (constructor),
                                           builder,
                                           info->id);
      g_assert (obj != NULL);
      if (construct_parameters.names->len > 0)
        g_warning ("Can't pass in construct-only parameters to %s", info->id);
    }
  else if (info->parent &&
           info->parent->tag_type == TAG_CHILD &&
           ((ChildInfo*)info->parent)->internal_child != NULL)
    {
      char *childname = ((ChildInfo*)info->parent)->internal_child;
      obj = gtk_builder_get_internal_child (builder, info, childname, error);
      if (!obj)
        {
          object_properties_destroy (¶meters);
          object_properties_destroy (&construct_parameters);
          return NULL;
        }
      if (construct_parameters.names)
        g_warning ("Can't pass in construct-only parameters to %s", childname);
      g_object_ref (obj);
    }
  else
    {
      ensure_special_construct_parameters (builder, info->type, &construct_parameters);

      if (construct_parameters.names)
        obj = g_object_new_with_properties (info->type,
                                            construct_parameters.names->len,
                                            (const char **) construct_parameters.names->pdata,
                                            (GValue *) construct_parameters.values->data);
      else
        obj = g_object_new (info->type, NULL);

      /* 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_DEBUG (BUILDER_TRACE, "created %s of type %s", info->id, g_type_name (info->type));
    }
  object_properties_destroy (&construct_parameters);

  if (parameters.names)
    {
      GtkBuildableIface *iface = NULL;
      gboolean custom_set_property = FALSE;
      GtkBuildable *buildable = NULL;

      if (GTK_IS_BUILDABLE (obj))
        {
          buildable = GTK_BUILDABLE (obj);
          iface = GTK_BUILDABLE_GET_IFACE (obj);
          if (iface->set_buildable_property)
            custom_set_property = TRUE;
        }

      if (custom_set_property)
        {
          for (i = 0; i < parameters.names->len; i++)
            {
              const char *name = object_properties_get_name (¶meters, i);
              const GValue *value = object_properties_get_value (¶meters, i);

              iface->set_buildable_property (buildable, builder, name, value);
              if (GTK_DEBUG_CHECK (BUILDER_TRACE))
                {
                  char *str = g_strdup_value_contents (value);
                  g_message ("set %s: %s = %s", info->id, name, str);
                  g_free (str);
                }
            }
        }
      else
        {
          g_object_setv (obj,
                         parameters.names->len,
                         (const char **) parameters.names->pdata,
                         (GValue *) parameters.values->data);
          if (GTK_DEBUG_CHECK (BUILDER_TRACE))
            {
              for (i = 0; i < parameters.names->len; i++)
                {
                  const char *name = object_properties_get_name (¶meters, i);
                  const GValue *value = object_properties_get_value (¶meters, i);
                  char *str = g_strdup_value_contents (value);
                  g_message ("set %s: %s = %s", info->id, name, str);
                  g_free (str);
                }
            }
        }
    }

  object_properties_destroy (¶meters);

  /* 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_apply_properties (GtkBuilder  *builder,
                               ObjectInfo  *info,
                               GError     **error)
{
  ObjectProperties parameters;

  g_assert (info->object != NULL);
  g_assert (info->type != G_TYPE_INVALID);

  object_properties_init (¶meters);

  /* Fetch all properties that are not construct-only */
  gtk_builder_get_parameters (builder, info->type,
                              info->id,
                              info->properties,
                              G_PARAM_CONSTRUCT_ONLY,
                              ¶meters, NULL);

  if (parameters.names)
    {
      GtkBuildableIface *iface = NULL;
      GtkBuildable *buildable = NULL;
      gboolean custom_set_property = FALSE;
      int i;

      if (GTK_IS_BUILDABLE (info->object))
        {
          buildable = GTK_BUILDABLE (info->object);
          iface = GTK_BUILDABLE_GET_IFACE (info->object);
          if (iface->set_buildable_property)
            custom_set_property = TRUE;
        }

      if (custom_set_property)
        {
          for (i = 0; i < parameters.names->len; i++)
            {
              const char *name = object_properties_get_name (¶meters, i);
              const GValue *value = object_properties_get_value (¶meters, i);
              iface->set_buildable_property (buildable, builder, name, value);
              if (GTK_DEBUG_CHECK (BUILDER_TRACE))
                {
                  char *str = g_strdup_value_contents (value);
                  g_message ("set %s: %s = %s", info->id, name, str);
                  g_free (str);
                }
            }
        }
      else
        {
          g_object_setv (info->object,
                         parameters.names->len,
                         (const char **) parameters.names->pdata,
                         (GValue *) parameters.values->data);
          if (GTK_DEBUG_CHECK (BUILDER_TRACE))
            {
              for (i = 0; i < parameters.names->len; i++)
                {
                  const char *name = object_properties_get_name (¶meters, i);
                  const GValue *value = object_properties_get_value (¶meters, i);
                  char *str = g_strdup_value_contents (value);
                  g_message ("set %s: %s = %s", info->id, name, str);
                  g_free (str);
                }
            }
        }
    }

  object_properties_destroy (¶meters);
}

void
_gtk_builder_add (GtkBuilder *builder,
                  ChildInfo  *child_info)
{
  GObject *object;
  GObject *parent;

  /* Internal children are already added
   * Also prevent us from being called twice.
   */

  if (!child_info ||
      child_info->internal_child ||
      child_info->added)
    return;

  object = child_info->object;
  if (!object)
    return;

  if (!child_info->parent)
    {
      g_warning ("%s: Not adding, No parent", object_get_id (object));
      return;
    }

  g_assert (object != NULL);

  parent = ((ObjectInfo*)child_info->parent)->object;

  GTK_DEBUG (BUILDER_TRACE, "adding %s to %s", object_get_id (object), object_get_id (parent));

  if (G_IS_LIST_STORE (parent))
    {
      if (child_info->type != NULL)
        {
          GTK_BUILDER_WARN_INVALID_CHILD_TYPE (parent, child_info->type);
        }
      else
        {
          g_list_store_append (G_LIST_STORE (parent), object);
        }
    }
  else
    {
      g_assert (GTK_IS_BUILDABLE (parent));
      gtk_buildable_add_child (GTK_BUILDABLE (parent), builder, object,
                               child_info->type);
    }

  child_info->added = TRUE;
}

void
_gtk_builder_add_signals (GtkBuilder *builder,
                          GPtrArray  *signals)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  if (G_UNLIKELY (!priv->signals))
    priv->signals = g_ptr_array_new_with_free_func ((GDestroyNotify)_free_signal_info);

  g_ptr_array_extend_and_steal (priv->signals, signals);
}

static gboolean
gtk_builder_apply_delayed_properties (GtkBuilder  *builder,
                                      GError     **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GSList *l, *props;
  gboolean result = TRUE;

  /* 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 (priv->delayed_properties);
  priv->delayed_properties = NULL;

  for (l = props; l; l = l->next)
    {
      DelayedProperty *property = l->data;
      GObject *object, *obj;

      if (result)
        {
          object = g_hash_table_lookup (priv->objects, property->object);
          g_assert (object != NULL);

          obj = gtk_builder_lookup_object (builder, property->value, property->line, property->col, error);
          if (obj)
            g_object_set (object, property->pspec->name, obj, NULL);
          else
            result = FALSE;
        }

      g_free (property->value);
      g_free (property->object);
      g_free (property);
    }
  g_slist_free (props);

  return result;
}

static inline gboolean
gtk_builder_create_bindings (GtkBuilder  *builder,
                             GError     **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GSList *l;
  gboolean result = TRUE;

  for (l = priv->bindings; l; l = l->next)
    {
      CommonInfo *common_info = l->data;

      if (common_info->tag_type == TAG_BINDING)
        {
          BindingInfo *info = l->data;
          GObject *source;

          source = gtk_builder_lookup_object (builder, info->source, info->line, info->col, error);
          if (source)
            g_object_bind_property (source, info->source_property,
                                    info->target, info->target_pspec->name,
                                    info->flags);
          else
            error = NULL;

          _free_binding_info (info, NULL);
        }
      else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
        {
          BindingExpressionInfo *info = l->data;
          GtkExpression *expression;
          GObject *object;

          if (info->object_name)
            {
              object = gtk_builder_lookup_object (builder, info->object_name, info->line, info->col, error);
              if (object == NULL)
                {
                  error = NULL;
                  result = FALSE;
                }
            }
          else if (priv->current_object)
            {
              object = priv->current_object;
            }
          else
            {
              object = info->target;
            }

          if (object)
            {
              expression = expression_info_construct (builder, priv->domain, info->expr, error);
              if (expression == NULL)
                {
                  g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col);
                  error = NULL;
                  result = FALSE;
                }
              else
                {
                  gtk_expression_bind (expression, info->target, info->target_pspec->name, object);
                }
            }

          free_binding_expression_info (info);
        }
      else
        g_assert_not_reached ();
    }

  g_clear_slist (&priv->bindings, NULL);
  return result;
}

/**
 * gtk_builder_new:
 *
 * Creates a new empty builder object.
 *
 * This function is only useful if you intend to make multiple calls
 * to [method@Gtk.Builder.add_from_file], [method@Gtk.Builder.add_from_resource]
 * or [method@Gtk.Builder.add_from_string] in order to merge multiple UI
 * descriptions into a single builder.
 *
 * Returns: a new (empty) `GtkBuilder` object
 */

GtkBuilder *
gtk_builder_new (void)
{
  return g_object_new (GTK_TYPE_BUILDER, NULL);
}

/**
 * gtk_builder_add_from_file:
 * @builder: a `GtkBuilder`
 * @filename: (type filename): the name of the file to parse
 * @error: (nullable): return location for an error
 *
 * Parses a file containing a UI definition and merges it with
 * the current contents of @builder.
 *
 * This function is useful if you need to call
 * [method@Gtk.Builder.set_current_object]) to add user data to
 * callbacks before loading GtkBuilder UI. Otherwise, you probably
 * want [ctor@Gtk.Builder.new_from_file] instead.
 *
 * If an error occurs, 0 will be returned and @error will be assigned a
 * `GError` from the `GTK_BUILDER_ERROR`, `G_MARKUP_ERROR` or `G_FILE_ERROR`
 * domains.
 *
 * Its not really reasonable to attempt to handle failures of this
 * call. You should not use this function with untrusted files (ie:
 * files that are not part of your application). Broken `GtkBuilder`
 * files can easily crash your program, and its possible that memory
 * was leaked leading up to the reported failure. The only reasonable
 * thing to do when an error is detected is to call `g_error()`.
 *
 * Returns: %TRUE on success, %FALSE if an error occurred
 */

gboolean
gtk_builder_add_from_file (GtkBuilder   *builder,
                           const char   *filename,
                           GError      **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  char *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, 0);

  tmp_error = NULL;

  if (!g_file_get_contents (filename, &buffer, &length, &tmp_error))
    {
      g_propagate_error (error, tmp_error);
      return FALSE;
    }

  g_free (priv->filename);
  g_free (priv->resource_prefix);
  priv->filename = g_strdup (filename);
  priv->resource_prefix = NULL;

  _gtk_builder_parser_parse_buffer (builder, filename,
                                    buffer, (gssize)length,
                                    NULL,
                                    &tmp_error);

  g_free (buffer);

  if (tmp_error != NULL)
    {
      g_propagate_error (error, tmp_error);
      return FALSE;
    }

  return TRUE;
}

/**
 * gtk_builder_add_objects_from_file:
 * @builder: a `GtkBuilder`
 * @filename: (type filename): the name of the file to parse
 * @object_ids: (array zero-terminated=1) (element-type utf8): nul-terminated array of objects to build
 * @error: (nullable): return location for an error
 *
 * Parses a file containing a 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
 * `GtkTreeModel`), you have to explicitly list all of them in @object_ids.
 *
 * Returns: %TRUE on success, %FALSE if an error occurred
 */

gboolean
gtk_builder_add_objects_from_file (GtkBuilder   *builder,
                                   const char   *filename,
                                   const char  **object_ids,
                                   GError      **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  char *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 (object_ids != NULL && object_ids[0] != NULL, 0);
  g_return_val_if_fail (error == NULL || *error == NULL, 0);

  tmp_error = NULL;

  if (!g_file_get_contents (filename, &buffer, &length, &tmp_error))
    {
      g_propagate_error (error, tmp_error);
      return 0;
    }

  g_free (priv->filename);
  g_free (priv->resource_prefix);
  priv->filename = g_strdup (filename);
  priv->resource_prefix = NULL;

  _gtk_builder_parser_parse_buffer (builder, filename,
                                    buffer, (gssize)length,
                                    object_ids,
                                    &tmp_error);

  g_free (buffer);

  if (tmp_error != NULL)
    {
      g_propagate_error (error, tmp_error);
      return FALSE;
    }

  return TRUE;
}

void
gtk_builder_set_allow_template_parents (GtkBuilder *builder,
                                        gboolean    allow_parents)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  priv->allow_template_parents = allow_parents;
}

/**
 * gtk_builder_extend_with_template:
 * @builder: a `GtkBuilder`
 * @object: the object that is being extended
 * @template_type: the type that the template is for
 * @buffer: the string to parse
 * @length: the length of @buffer (may be -1 if @buffer is nul-terminated)
 * @error: (nullable): return location for an error
 *
 * Main private entry point for building composite components
 * from template XML.
 *
 * Most likely you do not need to call this function in applications as
 * templates are handled by `GtkWidget`.
 *
 * Returns: A positive value on success, 0 if an error occurred
 */

gboolean
gtk_builder_extend_with_template (GtkBuilder   *builder,
                                  GObject      *object,
                                  GType         template_type,
                                  const char   *buffer,
                                  gssize        length,
                                  GError      **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  const char *name;
  GError *tmp_error;
  char *filename;

  g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
  g_return_val_if_fail (G_IS_OBJECT (object), 0);
  g_return_val_if_fail (g_type_name (template_type) != NULL, 0);
  g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (object), template_type), 0);
  g_return_val_if_fail (buffer && buffer[0], 0);

  tmp_error = NULL;

  g_free (priv->filename);
  g_free (priv->resource_prefix);
  priv->filename = g_strdup (".");
  priv->resource_prefix = NULL;
  priv->template_type = template_type;

  /* We specifically allow this function to be called multiple times with
   * the same @template_type as that is used in applications like Builder
   * to implement UI merging.
   */

  name = g_type_name (template_type);
  if (gtk_builder_get_object (builder, name) != object)
    gtk_builder_expose_object (builder, name, object);
  if (priv->allow_template_parents)
    {
      GType subtype;
      for (subtype = g_type_parent (template_type);
           subtype != G_TYPE_OBJECT;
           subtype = g_type_parent (subtype))
        {
          name = g_type_name (subtype);
          if (gtk_builder_get_object (builder, name) != object)
            gtk_builder_expose_object (builder, name, object);
        }
    }

  filename = g_strconcat ("<", name, " template>", NULL);
  _gtk_builder_parser_parse_buffer (builder, filename,
                                    buffer, length,
                                    NULL,
                                    &tmp_error);
  g_free (filename);

  if (tmp_error != NULL)
    {
      g_propagate_error (error, tmp_error);
      return FALSE;
    }

  return TRUE;
}

/**
 * gtk_builder_add_from_resource:
 * @builder: a `GtkBuilder`
 * @resource_path: the path of the resource file to parse
 * @error: (nullable): return location for an error
 *
 * Parses a resource file containing a UI definition
 * and merges it with the current contents of @builder.
 *
 * This function is useful if you need to call
 * [method@Gtk.Builder.set_current_object] to add user data to
 * callbacks before loading GtkBuilder UI. Otherwise, you probably
 * want [ctor@Gtk.Builder.new_from_resource] instead.
 *
 * If an error occurs, 0 will be returned and @error will be assigned a
 * `GError` from the %GTK_BUILDER_ERROR, %G_MARKUP_ERROR or %G_RESOURCE_ERROR
 * domain.
 *
 * Its not really reasonable to attempt to handle failures of this
 * call.  The only reasonable thing to do when an error is detected is
 * to call g_error().
 *
 * Returns: %TRUE on success, %FALSE if an error occurred
 */

gboolean
gtk_builder_add_from_resource (GtkBuilder   *builder,
                               const char   *resource_path,
                               GError      **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GError *tmp_error;
  GBytes *data;
  char *filename_for_errors;
  const 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 (priv->filename);
  g_free (priv->resource_prefix);
  priv->filename = g_strdup (".");

  slash = strrchr (resource_path, '/');
  if (slash != NULL)
    priv->resource_prefix = g_strndup (resource_path, slash - resource_path + 1);
  else
    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 FALSE;
    }

  return TRUE;
}

/**
 * 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: (nullable): return location for an error
 *
 * Parses a resource file containing a 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 explicitly list all of them in @object_ids.
 *
 * Returns: %TRUE on success, %FALSE if an error occurred
 */

gboolean
gtk_builder_add_objects_from_resource (GtkBuilder   *builder,
                                       const char   *resource_path,
                                       const char  **object_ids,
                                       GError      **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GError *tmp_error;
  GBytes *data;
  char *filename_for_errors;
  const 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 FALSE;
    }

  g_free (priv->filename);
  g_free (priv->resource_prefix);
  priv->filename = g_strdup (".");

  slash = strrchr (resource_path, '/');
  if (slash != NULL)
    priv->resource_prefix = g_strndup (resource_path, slash - resource_path + 1);
  else
    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 FALSE;
    }

  return TRUE;
}

/**
 * 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: (nullable): return location for an error
 *
 * Parses a string containing a UI definition and merges it
 * with the current contents of @builder.
 *
 * This function is useful if you need to call
 * [method@Gtk.Builder.set_current_object] to add user data to
 * callbacks before loading `GtkBuilder` UI. Otherwise, you probably
 * want [ctor@Gtk.Builder.new_from_string] instead.
 *
 * Upon errors %FALSE will be returned and @error will be assigned a
 * `GError` from the %GTK_BUILDER_ERROR, %G_MARKUP_ERROR or
 * %G_VARIANT_PARSE_ERROR domain.
 *
 * Its not really reasonable to attempt to handle failures of this
 * call.  The only reasonable thing to do when an error is detected is
 * to call g_error().
 *
 * Returns: %TRUE on success, %FALSE if an error occurred
 */

gboolean
gtk_builder_add_from_string (GtkBuilder   *builder,
                             const char   *buffer,
                             gssize        length,
                             GError      **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GError *tmp_error;

  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, 0);

  tmp_error = NULL;

  g_free (priv->filename);
  g_free (priv->resource_prefix);
  priv->filename = g_strdup (".");
  priv->resource_prefix = NULL;

  _gtk_builder_parser_parse_buffer (builder, "<input>",
                                    buffer, length,
                                    NULL,
                                    &tmp_error);
  if (tmp_error != NULL)
    {
      g_propagate_error (error, tmp_error);
      return FALSE;
    }

  return TRUE;
}

/**
 * gtk_builder_add_objects_from_string:
 * @builder: a `GtkBuilder`
 * @buffer: the string to parse
 * @length: the length of @buffer (may be -1 if @buffer is nul-terminated)
 * @object_ids: (array zero-terminated=1) (element-type utf8): nul-terminated array of objects to build
 * @error: (nullable): return location for an error
 *
 * Parses a string containing a UI definition, building only the
 * requested objects and merges them with the current contents of
 * @builder.
 *
 * Upon errors %FALSE 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
 * its child (for instance a `GtkTreeView` that depends on its
 * `GtkTreeModel`), you have to explicitly list all of them in @object_ids.
 *
 * Returns: %TRUE on success, %FALSE if an error occurred
 */

gboolean
gtk_builder_add_objects_from_string (GtkBuilder   *builder,
                                     const char   *buffer,
                                     gssize        length,
                                     const char  **object_ids,
                                     GError      **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GError *tmp_error;

  g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
  g_return_val_if_fail (buffer != 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;

  g_free (priv->filename);
  g_free (priv->resource_prefix);
  priv->filename = g_strdup (".");
  priv->resource_prefix = NULL;

  _gtk_builder_parser_parse_buffer (builder, "<input>",
                                    buffer, length,
                                    object_ids,
                                    &tmp_error);

  if (tmp_error != NULL)
    {
      g_propagate_error (error, tmp_error);
      return FALSE;
    }

  return TRUE;
}

/**
 * gtk_builder_get_object:
 * @builder: a `GtkBuilder`
 * @name: name of object to get
 *
 * Gets the object named @name.
 *
 * Note that this function does not increment the reference count
 * of the returned object.
 *
 * Returns: (nullable) (transfer none): the object named @name
 */

GObject *
gtk_builder_get_object (GtkBuilder  *builder,
                        const char *name)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
  g_return_val_if_fail (name != NULL, NULL);

  return g_hash_table_lookup (priv->objects, name);
}

/**
 * gtk_builder_get_objects:
 * @builder: a `GtkBuilder`
 *
 * Gets all objects that have been constructed by @builder.
 *
 * Note that this function does not increment the reference
 * counts of the returned objects.
 *
 * Returns: (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()
 */

GSList *
gtk_builder_get_objects (GtkBuilder *builder)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GSList *objects = NULL;
  GObject *object;
  GHashTableIter iter;

  g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);

  g_hash_table_iter_init (&iter, priv->objects);
  while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&object))
    objects = g_slist_prepend (objects, object);

  return g_slist_reverse (objects);
}

/**
 * gtk_builder_set_translation_domain:
 * @builder: a `GtkBuilder`
 * @domain: (nullable): the translation domain
 *
 * Sets the translation domain of @builder.
 */

void
gtk_builder_set_translation_domain (GtkBuilder  *builder,
                                    const char *domain)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  char *new_domain;

  g_return_if_fail (GTK_IS_BUILDER (builder));

  new_domain = g_strdup (domain);
  g_free (priv->domain);
  priv->domain = new_domain;

  g_object_notify_by_pspec (G_OBJECT (builder), builder_props[PROP_TRANSLATION_DOMAIN]);
}

/**
 * gtk_builder_get_translation_domain:
 * @builder: a `GtkBuilder`
 *
 * Gets the translation domain of @builder.
 *
 * Returns: (transfer none) (nullable): the translation domain
 */

const char *
gtk_builder_get_translation_domain (GtkBuilder *builder)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);

  return 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.
 *
 * Only a single object may be added using @name. However,
 * it is not an error to expose the same object under multiple
 * names. `gtk_builder_get_object()` may be used to determine
 * if an object has already been added with @name.
 */

void
gtk_builder_expose_object (GtkBuilder    *builder,
                           const char    *name,
                           GObject       *object)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  g_return_if_fail (GTK_IS_BUILDER (builder));
  g_return_if_fail (name && name[0]);
  g_return_if_fail (!g_hash_table_contains (priv->objects, name));

  object_set_id (object, name);
  g_hash_table_insert (priv->objects,
                       g_strdup (name),
                       g_object_ref (object));
}

/**
 * gtk_builder_get_current_object:
 * @builder: a `GtkBuilder`
 *
 * Gets the current object set via gtk_builder_set_current_object().
 *
 * Returns: (nullable) (transfer none): the current object
 */

GObject *
gtk_builder_get_current_object (GtkBuilder *builder)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);

  return priv->current_object;
}

/**
 * gtk_builder_set_current_object:
 * @builder: a `GtkBuilder`
 * @current_object: (nullable) (transfer none): the new current object
 *
 * Sets the current object for the @builder.
 *
 * The current object can be thought of as the `this` object that the
 * builder is working for and will often be used as the default object
 * when an object is optional.
 *
 * [method@Gtk.Widget.init_template] for example will set the current
 * object to the widget the template is inited for. For functions like
 * [ctor@Gtk.Builder.new_from_resource], the current object will be %NULL.
 */

void
gtk_builder_set_current_object (GtkBuilder *builder,
                                GObject    *current_object)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  g_return_if_fail (GTK_IS_BUILDER (builder));
  g_return_if_fail (current_object == NULL || G_IS_OBJECT (current_object));

  if (!g_set_object (&priv->current_object, current_object))
    return;

  g_object_notify_by_pspec (G_OBJECT (builder), builder_props[PROP_CURRENT_OBJECT]);
}

/**
 * gtk_builder_get_scope:
 * @builder: a `GtkBuilder`
 *
 * Gets the scope in use that was set via gtk_builder_set_scope().
 *
 * Returns: (transfer none): the current scope
 */

GtkBuilderScope *
gtk_builder_get_scope (GtkBuilder *builder)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);

  return priv->scope;
}

/**
 * gtk_builder_set_scope:
 * @builder: a `GtkBuilder`
 * @scope: (nullable) (transfer none): the scope to use
 *
 * Sets the scope the builder should operate in.
 *
 * If @scope is %NULL, a new [class@Gtk.BuilderCScope] will be created.
 */

void
gtk_builder_set_scope (GtkBuilder      *builder,
                       GtkBuilderScope *scope)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  g_return_if_fail (GTK_IS_BUILDER (builder));
  g_return_if_fail (scope == NULL || GTK_IS_BUILDER_SCOPE (scope));

  if (scope && priv->scope == scope)
    return;

  g_clear_object (&priv->scope);

  if (scope)
    priv->scope = g_object_ref (scope);
  else
    priv->scope = gtk_builder_cscope_new ();

  g_object_notify_by_pspec (G_OBJECT (builder), builder_props[PROP_SCOPE]);
}

static gboolean
gtk_builder_connect_signals (GtkBuilder  *builder,
                             GError     **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GObject *object;
  GObject *connect_object;
  gboolean result = TRUE;

  if (!priv->signals ||
      priv->signals->len == 0)
    return TRUE;

  for (guint i = 0; i < priv->signals->len; i++)
    {
      SignalInfo *signal = g_ptr_array_index (priv->signals, i);
      GClosure *closure;

      g_assert (signal != NULL);
      g_assert (signal->id != 0);

      object = g_hash_table_lookup (priv->objects, signal->object_name);
      g_assert (object != NULL);

      connect_object = NULL;

      if (signal->connect_object_name)
        {
          connect_object = g_hash_table_lookup (priv->objects,
                                                signal->connect_object_name);
          if (!connect_object)
            {
              g_set_error (error,
                           GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_ID,
                           "Could not lookup object %s on signal %s of object %s",
                           signal->connect_object_name, g_signal_name (signal->id),
                           signal->object_name);
              break;
            }
        }

      closure = gtk_builder_create_closure (builder,
                                            signal->handler,
                                            signal->flags & G_CONNECT_SWAPPED ? TRUE : FALSE,
                                            connect_object,
                                            error);

      if (closure == NULL)
        {
          result = false;
          break;
        }

      g_signal_connect_closure_by_id (object,
                                      signal->id,
                                      signal->detail,
                                      closure,
                                      signal->flags & G_CONNECT_AFTER ? TRUE : FALSE);
    }

  g_ptr_array_free (priv->signals, TRUE);
  priv->signals = NULL;

  return result;
}

gboolean
_gtk_builder_finish (GtkBuilder  *builder,
                     GError     **error)
{
  return gtk_builder_apply_delayed_properties (builder, error) &&
         gtk_builder_create_bindings (builder, error) &&
         gtk_builder_connect_signals (builder, error);
}

/**
 * gtk_builder_value_from_string:
 * @builder: a `GtkBuilder`
 * @pspec: the `GParamSpec` for the property
 * @string: the string representation of the value
 * @value: (out): the `GValue` to store the result in
 * @error: (nullable): return location for an error
 *
 * Demarshals a value from a string.
 *
 * This function calls g_value_init() on the @value argument,
 * so it need not be initialised beforehand.
 *
 * Can handle char, uchar, boolean, int, uint, long,
 * ulong, enum, flags, float, double, string, `GdkRGBA` and
 * `GtkAdjustment` type values.
 *
 * Upon errors %FALSE will be returned and @error will be
 * assigned a `GError` from the %GTK_BUILDER_ERROR domain.
 *
 * Returns: %TRUE on success
 */

gboolean
gtk_builder_value_from_string (GtkBuilder   *builder,
                               GParamSpec   *pspec,
                               const char   *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
   */

  if (G_IS_PARAM_SPEC_UNICHAR (pspec))
    {
      gunichar c;
      g_value_init (value, G_TYPE_UINT);
      c = g_utf8_get_char_validated (string, -1);
      if (c != 0 && c != (gunichar)-1 && c != (gunichar)-2)
        g_value_set_uint (value, c);
      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);
}

gboolean
_gtk_builder_boolean_from_string (const char   *string,
                                  gboolean     *value,
                                  GError      **error)
{
  if (string[0] == '\0')
    goto error;
  else if (string[1] == '\0')
    {
      char c;

      c = string[0];
      if (c == '1' ||
          c == 'y' || c == 't' ||
          c == 'Y' || c == 'T')
        *value = TRUE;
      else if (c == '0' ||
               c == 'n' || c == 'f' ||
               c == 'N' || c == 'F')
        *value = FALSE;
      else
        goto error;
    }
  else
    {
      if (g_ascii_strcasecmp (string, "true") == 0 ||
          g_ascii_strcasecmp (string, "yes") == 0)
        *value = TRUE;
      else if (g_ascii_strcasecmp (string, "false") == 0 ||
               g_ascii_strcasecmp (string, "no") == 0)
        *value = FALSE;
      else
        goto error;
    }

  return TRUE;

error:
  g_set_error (error,
               GTK_BUILDER_ERROR,
               GTK_BUILDER_ERROR_INVALID_VALUE,
               "Could not parse boolean '%s'",
               string);
  return FALSE;
}

gboolean
gtk_builder_parse_translatable (const char  *string,
                                gboolean    *value,
                                GError     **error)
{
  if (!_gtk_builder_boolean_from_string (string, value, error))
    return FALSE;

  if (*value && strcmp (string, "yes") != 0)
    GTK_DEBUG (BUILDER, "Useless translatable attribute: '%s' (xgettext only recognizes 'yes')", string);

  return TRUE;
}

/**
 * gtk_builder_value_from_string_type:
 * @builder: a `GtkBuilder`
 * @type: the `GType` of the value
 * @string: the string representation of the value
 * @value: (out): the `GValue` to store the result in
 * @error: (nullable): return location for an error
 *
 * Demarshals a value from a string.
 *
 * Unlike [method@Gtk.Builder.value_from_string], this function
 * takes a `GType` instead of `GParamSpec`.
 *
 * 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
 */

gboolean
gtk_builder_value_from_string_type (GtkBuilder   *builder,
                                    GType         type,
                                    const char   *string,
                                    GValue       *value,
                                    GError      **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  gboolean ret = TRUE;

  g_return_val_if_fail (string != NULL, FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  g_value_init (value, type);

  switch (G_TYPE_FUNDAMENTAL (type))
    {
    case G_TYPE_CHAR:
      g_value_set_schar (value, string[0]);
      break;
    case G_TYPE_UCHAR:
      g_value_set_uchar (value, (guchar)string[0]);
      break;
    case G_TYPE_BOOLEAN:
      {
        gboolean b;

        if (!_gtk_builder_boolean_from_string (string, &b, error))
          {
            ret = FALSE;
            break;
          }
        g_value_set_boolean (value, b);
        break;
      }
    case G_TYPE_INT:
    case G_TYPE_LONG:
    case G_TYPE_INT64:
      {
        gint64 l;
        char *endptr = NULL;

        errno = 0;
        l = g_ascii_strtoll (string, &endptr, 0);
        if (errno || endptr == string)
          {
            g_set_error (error,
                         GTK_BUILDER_ERROR,
                         GTK_BUILDER_ERROR_INVALID_VALUE,
                         "Could not parse integer '%s'",
                         string);
            ret = FALSE;
            break;
          }
        if (G_VALUE_HOLDS_INT (value))
          g_value_set_int (value, l);
        else if (G_VALUE_HOLDS_LONG (value))
          g_value_set_long (value, l);
        else
          g_value_set_int64 (value, l);
        break;
      }
    case G_TYPE_UINT:
    case G_TYPE_ULONG:
    case G_TYPE_UINT64:
      {
        guint64 ul;
        char *endptr = NULL;
        errno = 0;
        ul = g_ascii_strtoull (string, &endptr, 0);
        if (errno || endptr == string)
          {
            g_set_error (error,
                         GTK_BUILDER_ERROR,
                         GTK_BUILDER_ERROR_INVALID_VALUE,
                         "Could not parse unsigned integer '%s'",
                         string);
            ret = FALSE;
            break;
          }
        if (G_VALUE_HOLDS_UINT (value))
          g_value_set_uint (value, ul);
        else if (G_VALUE_HOLDS_ULONG (value))
          g_value_set_ulong (value, ul);
        else
          g_value_set_uint64 (value, ul);
        break;
      }
    case G_TYPE_ENUM:
      {
        int 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:
      {
        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;
        char *endptr = NULL;
        errno = 0;
        d = g_ascii_strtod (string, &endptr);
        if (errno || endptr == string)
          {
            g_set_error (error,
                         GTK_BUILDER_ERROR,
                         GTK_BUILDER_ERROR_INVALID_VALUE,
                         "Could not parse double '%s'",
                         string);
            ret = FALSE;
            break;
          }
        if (G_VALUE_HOLDS_FLOAT (value))
          g_value_set_float (value, d);
        else
          g_value_set_double (value, d);
        break;
      }
    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_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, GDK_TYPE_CONTENT_FORMATS))
        {
          GdkContentFormats *formats;

          formats = gdk_content_formats_parse (string);
          if (formats)
            g_value_take_boxed (value, formats);
          else
            {
              g_set_error (error,
                           GTK_BUILDER_ERROR,
                           GTK_BUILDER_ERROR_INVALID_VALUE,
                           "Could not parse GdkContentFormats '%s'",
                           string);
              ret = FALSE;
            }
        }
      else if (G_VALUE_HOLDS (value, GSK_TYPE_TRANSFORM))
        {
          GskTransform *transform;

          if (gsk_transform_parse (string, &transform))
            g_value_take_boxed (value, transform);
          else
            {
              g_set_error (error,
                           GTK_BUILDER_ERROR,
                           GTK_BUILDER_ERROR_INVALID_VALUE,
                           "Could not parse transform '%s'",
                           string);
              ret = FALSE;
            }
        }
      else if (G_VALUE_HOLDS (value, G_TYPE_STRV))
        {
          char **vector = g_strsplit (string, "\n"0);
          g_value_take_boxed (value, vector);
        }
      else if (G_VALUE_HOLDS (value, G_TYPE_BYTES))
        {
          g_value_take_boxed (value, g_bytes_new (string, strlen (string)));
        }
      else if (G_VALUE_HOLDS (value, PANGO_TYPE_ATTR_LIST))
        {
          PangoAttrList *attrs;

          attrs = pango_attr_list_from_string (string);
          if (attrs)
            {
              g_value_take_boxed (value, attrs);
            }
          else
            {
              g_set_error (error,
                           GTK_BUILDER_ERROR,
                           GTK_BUILDER_ERROR_INVALID_VALUE,
                           "Could not parse PangoAttrList '%s'",
                           string);
              ret = FALSE;
            }
        }
      else if (G_VALUE_HOLDS (value, PANGO_TYPE_TAB_ARRAY))
        {
          PangoTabArray *tabs;

          tabs = pango_tab_array_from_string (string);
          if (tabs)
            {
              g_value_take_boxed (value, tabs);
            }
          else
            {
              g_set_error (error,
                           GTK_BUILDER_ERROR,
                           GTK_BUILDER_ERROR_INVALID_VALUE,
                           "Could not parse PangoTabArray '%s'",
                           string);
              ret = FALSE;
            }
        }
      else if (G_VALUE_HOLDS (value, PANGO_TYPE_FONT_DESCRIPTION))
        {
          PangoFontDescription *desc;

          desc = pango_font_description_from_string (string);
          if (desc)
            {
              g_value_take_boxed (value, desc);
            }
          else
            {
              g_set_error (error,
                           GTK_BUILDER_ERROR,
                           GTK_BUILDER_ERROR_INVALID_VALUE,
                           "Could not parse PangoFontDescription '%s'",
                           string);
              ret = FALSE;
            }
        }
      else if (G_VALUE_HOLDS (value, G_TYPE_DATE_TIME))
        {
          GDateTime *date_time;

          date_time = g_date_time_new_from_iso8601 (string, NULL);

          if (date_time)
            g_value_take_boxed (value, date_time);
          else
            {
              g_set_error (error,
                           GTK_BUILDER_ERROR,
                           GTK_BUILDER_ERROR_INVALID_VALUE,
                           "Could not parse GDateTime '%s'",
                           string);
              ret = FALSE;
            }
        }
      else
        {
          g_set_error (error,
                       GTK_BUILDER_ERROR,
                       GTK_BUILDER_ERROR_INVALID_VALUE,
                       "Could not parse '%s' as a %s",
                       string, G_VALUE_TYPE_NAME (value));
          ret = FALSE;
        }
      break;
    case G_TYPE_OBJECT:
    case G_TYPE_INTERFACE:
      if (G_VALUE_HOLDS (value, GDK_TYPE_PAINTABLE) ||
          G_VALUE_HOLDS (value, GDK_TYPE_TEXTURE))
        {
          GObject *object = g_hash_table_lookup (priv->objects, string);
          char *filename;
          GError *tmp_error = NULL;
          GdkTexture *texture = NULL;

          if (object)
            {
              if (g_type_is_a (G_OBJECT_TYPE (object), G_VALUE_TYPE (value)))
                {
                  g_value_set_object (value, object);
                  return TRUE;
                }
              else
                {
                  g_set_error (error,
                               GTK_BUILDER_ERROR,
                               GTK_BUILDER_ERROR_INVALID_VALUE,
                               "Could not load image '%s': "
                               " '%s' is already used as object id for a %s",
                               string, string, G_OBJECT_TYPE_NAME (object));
                  return FALSE;
                }
            }

          filename = _gtk_builder_get_resource_path (builder, string);
          if (filename != NULL)
            {
              texture = gdk_texture_new_from_resource (filename);
            }
          else
            {
              GFile *file;

              filename = _gtk_builder_get_absolute_filename (builder, string);
              file = g_file_new_for_path (filename);
              texture = gdk_texture_new_from_file (file, &tmp_error);
              g_object_unref (file);
            }

          g_free (filename);

          if (!texture)
            {
              g_warning ("Could not load image '%s': %s", string, tmp_error->message);
              g_error_free (tmp_error);

              texture = gdk_texture_new_from_resource (IMAGE_MISSING_RESOURCE_PATH);
            }

          g_value_take_object (value, texture);

          ret = TRUE;
        }
      else if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
        {
          char *filename;
          GError *tmp_error = NULL;
          GdkPixbuf *pixbuf = NULL;
          GObject *object;

          object = g_hash_table_lookup (priv->objects, string);

          if (object)
            {
              if (g_type_is_a (G_OBJECT_TYPE (object), G_VALUE_TYPE (value)))
                {
                  g_value_set_object (value, object);
                  return TRUE;
                }
              else
                {
                  g_set_error (error,
                               GTK_BUILDER_ERROR,
                               GTK_BUILDER_ERROR_INVALID_VALUE,
                               "Could not load image '%s': "
                               " '%s' is already used as object id for a %s",
                               string, string, G_OBJECT_TYPE_NAME (object));
                  return FALSE;
                }
            }

          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)
            {
              g_warning ("Could not load image '%s': %s", string, tmp_error->message);
              g_error_free (tmp_error);

              pixbuf = gdk_pixbuf_new_from_resource (IMAGE_MISSING_RESOURCE_PATH, NULL);
            }

          g_value_take_object (value, pixbuf);

          g_free (filename);

          ret = TRUE;
        }
      else if (G_VALUE_HOLDS (value, G_TYPE_FILE))
        {
          GObject *object = g_hash_table_lookup (priv->objects, string);
          GFile *file;

          if (object)
            {
              if (g_type_is_a (G_OBJECT_TYPE (object), G_VALUE_TYPE (value)))
                {
                  g_value_set_object (value, object);
                  return TRUE;
                }
              else
                {
                  g_set_error (error,
                               GTK_BUILDER_ERROR,
                               GTK_BUILDER_ERROR_INVALID_VALUE,
                               "Could not create file '%s': "
                               " '%s' is already used as object id",
                               string, string);
                  return FALSE;
                }
            }

          if (!g_uri_is_valid (string, G_URI_FLAGS_NONE, NULL))
            {
              gchar *fullpath = _gtk_builder_get_absolute_filename (builder, string);
              file = g_file_new_for_path (fullpath);
              g_free (fullpath);
            }
          else if (g_str_has_prefix (string, "file://"))
            {
              gchar *path = g_uri_unescape_string (string + strlen ("file://"), "/");
              gchar *fullpath = _gtk_builder_get_absolute_filename (builder, path);

              file = g_file_new_for_path (fullpath);

              g_free (fullpath);
              g_free (path);
            }
          else
            {
              file = g_file_new_for_uri (string);
            }

          g_value_set_object (value, file);
          g_object_unref (G_OBJECT (file));

          ret = TRUE;
        }
      else if (G_VALUE_HOLDS (value, GTK_TYPE_SHORTCUT_TRIGGER))
        {
          GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (string);

          if (trigger)
            g_value_take_object (value, trigger);
          else
            {
              g_set_error (error,
                           GTK_BUILDER_ERROR,
                           GTK_BUILDER_ERROR_INVALID_VALUE,
                           "Could not parse shortcut trigger '%s'",
                           string);
              ret = FALSE;
            }
        }
      else if (G_VALUE_HOLDS (value, GTK_TYPE_SHORTCUT_ACTION))
        {
          GtkShortcutAction *action = gtk_shortcut_action_parse_builder (builder, string, error);

          if (action)
            g_value_take_object (value, action);
          else
            ret = FALSE;
        }
      else
        {
          GObject *object = g_hash_table_lookup (priv->objects, string);

          if (object && g_value_type_compatible (G_OBJECT_TYPE (object), type))
            {
              g_value_set_object (value, object);
            }
          else if (object)
            {
              g_set_error (error,
                           GTK_BUILDER_ERROR,
                           GTK_BUILDER_ERROR_INVALID_VALUE,
                           "Object named \"%s\" is of type \"%s\" which is not compatible with expected type \"%s\"",
                           string, G_OBJECT_TYPE_NAME (object), g_type_name (type));
              ret = FALSE;
            }
          else
            {
              g_set_error (error,
                           GTK_BUILDER_ERROR,
                           GTK_BUILDER_ERROR_INVALID_VALUE,
                           "No object named \"%s\"", string);
              ret = FALSE;
            }
        }
      break;
    case G_TYPE_POINTER:
      if (G_VALUE_HOLDS (value, G_TYPE_GTYPE))
        {
          GType resolved_type;

          resolved_type = gtk_builder_get_type_from_name (builder, string);
          if (resolved_type == G_TYPE_INVALID)
            {
              g_set_error (error,
                           GTK_BUILDER_ERROR,
                           GTK_BUILDER_ERROR_INVALID_VALUE,
                           "Unsupported GType '%s' for value of type 'GType'", string);
              return FALSE;
            }
          g_value_set_gtype (value, resolved_type);

          ret = TRUE;
        }
      else
        ret = FALSE;
      break;
    default:
      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;
}

gboolean
_gtk_builder_enum_from_string (GType         type,
                               const char   *string,
                               int          *enum_value,
                               GError      **error)
{
  GEnumClass *eclass;
  GEnumValue *ev;
  char *endptr;
  int value;
  gboolean ret;

  g_return_val_if_fail (G_TYPE_IS_ENUM (type), FALSE);
  g_return_val_if_fail (string != NULL, FALSE);

  ret = TRUE;

  endptr = NULL;
  errno = 0;
  value = g_ascii_strtoull (string, &endptr, 0);
  if (errno == 0 && endptr != string) /* parsed a number */
    *enum_value = value;
  else
    {
      eclass = g_type_class_ref (type);
      ev = g_enum_get_value_by_nick (eclass, string);
      if (!ev)
        ev = g_enum_get_value_by_name (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;
}

gboolean
_gtk_builder_flags_from_string (GType         type,
                                const char   *string,
                                guint        *flags_value,
                                GError      **error)
{
  GFlagsClass *fclass;
  char *endptr, *prevptr;
  guint i, j, value;
  char *flagstr;
  GFlagsValue *fv;
  const char *flag;
  gunichar ch;
  gboolean eos, ret;

  g_return_val_if_fail (G_TYPE_IS_FLAGS (type), FALSE);
  g_return_val_if_fail (string != 0FALSE);

  ret = TRUE;

  endptr = NULL;
  errno = 0;
  value = g_ascii_strtoull (string, &endptr, 0);
  if (errno == 0 && endptr != string) /* parsed a number */
    *flags_value = value;
  else
    {
      fclass = g_type_class_ref (type);

      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 = NULL;

              if (!fv)
                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);
    }

  return ret;
}

/**
 * gtk_builder_get_type_from_name:
 * @builder: a `GtkBuilder`
 * @type_name: type name to lookup
 *
 * Looks up a type by name.
 *
 * This is 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
 */

GType
gtk_builder_get_type_from_name (GtkBuilder  *builder,
                                const char *type_name)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GType type;

  g_return_val_if_fail (GTK_IS_BUILDER (builder), G_TYPE_INVALID);
  g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID);

  type = gtk_builder_scope_get_type_from_name (priv->scope, builder, type_name);
  if (type == G_TYPE_INVALID)
    return G_TYPE_INVALID;

  if (G_TYPE_IS_CLASSED (type))
    g_type_class_unref (g_type_class_ref (type));

  return type;
}

/**
 * gtk_builder_error_quark:
 *
 * Registers an error quark for [class@Gtk.Builder] errors.
 *
 * Returns: the error quark
 **/

GQuark
gtk_builder_error_quark (void)
{
  return g_quark_from_static_string ("gtk-builder-error-quark");
}

char *
_gtk_builder_get_resource_path (GtkBuilder *builder, const char *string)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  if (g_str_has_prefix (string, "resource:///"))
    return g_uri_unescape_string (string + 11"/");

  if (g_path_is_absolute (string) ||
      priv->resource_prefix == NULL)
    return NULL;

  return g_build_path ("/", priv->resource_prefix, string, NULL);
}

char *
_gtk_builder_get_absolute_filename (GtkBuilder  *builder,
                                    const char *string)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  char *filename;
  char *dirname = NULL;

  if (g_path_is_absolute (string))
    return g_strdup (string);

  if (priv->filename &&
      strcmp (priv->filename, ".") != 0)
    {
      dirname = g_path_get_dirname (priv->filename);

      if (strcmp (dirname, ".") == 0)
        {
          g_free (dirname);
          dirname = g_get_current_dir ();
        }
    }
  else
    dirname = g_get_current_dir ();

  filename = g_build_filename (dirname, string, NULL);
  g_free (dirname);

  return filename;
}

GType
gtk_builder_get_template_type (GtkBuilder *builder,
                               gboolean   *out_allow_parents)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  *out_allow_parents = priv->allow_template_parents;

  return priv->template_type;
}

/**
 * gtk_builder_create_closure:
 * @builder: a `GtkBuilder`
 * @function_name: name of the function to look up
 * @flags: closure creation flags
 * @object: (nullable): Object to create the closure with
 * @error: (nullable): return location for an error
 *
 * Creates a closure to invoke the function called @function_name.
 *
 * This is using the create_closure() implementation of @builder's
 * [iface@Gtk.BuilderScope].
 *
 * If no closure could be created, %NULL will be returned and @error
 * will be set.
 *
 * Returns: (transfer floating) (nullable): A new closure for invoking @function_name
 */

GClosure *
gtk_builder_create_closure (GtkBuilder             *builder,
                            const char             *function_name,
                            GtkBuilderClosureFlags  flags,
                            GObject                *object,
                            GError                **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);

  g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
  g_return_val_if_fail (function_name, NULL);
  g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  return gtk_builder_scope_create_closure (priv->scope, builder, function_name, flags, object, error);
}

/**
 * gtk_builder_new_from_file: (constructor)
 * @filename: (type filename): filename of user interface description file
 *
 * Parses the UI definition in the file @filename.
 *
 * If there is an error opening the file or parsing the description then
 * the program will be aborted. You should only ever attempt to parse
 * user interface descriptions that are shipped as part of your program.
 *
 * Returns: (transfer full): a `GtkBuilder` containing the described interface
 */

GtkBuilder *
gtk_builder_new_from_file (const char *filename)
{
  GError *error = NULL;
  GtkBuilder *builder;

  builder = gtk_builder_new ();
  if (!gtk_builder_add_from_file (builder, filename, &error))
    g_error ("failed to add UI from file %s: %s", filename, error->message);

  return builder;
}

/**
 * gtk_builder_new_from_resource: (constructor)
 * @resource_path: a `GResource` resource path
 *
 * Parses the UI definition at @resource_path.
 *
 * If there is an error locating the resource or parsing the
 * description, then the program will be aborted.
 *
 * Returns: (transfer full): a `GtkBuilder` containing the described interface
 */

GtkBuilder *
gtk_builder_new_from_resource (const char *resource_path)
{
  GError *error = NULL;
  GtkBuilder *builder;

  builder = gtk_builder_new ();
  if (!gtk_builder_add_from_resource (builder, resource_path, &error))
    g_error ("failed to add UI from resource %s: %s", resource_path, error->message);

  return builder;
}

/**
 * gtk_builder_new_from_string: (constructor)
 * @string: a user interface (XML) description
 * @length: the length of @string, or -1
 *
 * Parses the UI definition in @string.
 *
 * If @string is %NULL-terminated, then @length should be -1.
 * If @length is not -1, then it is the length of @string.
 *
 * If there is an error parsing @string then the program will be
 * aborted. You should not attempt to parse user interface description
 * from untrusted sources.
 *
 * Returns: (transfer full): a `GtkBuilder` containing the interface described by @string
 */

GtkBuilder *
gtk_builder_new_from_string (const char *string,
                             gssize       length)
{
  GError *error = NULL;
  GtkBuilder *builder;

  builder = gtk_builder_new ();
  if (!gtk_builder_add_from_string (builder, string, length, &error))
    g_error ("failed to add UI: %s", error->message);

  return builder;
}

/*< private >
 * _gtk_builder_prefix_error:
 * @builder: a `GtkBuilder`
 * @context: the `GtkBuildableParseContext`
 * @error: an error
 *
 * Calls g_prefix_error() to prepend a filename:line:column marker
 * to the given error. The filename is taken from @builder, and
 * the line and column are obtained by calling
 * g_markup_parse_context_get_position().
 *
 * This is intended to be called on errors returned by
 * g_markup_collect_attributes() in a start_element vfunc.
 */

void
_gtk_builder_prefix_error (GtkBuilder                *builder,
                           GtkBuildableParseContext  *context,
                           GError                   **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  int line, col;

  gtk_buildable_parse_context_get_position (context, &line, &col);
  g_prefix_error (error, "%s:%d:%d ", priv->filename, line, col);
}

/*< private >
 * _gtk_builder_error_unhandled_tag:
 * @builder: a `GtkBuilder`
 * @context: the `GtkBuildableParseContext`
 * @object: name of the object that is being handled
 * @element_name: name of the element whose start tag is being handled
 * @error: return location for the error
 *
 * Sets @error to a suitable error indicating that an @element_name
 * tag is not expected in the custom markup for @object.
 *
 * This is intended to be called in a start_element vfunc.
 */

void
_gtk_builder_error_unhandled_tag (GtkBuilder                *builder,
                                  GtkBuildableParseContext  *context,
                                  const char                *object,
                                  const char                *element_name,
                                  GError                   **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  int line, col;

  gtk_buildable_parse_context_get_position (context, &line, &col);
  g_set_error (error,
               GTK_BUILDER_ERROR,
               GTK_BUILDER_ERROR_UNHANDLED_TAG,
               "%s:%d:%d Unsupported tag for %s: <%s>",
               priv->filename, line, col,
               object, element_name);
}

/*< private >
 * @builder: a `GtkBuilder`
 * @context: the `GtkBuildableParseContext`
 * @parent_name: the name of the expected parent element
 * @error: return location for an error
 *
 * Checks that the parent element of the currently handled
 * start tag is @parent_name and set @error if it isn't.
 *
 * This is intended to be called in start_element vfuncs to
 * ensure that element nesting is as intended.
 *
 * Returns: %TRUE if @parent_name is the parent element
 */

gboolean
_gtk_builder_check_parent (GtkBuilder                *builder,
                           GtkBuildableParseContext  *context,
                           const char                *parent_name,
                           GError                   **error)
{
  return _gtk_builder_check_parents (builder, context, error, parent_name, NULL);
}

gboolean
_gtk_builder_check_parents (GtkBuilder                *builder,
                            GtkBuildableParseContext  *context,
                            GError                   **error,
                            ...)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GPtrArray *stack;
  int line, col;
  const char *parent;
  const char *element;
  va_list args;
  gboolean in_template;

  stack = gtk_buildable_parse_context_get_element_stack (context);

  element = g_ptr_array_index (stack, stack->len - 1);
  parent = stack->len > 1 ? g_ptr_array_index (stack, stack->len - 2) : "";

  in_template = g_str_equal (parent, "template");

  va_start (args, error);

  while (1) {
    char *parent_name = va_arg (args, char *);

    if (parent_name == NULL)
      break;

    if (g_str_equal (parent_name, parent) || (in_template && g_str_equal (parent_name, "object")))
      {
        va_end (args);
        return TRUE;
      }
  }

  va_end (args);

  gtk_buildable_parse_context_get_position (context, &line, &col);
  g_set_error (error,
               GTK_BUILDER_ERROR,
               GTK_BUILDER_ERROR_INVALID_TAG,
               "%s:%d:%d Can't use <%s> here",
               priv->filename, line, col, element);

  return FALSE;
}

GObject *
gtk_builder_lookup_object (GtkBuilder   *builder,
                           const char   *name,
                           int           line,
                           int           col,
                           GError      **error)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GObject *obj;

  obj = g_hash_table_lookup (priv->objects, name);
  if (obj == NULL)
    {
      g_set_error (error,
                   GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_ID,
                   "%s:%d:%d Object with ID %s not found",
                   priv->filename, line, col, name);
    }

  return obj;
}

/*< private >
 * @builder: a `GtkBuilder`
 * @name: object name to look up
 * @line: line number where @name was encountered
 * @col: column number where @name was encountered
 *
 * Looks up an object by name. Similar to gtk_builder_get_object(),
 * but sets an error if lookup fails during custom_tag_end,
 * custom_finished or parser_finished vfuncs.
 *
 * The reason for doing things this way is that these vfuncs don't
 * take a GError** parameter to return an error.
 *
 * Returns: the found object
 */

GObject *
_gtk_builder_lookup_object (GtkBuilder  *builder,
                            const char *name,
                            int          line,
                            int          col)
{
  GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
  GObject *obj;
  GError *error = NULL;

  obj = g_hash_table_lookup (priv->objects, name);
  error = (GError *) g_object_get_data (G_OBJECT (builder), "lookup-error");

  if (!obj && !error)
    {
      g_set_error (&error,
                   GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_ID,
                   "%s:%d:%d Object with ID %s not found",
                   priv->filename, line, col, name);
      g_object_set_data_full (G_OBJECT (builder), "lookup-error",
                              error, (GDestroyNotify)g_error_free);
    }

  return obj;
}

/*< private >
 * _gtk_builder_lookup_failed:
 * @GtkBuilder: a `GtkBuilder`
 * @error: return location for error
 *
 * Finds whether any object lookups have failed.
 *
 * Returns: %TRUE if @error has been set
 */

gboolean
_gtk_builder_lookup_failed (GtkBuilder  *builder,
                            GError     **error)
{
  GError *lookup_error;

  lookup_error = (GError*) g_object_steal_data (G_OBJECT (builder), "lookup-error");
  if (lookup_error)
    {
      g_propagate_error (error, lookup_error);
      return TRUE;
    }

  return FALSE;
}

void
gtk_buildable_child_deprecation_warning (GtkBuildable *buildable,
                                         GtkBuilder   *builder,
                                         const char   *type,
                                         const char   *prop)
{
  if (type)
    GTK_DEBUG (BUILDER, "<child type=\"%s\"> in %s is deprecated, just set the %s property",
               type, G_OBJECT_TYPE_NAME (buildable), prop);
  else
    GTK_DEBUG (BUILDER, "<child> in %s is deprecated, just set the %s property",
               G_OBJECT_TYPE_NAME (buildable), prop);
}

void
gtk_buildable_tag_deprecation_warning (GtkBuildable *buildable,
                                       GtkBuilder   *builder,
                                       const char   *tag,
                                       const char   *prop)
{
  GTK_DEBUG (BUILDER, "<%s> in %s is deprecated, just set the %s property",
             tag, G_OBJECT_TYPE_NAME (buildable), prop);
}

Messung V0.5 in Prozent
C=98 H=96 G=96

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.147Bemerkung:  (vorverarbeitet am  2026-07-03) ¤

*Bot Zugriff






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik