[gtk+/composite-templates-new: 3/11] Add Composite Child machinery and APIs to GtkContainer



commit a754e8769cb2b9fc8b1c7bcfe93b12192ecc58bb
Author: Tristan Van Berkom <tristanvb openismus com>
Date:   Wed Mar 20 11:56:39 2013 +0900

    Add Composite Child machinery and APIs to GtkContainer
    
    This commit implements the needed machinery for GtkContainer
    to build it's composite content from GtkBuilder XML and
    adds the following API:
    
      o gtk_container_init_template()
    
        An api to be called in instance initializers of any
        GtkContainer subclass that uses template XML to build it's components.
    
      o gtk_container_class_set_template()
    
        API to associate GtkBuilder XML to a given GtkContainer subclass
    
      o gtk_container_class_automate_child()
    
        API to declare an object built by GtkBuilder to be associated
        with an instance structure offset and automatically set.
    
     o gtk_container_get_automated_child()
    
       API for bindings to fetch a child declared to be automated by
       gtk_container_class_automate_child(), for the case where bindings
       do not generate GObjects under the hood and cannot use structure
       offsets to resolve composite object pointers.
    
     o gtk_container_class_declare_callback[s]()
    
       Declare static functions to be used in signal callbacks from
       a given class's template XML
    
     o gtk_container_class_set_connect_func()
    
       API for bindings to override the signal connection machinery
       for a given GtkContainer derived class.

 docs/reference/gtk/gtk3-sections.txt |    9 +
 gtk/gtk.symbols                      |    7 +
 gtk/gtkcontainer.c                   |  718 ++++++++++++++++++++++++++++++++++
 gtk/gtkcontainer.h                   |   36 ++-
 gtk/gtkenums.h                       |   33 ++
 5 files changed, 802 insertions(+), 1 deletions(-)
---
diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt
index 25272a3..8f11823 100644
--- a/docs/reference/gtk/gtk3-sections.txt
+++ b/docs/reference/gtk/gtk3-sections.txt
@@ -930,6 +930,15 @@ gtk_container_class_find_child_property
 gtk_container_class_install_child_property
 gtk_container_class_list_child_properties
 gtk_container_class_handle_border_width
+GtkTemplateSource
+gtk_container_init_template
+gtk_container_class_set_template
+GtkAutomateChildType
+gtk_container_get_automated_child
+gtk_container_class_automate_child
+gtk_container_class_declare_callback
+gtk_container_class_declare_callbacks
+gtk_container_class_set_connect_func
 
 <SUBSECTION Standard>
 GTK_CONTAINER
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index cb8fdf1..9bdef86 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -737,12 +737,19 @@ gtk_container_child_set
 gtk_container_child_set_property
 gtk_container_child_set_valist
 gtk_container_child_type
+gtk_container_class_automate_child
+gtk_container_class_declare_signal_callback
+gtk_container_class_declare_signal_callbacks
 gtk_container_class_find_child_property
 gtk_container_class_handle_border_width
 gtk_container_class_install_child_property
 gtk_container_class_list_child_properties
+gtk_container_class_set_signal_connect_func
+gtk_container_class_set_template_from_resource
+gtk_container_class_set_template_from_static_string
 gtk_container_forall
 gtk_container_foreach
+gtk_container_get_automated_child
 gtk_container_get_border_width
 gtk_container_get_children
 gtk_container_get_focus_chain
diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c
index ce16fe4..34a92ba 100644
--- a/gtk/gtkcontainer.c
+++ b/gtk/gtkcontainer.c
@@ -229,8 +229,91 @@
  * for regular properties.
  * </para>
  * </refsect2>
+ * <refsect2 id="GtkContainer-BUILDER-TEMPLATES">
+ * <title>Building composite widgets from template XML</title>
+ * <para>
+ * GtkContainer exposes some facilities to automate the proceedure
+ * of creating composite widgets using #GtkBuilder interface description
+ * language.
+ * </para>
+ * <para>
+ * To create composite widgets with #GtkBuilder XML, one must associate
+ * the interface description with the container class at class initialization
+ * time using gtk_container_class_set_template().
+ * </para>
+ * <para>
+ * The interface description semantics expected in composite template descriptions
+ * is slightly different from regulare #GtkBuilder XML.
+ * </para>
+ * <para>
+ * Unlike regular interface descriptions, gtk_container_class_set_template() will expect a
+ * &lt;template&gt; tag as a direct child of the toplevel &lt;interface&gt;
+ * tag. The &lt;template&gt; tag must specify the "class" attribute which
+ * must be the type name of the container. Optionally, the "parent" attribute
+ * may be specified to specify the direct parent type of the container type, this
+ * is ignored by the GtkBuilder but required for Glade to introspect what kind
+ * of properties and internal children exist for a given type when the actual
+ * type does not exist.
+ * </para>
+ * <para>
+ * The XML which is contained inside the &lt;template&gt; tag behaves as if
+ * it were added to the &lt;object&gt; tag defining @widget itself. You may set
+ * properties on @widget by inserting &lt;property&gt; tags into the &lt;template&gt; 
+ * tag, and also add &lt;child&gt; tags to add children and extend @widget in the
+ * normal way you would with &lt;object&gt; tags.
+ * </para>
+ * <para>
+ * Additionally, &lt;object&gt; tags can also be added before and
+ * after the initial &lt;template&gt; tag in the normal way, allowing
+ * one to define auxilary objects which might be referenced by other
+ * widgets declared as children of the &lt;template&gt; tag.
+ * </para>
+ * <para>
+ * <example>
+ * <title>A GtkBuilder Template Definition</title>
+ * <programlisting><![CDATA[
+ * <interface>
+ *   <template class="FooWidget" parent="GtkBox">
+ *     <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+ *     <property name="spacing">4</property>
+ *     <child>
+ *       <object class="GtkButton" id="hello_button">
+ *         <property name="label">Hello World</property>
+ *       </object>
+ *     </child>
+ *     <child>
+ *       <object class="GtkButton" id="goodbye_button">
+ *         <property name="label">Goodbye World</property>
+ *       </object>
+ *     </child>
+ *   </template>
+ * </interface>
+ * ]]></programlisting>
+ * </example>
+ * </para>
+ * </refsect2>
  */
 
+typedef struct {
+  gchar               *name;           /* Name of the template automatic child */
+  gboolean             internal_child; /* Whether the automatic widget should be exported as an 
<internal-child> */
+  GtkAutomateChildType child_type;     /* How the automatic widget pointer is stored */
+  guint                offset;         /* Instance or Private data offset where to set the automatic child */
+} AutomaticChildClass;
+
+typedef struct {
+  gchar     *callback_name;
+  GCallback  callback_symbol;
+} CallbackSymbol;
+
+struct _GtkContainerTemplate {
+  GBytes               *data;
+  GSList               *children;
+  GSList               *callbacks;
+  GtkBuilderConnectFunc connect_func;
+  gpointer              connect_data;
+  GDestroyNotify        destroy_notify;
+};
 
 struct _GtkContainerPrivate
 {
@@ -239,6 +322,11 @@ struct _GtkContainerPrivate
   guint resize_handler;
   GdkFrameClock *resize_clock;
 
+  /* A hash by GType key, containing
+   * hash tables by widget name
+   */
+  GHashTable *auto_children;
+
   guint border_width : 16;
 
   guint has_focus_chain    : 1;
@@ -323,12 +411,34 @@ static gchar* gtk_container_child_default_composite_name (GtkContainer *containe
 static GtkWidgetPath * gtk_container_real_get_path_for_child (GtkContainer *container,
                                                               GtkWidget    *child);
 
+/* --- functions dealing with template data structures --- */
+static AutomaticChildClass  *automatic_child_class_new             (const gchar          *name,
+                                                                   gboolean              internal_child,
+                                                                   GtkAutomateChildType  child_type,
+                                                                   guint                 offset);
+static void                  automatic_child_class_free            (AutomaticChildClass  *child_class);
+static CallbackSymbol       *callback_symbol_new                   (const gchar          *name,
+                                                                   GCallback             callback);
+static void                  callback_symbol_free                  (CallbackSymbol       *callback);
+static void                  template_data_free                    (GtkContainerTemplate *template_data);
+static GHashTable           *get_auto_child_hash                   (GtkContainer         *container,
+                                                                   GType                 type,
+                                                                   gboolean              create);
+static gboolean              setup_automatic_child                 (GtkContainerTemplate *template_data,
+                                                                   GType                 class_type,
+                                                                   AutomaticChildClass  *child_class,
+                                                                   GtkContainer         *container,
+                                                                   GtkBuilder           *builder);
+
 /* GtkBuildable */
 static void gtk_container_buildable_init           (GtkBuildableIface *iface);
 static void gtk_container_buildable_add_child      (GtkBuildable *buildable,
                                                     GtkBuilder   *builder,
                                                     GObject      *child,
                                                     const gchar  *type);
+static GObject *gtk_container_buildable_get_internal_child (GtkBuildable  *buildable,
+                                                           GtkBuilder    *builder,
+                                                           const gchar   *childname);
 static gboolean gtk_container_buildable_custom_tag_start (GtkBuildable  *buildable,
                                                           GtkBuilder    *builder,
                                                           GObject       *child,
@@ -402,6 +512,7 @@ gtk_container_base_class_init (GtkContainerClass *class)
   /* reset instance specifc class fields that don't get inherited */
   class->set_child_property = NULL;
   class->get_child_property = NULL;
+  class->tmpl = NULL;
 }
 
 static void
@@ -419,6 +530,7 @@ gtk_container_base_class_finalize (GtkContainerClass *class)
       g_param_spec_unref (pspec);
     }
   g_list_free (list);
+  template_data_free (class->tmpl);
 }
 
 static void
@@ -526,6 +638,7 @@ gtk_container_buildable_init (GtkBuildableIface *iface)
 {
   parent_buildable_iface = g_type_interface_peek_parent (iface);
   iface->add_child = gtk_container_buildable_add_child;
+  iface->get_internal_child = gtk_container_buildable_get_internal_child;
   iface->custom_tag_start = gtk_container_buildable_custom_tag_start;
   iface->custom_tag_end = gtk_container_buildable_custom_tag_end;
 }
@@ -550,6 +663,49 @@ gtk_container_buildable_add_child (GtkBuildable  *buildable,
                g_type_name (G_OBJECT_TYPE (child)), g_type_name (G_OBJECT_TYPE (buildable)));
 }
 
+static GObject *
+gtk_container_buildable_get_internal_child (GtkBuildable  *buildable,
+                                           GtkBuilder    *builder,
+                                           const gchar   *childname)
+{
+  GtkContainerClass *class;
+  GSList *l;
+  GType internal_child_type = 0;
+
+  /* Check if it's a valid internal child first, only children
+   * that are explicitly declared to be exported as 'internal-children'
+   * can be evaluated here
+   */
+  for (class = GTK_CONTAINER_GET_CLASS (buildable);
+       GTK_IS_CONTAINER_CLASS (class);
+       class = g_type_class_peek_parent (class))
+    {
+      GtkContainerTemplate *template = class->tmpl;
+
+      if (!template)
+       continue;
+
+      for (l = template->children; l && internal_child_type == 0; l = l->next)
+       {
+         AutomaticChildClass *child_class = l->data;
+
+         if (child_class->internal_child &&
+             strcmp (childname, child_class->name) == 0)
+           internal_child_type = G_OBJECT_CLASS_TYPE (class);
+       }
+    }
+
+  /* Now return the 'internal-child' from the class which declared it, note
+   * that gtk_container_get_automated_child() an API used to access objects
+   * which are in the private scope of a given class (so it's not appropriate
+   * to call it here if the child is not explicitly marked to be internal).
+   */
+  if (internal_child_type != 0)
+    return gtk_container_get_automated_child (GTK_CONTAINER (buildable), internal_child_type, childname);
+
+  return parent_buildable_iface->get_internal_child (buildable, builder, childname);
+}
+
 static void
 gtk_container_buildable_set_child_property (GtkContainer *container,
                                             GtkBuilder   *builder,
@@ -1376,6 +1532,12 @@ gtk_container_destroy (GtkWidget *widget)
 
   gtk_container_foreach (container, (GtkCallback) gtk_widget_destroy, NULL);
 
+  if (priv->auto_children)
+    {
+      g_hash_table_destroy (priv->auto_children);
+      priv->auto_children = NULL;
+    }
+
   GTK_WIDGET_CLASS (parent_class)->destroy (widget);
 }
 
@@ -3415,3 +3577,559 @@ gtk_container_get_path_for_child (GtkContainer *container,
 
   return path;
 }
+
+/****************************************************************
+ *                 GtkBuilder automated templates               *
+ ****************************************************************/
+static AutomaticChildClass *
+automatic_child_class_new (const gchar         *name,
+                          gboolean             internal_child,
+                          GtkAutomateChildType child_type,
+                          guint                offset)
+{
+  AutomaticChildClass *child_class = g_slice_new0 (AutomaticChildClass);
+
+  child_class->name = g_strdup (name);
+  child_class->internal_child = internal_child;
+  child_class->child_type = child_type;
+  child_class->offset = offset;
+
+  return child_class;
+}
+
+static void
+automatic_child_class_free (AutomaticChildClass *child_class)
+{
+  if (child_class)
+    {
+      g_free (child_class->name);
+      g_slice_free (AutomaticChildClass, child_class);
+    }
+}
+
+static CallbackSymbol *
+callback_symbol_new (const gchar *name,
+                    GCallback    callback)
+{
+  CallbackSymbol *cb = g_slice_new0 (CallbackSymbol);
+
+  cb->callback_name = g_strdup (name);
+  cb->callback_symbol = callback;
+
+  return cb;
+}
+
+static void
+callback_symbol_free (CallbackSymbol *callback)
+{
+  if (callback)
+    {
+      g_free (callback->callback_name);
+      g_slice_free (CallbackSymbol, callback);
+    }
+}
+
+static void
+template_data_free (GtkContainerTemplate *template_data)
+{
+  if (template_data)
+    {
+      g_bytes_unref (template_data->data);
+      g_slist_free_full (template_data->children, (GDestroyNotify)automatic_child_class_free);
+      g_slist_free_full (template_data->callbacks, (GDestroyNotify)callback_symbol_free);
+
+      if (template_data->connect_data &&
+         template_data->destroy_notify)
+       template_data->destroy_notify (template_data->connect_data);
+
+      g_slice_free (GtkContainerTemplate, template_data);
+    }
+}
+
+static void
+template_data_add_callback_symbol (GtkContainerTemplate *template_data,
+                                  const gchar          *callback_name,
+                                  GCallback             callback_symbol)
+{
+  CallbackSymbol *cb;
+
+  cb = callback_symbol_new (callback_name, callback_symbol);
+  template_data->callbacks = g_slist_prepend (template_data->callbacks, cb);
+}
+
+static GHashTable *
+get_auto_child_hash (GtkContainer        *container,
+                    GType                type,
+                    gboolean             create)
+{
+  GHashTable *auto_child_hash;
+
+  if (container->priv->auto_children == NULL)
+    {
+      if (!create)
+       return NULL;
+
+      container->priv->auto_children =
+       g_hash_table_new_full (g_direct_hash,
+                              g_direct_equal,
+                              NULL,
+                              (GDestroyNotify)g_hash_table_destroy);
+    }
+
+  auto_child_hash =
+    g_hash_table_lookup (container->priv->auto_children, GSIZE_TO_POINTER (type));
+
+  if (!auto_child_hash && create)
+    {
+      auto_child_hash = g_hash_table_new_full (g_str_hash,
+                                              g_str_equal,
+                                              NULL,
+                                              (GDestroyNotify)g_object_unref);
+
+      g_hash_table_insert (container->priv->auto_children,
+                          GSIZE_TO_POINTER (type),
+                          auto_child_hash);
+    }
+
+  return auto_child_hash;
+}
+
+static gboolean
+setup_automatic_child (GtkContainerTemplate *template_data,
+                      GType                 class_type,
+                      AutomaticChildClass  *child_class,
+                      GtkContainer         *container,
+                      GtkBuilder           *builder)
+{
+  GHashTable *auto_child_hash;
+  GObject    *object;
+  gpointer    class_private = NULL;
+  GObject   **destination = NULL;
+
+  object = gtk_builder_get_object (builder, child_class->name);
+  if (!object)
+    {
+      g_critical ("Unable to retrieve object '%s' from class template for type '%s' while building a '%s'",
+                 child_class->name, g_type_name (class_type), G_OBJECT_TYPE_NAME (container));
+      return FALSE;
+    }
+
+  /* Insert into the hash so that it can be fetched with
+   * gtk_container_get_automated_child() and also in automated
+   * implementations of GtkBuildable.get_internal_child()
+   */
+  auto_child_hash = get_auto_child_hash (container, class_type, TRUE);
+  g_hash_table_insert (auto_child_hash, child_class->name, g_object_ref (object));
+
+  switch (child_class->child_type)
+    {
+    case GTK_AUTOMATE_CHILD_PUBLIC:
+
+      /* Assign 'object' to the specified offset in the public instance data */
+      destination = G_STRUCT_MEMBER_P (container, child_class->offset);
+      break;
+
+    case GTK_AUTOMATE_CHILD_PRIVATE:
+
+      /* Assign 'object' to the specified offset in the class private data */
+      class_private = G_TYPE_INSTANCE_GET_PRIVATE (container, class_type, gpointer);
+      destination = G_STRUCT_MEMBER_P (class_private, child_class->offset);
+      break;
+
+    case GTK_AUTOMATE_CHILD_MANUAL:
+      /* Do nothing */
+      break;
+    }
+
+  if (destination)
+    *destination = object;
+
+  return TRUE;
+}
+
+/**
+ * gtk_container_init_template:
+ * @container: a #GtkContainer
+ *
+ * Creates and initializes child widgets defined in templates. This
+ * function must be called in the instance initializer for any
+ * class which assigned itself a template using gtk_container_class_set_template()
+ *
+ * It is important to call this function in the instance initializer
+ * of a #GtkContainer subclass and not in #GObject.constructed() or
+ * #GObject.constructor() for two reasons.
+ *
+ * One reason is that generally derived widgets will assume that parent
+ * class composite widgets have been created in their instance
+ * initializers.
+ *
+ * Another reason is that when calling g_object_new() on a container with
+ * composite templates, it's important to build the composite widgets
+ * before the construct properties are set. Properties passed to g_object_new()
+ * should take precedence over properties set in the private template XML.
+ *
+ * Since: 3.10
+ */
+void
+gtk_container_init_template (GtkContainer *container)
+{
+  GtkContainerTemplate *template;
+  GtkBuilder *builder;
+  GError *error = NULL;
+  GObject *object;
+  GtkWidget *widget;
+  GSList *l;
+  GType class_type;
+
+  g_return_if_fail (GTK_IS_CONTAINER (container));
+
+  widget = GTK_WIDGET (container);
+  object = G_OBJECT (container);
+  class_type = G_OBJECT_TYPE (container);
+
+  template = GTK_CONTAINER_GET_CLASS (container)->tmpl;
+  g_return_if_fail (template != NULL);
+
+  /* It is important that gtk_container_template_init()  to build templates at widget_init()
+   * time rather than in the constructor() for two reasons:
+   *
+   *   o Many third party derived container widgets assume
+   *     that parent class widgetry is already built in thier own
+   *     widget _init() time. For instance GtkDialog child classes
+   *     which access the vbox/action_area from the _init() function.
+   *
+   *   o When calling g_object_new() on a container with composite
+   *     templates, it's important to build widgetry and set properties
+   *     before the construct properties are set. Properties passed
+   *     to g_object_new() should take precedence over properties
+   *     set in the private template XML.
+   */
+  builder = gtk_builder_new ();
+
+  /* Add any callback symbols declared for this GType to the GtkBuilder namespace */
+  for (l = template->callbacks; l; l = l->next)
+    {
+      CallbackSymbol *callback = l->data;
+
+      gtk_builder_add_callback_symbol (builder, callback->callback_name, callback->callback_symbol);
+    }
+
+  /* This will build the template XML as children to the container instance, also it
+   * will validate that the template is created for the correct GType and assert that
+   * there is no infinate recursion.
+   */
+  if (!_gtk_builder_extend_with_template (builder, widget, class_type,
+                                         (const gchar *)g_bytes_get_data (template->data, NULL),
+                                         g_bytes_get_size (template->data),
+                                         &error))
+    {
+      g_critical ("Error building template class '%s' for an instance of type '%s': %s",
+                 g_type_name (class_type), G_OBJECT_TYPE_NAME (object), error->message);
+      g_error_free (error);
+
+      /* This should never happen, if the template XML cannot be built
+       * then it is a critical programming error.
+       */
+      g_object_unref (builder);
+      return;
+    }
+
+  /* Build the automatic child data
+   */
+  for (l = template->children; l; l = l->next)
+    {
+      AutomaticChildClass *child_class = l->data;
+
+      /* This will setup the pointer of an automated child, and cause
+       * it to be available in any GtkBuildable.get_internal_child()
+       * invocations which may follow by reference in child classes.
+       */
+      if (!setup_automatic_child (template,
+                                 class_type,
+                                 child_class,
+                                 container,
+                                 builder))
+       {
+         g_object_unref (builder);
+         return;
+       }
+    }
+
+  /* Connect signals. All signal data from a template receive the 
+   * template instance as user data automatically.
+   *
+   * A GtkBuilderConnectFunc can be provided to gtk_container_class_set_signal_connect_func()
+   * in order for templates to be usable by bindings.
+   */
+  if (template->connect_func)
+    gtk_builder_connect_signals_full (builder, template->connect_func, template->connect_data);
+  else
+    gtk_builder_connect_signals (builder, object);
+
+  g_object_unref (builder);
+}
+
+/**
+ * gtk_container_class_set_template:
+ * @container_class: A #GtkContainerClass
+ * @template_source: The #GtkTemplateSource defining how to load @template_string
+ * @template_string: (transfer none): A string referring to GtkBuilder XML template
+ *
+ * This should be called a class initialization time to specify
+ * the GtkBuilder XML to be used to extend a #GtkContainer type widget.
+ *
+ * The XML can be loaded from a variety of sources, defined by @template_source.
+ *
+ * <note><para>Any class that installs templates must call gtk_container_init_template()
+ * in the widget's instance initializer</para></note>
+ *
+ * Since: 3.10
+ */
+void
+gtk_container_class_set_template (GtkContainerClass    *container_class,
+                                 GtkTemplateSource     template_source,
+                                 const gchar          *template_string)
+{
+  GError *error = NULL;
+  GBytes *bytes = NULL;
+  gchar  *data;
+  gsize   length = 0;
+
+  g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
+  g_return_if_fail (template_string && template_string[0]);
+  g_return_if_fail (container_class->tmpl == NULL);
+
+  switch (template_source)
+    {
+    case GTK_TEMPLATE_SOURCE_STRING:
+      bytes = g_bytes_new (template_string, strlen (template_string));
+      break;
+
+    case GTK_TEMPLATE_SOURCE_STATIC_STRING:
+      bytes = g_bytes_new_static (template_string, strlen (template_string));
+      break;
+
+    case GTK_TEMPLATE_SOURCE_RESOURCE:
+      bytes = g_resources_lookup_data (template_string, 0, &error);
+      if (!bytes)
+       {
+         g_critical ("Unable to load resource for composite template for type '%s': %s",
+                     G_OBJECT_CLASS_NAME (container_class), error->message);
+         g_error_free (error);
+         return;
+       }
+      break;
+
+    case GTK_TEMPLATE_SOURCE_FILE:
+      if (!g_file_get_contents (template_string, &data, &length, &error))
+       {
+         g_critical ("Unable to load file for composite template for type '%s': %s",
+                     G_OBJECT_CLASS_NAME (container_class), error->message);
+         g_error_free (error);
+         return;
+       }
+
+      bytes = g_bytes_new_take (data, length);
+      break;
+
+    default:
+      g_critical ("Invalid template source passed to gtk_container_class_set_template()");
+      return;
+    }
+
+  if (!bytes)
+    {
+      g_critical ("Unable to load composite template XML data for type %s",
+                 G_OBJECT_CLASS_NAME (container_class));
+      return;
+    }
+
+  container_class->tmpl = g_slice_new0 (GtkContainerTemplate);
+  container_class->tmpl->data = bytes;
+}
+
+/**
+ * gtk_container_class_declare_callback:
+ * @container_class: A #GtkContainerClass
+ * @callback_name: The name of the callback as expected in the template XML
+ * @callback_symbol: (scope async): The callback symbol
+ *
+ * Declares a @callback_symbol to handle @callback_name from the template XML
+ * defined for @container_type. See gtk_builder_add_callback_symbol().
+ *
+ * <note><para>This must be called from a composite container classes class
+ * initializer after calling gtk_container_class_set_template()</para></note>
+ *
+ * Since: 3.10
+ */
+void
+gtk_container_class_declare_callback (GtkContainerClass    *container_class,
+                                     const gchar          *callback_name,
+                                     GCallback             callback_symbol)
+{
+  g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
+  g_return_if_fail (callback_name && callback_name[0]);
+  g_return_if_fail (callback_symbol != NULL);
+  g_return_if_fail (container_class->tmpl != NULL);
+
+  template_data_add_callback_symbol (container_class->tmpl, callback_name, callback_symbol);
+}
+
+/**
+ * gtk_container_class_declare_callbacks:
+ * @container_class: A #GtkContainerClass
+ * @first_callback_name: The name of the callback as expected in the template XML
+ * @first_callback_symbol: (scope async): The callback symbol
+ * @...: A list of callback name and callback symbol pairs terminated with %NULL
+ *
+ * A convenience function to declare many callbacks instead of calling
+ * gtk_container_class_declare_callback() for each symbol.
+ *
+ * Since: 3.10
+ */
+void
+gtk_container_class_declare_callbacks (GtkContainerClass    *container_class,
+                                      const gchar          *first_callback_name,
+                                      GCallback             first_callback_symbol,
+                                      ...)
+{
+  va_list var_args;
+  const gchar *callback_name;
+  GCallback callback_symbol;
+
+  g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
+  g_return_if_fail (first_callback_name && first_callback_name[0]);
+  g_return_if_fail (first_callback_symbol != NULL);
+  g_return_if_fail (container_class->tmpl != NULL);
+
+  callback_name = first_callback_name;
+  callback_symbol = first_callback_symbol;
+
+  va_start (var_args, first_callback_symbol);
+
+  do {
+
+    template_data_add_callback_symbol (container_class->tmpl, callback_name, callback_symbol);
+
+    callback_name = va_arg (var_args, const gchar*);
+
+    if (callback_name)
+      callback_symbol = va_arg (var_args, GCallback);
+
+  } while (callback_name != NULL);
+
+  va_end (var_args);
+}
+
+/**
+ * gtk_container_class_set_connect_func:
+ * @container_class: A #GtkContainerClass
+ * @connect_func: The #GtkBuilderConnectFunc to use when connecting signals in the class template
+ * @connect_data: The data to pass to @connect_func
+ * @connect_data_destroy: The #GDestroyNotify to free @connect_data, this will only be used at
+ *                        class finalization time, when no classes of type @container_type are in use 
anymore.
+ *
+ * For use in lanuage bindings, this will override the default #GtkBuilderConnectFunc to be
+ * used when parsing GtkBuilder xml from this class's template data.
+ *
+ * <note><para>This must be called from a composite container classes class
+ * initializer after calling gtk_container_class_set_template()</para></note>
+ *
+ * Since: 3.10
+ */
+void
+gtk_container_class_set_connect_func (GtkContainerClass    *container_class,
+                                     GtkBuilderConnectFunc connect_func,
+                                     gpointer              connect_data,
+                                     GDestroyNotify        connect_data_destroy)
+{
+  g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
+  g_return_if_fail (container_class->tmpl != NULL);
+
+  /* Defensive, destroy any previously set data */
+  if (container_class->tmpl->connect_data &&
+      container_class->tmpl->destroy_notify)
+    container_class->tmpl->destroy_notify (container_class->tmpl->connect_data);
+
+  container_class->tmpl->connect_func   = connect_func;
+  container_class->tmpl->connect_data   = connect_data;
+  container_class->tmpl->destroy_notify = connect_data_destroy;
+}
+
+/**
+ * gtk_container_class_automate_child:
+ * @container_class: A #GtkContainerClass
+ * @name: The "id" of the child defined in the template XML
+ * @internal_child: Whether the child should be accessible as an "internal-child"
+ *                  when this class is used in GtkBuilder XML
+ * @auto_child_type: The #GtkAutomateChildType defining the storage type for this child pointer
+ * @struct_offset: The structure offset where the automated child pointer should be set. This
+ *                 parameter is ignored when @auto_child_type is specified as %GTK_AUTOMATE_CHILD_MANUAL.
+ *
+ * Automatically assign an object declared in the class template XML to be set to a location
+ * on a freshly built instance.
+ *
+ * If @internal_child is specified, #GtkBuildableIface.get_internal_child() will be automatically
+ * implemented by the #GtkContainer class so there is no need to implement it manually.
+ *
+ * <note><para>This must be called from a composite container classes class
+ * initializer after calling gtk_container_class_set_template()</para></note>
+ *
+ * Since: 3.10
+ */
+void
+gtk_container_class_automate_child (GtkContainerClass    *container_class,
+                                   const gchar          *name,
+                                   gboolean              internal_child,
+                                   GtkAutomateChildType  auto_child_type,
+                                   guint                 struct_offset)
+{
+  AutomaticChildClass *child_class;
+
+  g_return_if_fail (GTK_IS_CONTAINER_CLASS (container_class));
+  g_return_if_fail (name && name[0]);
+  g_return_if_fail (container_class->tmpl != NULL);
+
+  child_class = automatic_child_class_new (name,
+                                          internal_child,
+                                          auto_child_type,
+                                          struct_offset);
+  container_class->tmpl->children =
+    g_slist_prepend (container_class->tmpl->children, child_class);
+}
+
+/**
+ * gtk_container_get_automated_child:
+ * @container: A #GtkContainer
+ * @container_type: The #GType to get an automated child for
+ * @name: The "id" of the child defined in the template XML
+ *
+ * Fetch an object build from the template XML for @container_type in this @container instance.
+ *
+ * This will only report children which were previously declared with gtk_container_class_automate_child().
+ *
+ * This function is only meant to be called for code which is private to the @container_type which
+ * declared the child and is meant for language bindings which cannot easily make use
+ * of the GObject structure offsets.
+ *
+ * Returns: (transfer none): The object built in the template XML with the id @name
+ */
+GObject *
+gtk_container_get_automated_child (GtkContainer         *container,
+                                  GType                 container_type,
+                                  const gchar          *name)
+{
+  GHashTable *auto_child_hash;
+  GObject *ret = NULL;
+
+  g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
+  g_return_val_if_fail (g_type_name (container_type) != NULL, NULL);
+  g_return_val_if_fail (name && name[0], NULL);
+
+  auto_child_hash = get_auto_child_hash (container, container_type, FALSE);
+
+  if (auto_child_hash)
+    ret = g_hash_table_lookup (auto_child_hash, name);
+
+  return ret;
+}
diff --git a/gtk/gtkcontainer.h b/gtk/gtkcontainer.h
index 3bcd2b1..69a4090 100644
--- a/gtk/gtkcontainer.h
+++ b/gtk/gtkcontainer.h
@@ -31,6 +31,7 @@
 #endif
 
 #include <gtk/gtkwidget.h>
+#include <gtk/gtkbuilder.h>
 
 
 G_BEGIN_DECLS
@@ -46,6 +47,7 @@ G_BEGIN_DECLS
 typedef struct _GtkContainer              GtkContainer;
 typedef struct _GtkContainerPrivate       GtkContainerPrivate;
 typedef struct _GtkContainerClass         GtkContainerClass;
+typedef struct _GtkContainerTemplate      GtkContainerTemplate;
 
 struct _GtkContainer
 {
@@ -91,6 +93,8 @@ struct _GtkContainerClass
 
   unsigned int _handle_border_width : 1;
 
+  GtkContainerTemplate *tmpl;
+
   /* Padding for future expansion */
   void (*_gtk_reserved1) (void);
   void (*_gtk_reserved2) (void);
@@ -99,7 +103,6 @@ struct _GtkContainerClass
   void (*_gtk_reserved5) (void);
   void (*_gtk_reserved6) (void);
   void (*_gtk_reserved7) (void);
-  void (*_gtk_reserved8) (void);
 };
 
 
@@ -222,6 +225,37 @@ void    gtk_container_class_handle_border_width (GtkContainerClass *klass);
 GtkWidgetPath * gtk_container_get_path_for_child (GtkContainer      *container,
                                                   GtkWidget         *child);
 
+GDK_AVAILABLE_IN_3_10
+void    gtk_container_init_template             (GtkContainer    *container);
+GDK_AVAILABLE_IN_3_10
+GObject *gtk_container_get_automated_child      (GtkContainer         *container,
+                                                GType                 container_type,
+                                                const gchar          *name);
+GDK_AVAILABLE_IN_3_10
+void    gtk_container_class_set_template        (GtkContainerClass    *container_class,
+                                                GtkTemplateSource     template_source,
+                                                const gchar          *template_string);
+GDK_AVAILABLE_IN_3_10
+void    gtk_container_class_declare_callback    (GtkContainerClass    *container_class,
+                                                const gchar          *callback_name,
+                                                GCallback             callback_symbol);
+GDK_AVAILABLE_IN_3_10
+void    gtk_container_class_declare_callbacks   (GtkContainerClass    *container_class,
+                                                const gchar          *first_callback_name,
+                                                GCallback             first_callback_symbol,
+                                                ...) G_GNUC_NULL_TERMINATED;
+GDK_AVAILABLE_IN_3_10
+void    gtk_container_class_set_connect_func    (GtkContainerClass    *container_class,
+                                                GtkBuilderConnectFunc connect_func,
+                                                gpointer              connect_data,
+                                                GDestroyNotify        connect_data_destroy);
+GDK_AVAILABLE_IN_3_10
+void    gtk_container_class_automate_child      (GtkContainerClass    *container_class,
+                                                const gchar          *name,
+                                                gboolean              internal_child,
+                                                GtkAutomateChildType  auto_child_type,
+                                                guint                 struct_offset);
+
 G_END_DECLS
 
 #endif /* __GTK_CONTAINER_H__ */
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index 1789d98..0d3201c 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -1060,4 +1060,37 @@ typedef enum
   GTK_INPUT_HINT_INHIBIT_OSK         = 1 << 7
 } GtkInputHints;
 
+/**
+ * GtkAutomateChildType:
+ * @GTK_AUTOMATE_CHILD_MANUAL: Do not add any automatic pointer to the widget class
+ * @GTK_AUTOMATE_CHILD_PUBLIC: Set the child pointer to a public instance data location
+ * @GTK_AUTOMATE_CHILD_PRIVATE: Set the child pointer to a private instance data location
+ *
+ * Defines how gtk_container_class_automate_child() should deal with automatic children
+ * defined in a #GtkBuilder template.
+ *
+ * Since: 3.10
+ */
+typedef enum {
+  GTK_AUTOMATE_CHILD_MANUAL,
+  GTK_AUTOMATE_CHILD_PUBLIC,
+  GTK_AUTOMATE_CHILD_PRIVATE,
+} GtkAutomateChildType;
+
+/**
+ * GtkTemplateSource:
+ * @GTK_TEMPLATE_SOURCE_STRING: Load the template XML from a string, the container class will keep a copy of 
the passed string.
+ * @GTK_TEMPLATE_SOURCE_STATIC_STRING: Load the template XML from a static string constant, without copying 
the string.
+ * @GTK_TEMPLATE_SOURCE_RESOURCE: Load the template XML from a global resource using 
g_resources_lookup_data().
+ * @GTK_TEMPLATE_SOURCE_FILE: Load the template XML from an absolute file name.
+ *
+ * Defines how a composite #GtkContainer's template XML data is loaded with 
gtk_container_class_set_template().
+ */
+typedef enum {
+  GTK_TEMPLATE_SOURCE_STRING,
+  GTK_TEMPLATE_SOURCE_STATIC_STRING,
+  GTK_TEMPLATE_SOURCE_RESOURCE,
+  GTK_TEMPLATE_SOURCE_FILE
+} GtkTemplateSource;
+
 #endif /* __GTK_ENUMS_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]