/*
* Copyright © 2019 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Authors: Benjamin Otte
*/
#include "config.h"
#include "gtkbuilderlistitemfactory.h"
#include "gtkbuilder.h"
#include "gtkbuilderprivate.h"
#include "gtklistitemfactoryprivate.h"
#include "gtklistitemprivate.h"
/**
* GtkBuilderListItemFactory:
*
* `GtkBuilderListItemFactory` is a `GtkListItemFactory` that creates
* widgets by instantiating `GtkBuilder` UI templates.
*
* The templates must be extending `GtkListItem`, and typically use
* `GtkExpression`s to obtain data from the items in the model.
*
* Example:
* ```xml
*
*
*
*
*
*
*
* ```
*/
struct _GtkBuilderListItemFactory
{
GtkListItemFactory parent_instance;
GtkBuilderScope *scope;
GBytes *bytes;
GBytes *data;
char *resource;
};
struct _GtkBuilderListItemFactoryClass
{
GtkListItemFactoryClass parent_class;
};
enum {
PROP_0,
PROP_BYTES,
PROP_RESOURCE,
PROP_SCOPE,
N_PROPS
};
G_DEFINE_TYPE (GtkBuilderListItemFactory, gtk_builder_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_builder_list_item_factory_setup (GtkListItemFactory *factory,
GObject *item,
gboolean bind,
GFunc func,
gpointer data)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (factory);
GtkBuilder *builder;
GError *error = NULL;
GTK_LIST_ITEM_FACTORY_CLASS (gtk_builder_list_item_factory_parent_class)->setup (factory, item, bind, func, data);
builder = gtk_builder_new ();
gtk_builder_set_current_object (builder, item);
if (self->scope)
gtk_builder_set_scope (builder, self->scope);
gtk_builder_set_allow_template_parents (builder, TRUE);
if (!gtk_builder_extend_with_template (builder, G_OBJECT (item), G_OBJECT_TYPE (item),
(const char *)g_bytes_get_data (self->data, NULL),
g_bytes_get_size (self->data),
&error))
{
g_critical ("Error building template for list item: %s", 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;
}
g_object_unref (builder);
}
static void
gtk_builder_list_item_factory_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
switch (property_id)
{
case PROP_BYTES:
g_value_set_boxed (value, self->bytes);
break;
case PROP_RESOURCE:
g_value_set_string (value, self->resource);
break;
case PROP_SCOPE:
g_value_set_object (value, self->scope);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static gboolean
gtk_builder_list_item_factory_set_bytes (GtkBuilderListItemFactory *self,
GBytes *bytes)
{
if (bytes == NULL)
return FALSE;
if (self->bytes)
{
g_critical ("Data for GtkBuilderListItemFactory has already been set.");
return FALSE;
}
self->bytes = g_bytes_ref (bytes);
if (!_gtk_buildable_parser_is_precompiled (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)))
{
GError *error = NULL;
GBytes *data;
data = _gtk_buildable_parser_precompile (g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes),
&error);
if (data == NULL)
{
g_warning ("Failed to precompile template for GtkBuilderListItemFactory: %s", error->message);
g_error_free (error);
self->data = g_bytes_ref (bytes);
}
else
{
self->data = data;
}
}
return TRUE;
}
static void
gtk_builder_list_item_factory_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
switch (property_id)
{
case PROP_BYTES:
gtk_builder_list_item_factory_set_bytes (self, g_value_get_boxed (value));
break;
case PROP_RESOURCE:
{
GError *error = NULL;
GBytes *bytes;
const char *resource;
resource = g_value_get_string (value);
if (resource == NULL)
break;
bytes = g_resources_lookup_data (resource, 0, &error);
if (bytes)
{
if (gtk_builder_list_item_factory_set_bytes (self, bytes))
self->resource = g_strdup (resource);
g_bytes_unref (bytes);
}
else
{
g_critical ("Unable to load resource for list item template: %s", error->message);
g_error_free (error);
}
}
break;
case PROP_SCOPE:
self->scope = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_builder_list_item_factory_finalize (GObject *object)
{
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
g_clear_object (&self->scope);
g_bytes_unref (self->bytes);
g_bytes_unref (self->data);
g_free (self->resource);
G_OBJECT_CLASS (gtk_builder_list_item_factory_parent_class)->finalize (object);
}
static void
gtk_builder_list_item_factory_class_init (GtkBuilderListItemFactoryClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
gobject_class->finalize = gtk_builder_list_item_factory_finalize;
gobject_class->get_property = gtk_builder_list_item_factory_get_property;
gobject_class->set_property = gtk_builder_list_item_factory_set_property;
factory_class->setup = gtk_builder_list_item_factory_setup;
/**
* GtkBuilderListItemFactory:bytes: (attributes org.gtk.Property.get=gtk_builder_list_item_factory_get_bytes)
*
* `GBytes` containing the UI definition.
*/
properties[PROP_BYTES] =
g_param_spec_boxed ("bytes", NULL, NULL,
G_TYPE_BYTES,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
/**
* GtkBuilderListItemFactory:resource: (attributes org.gtk.Property.get=gtk_builder_list_item_factory_get_resource)
*
* Path of the resource containing the UI definition.
*/
properties[PROP_RESOURCE] =
g_param_spec_string ("resource", NULL, NULL,
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
/**
* GtkBuilderListItemFactory:scope: (attributes org.gtk.Property.get=gtk_builder_list_item_factory_get_scope)
*
* `GtkBuilderScope` to use when instantiating listitems
*/
properties[PROP_SCOPE] =
g_param_spec_object ("scope", NULL, NULL,
GTK_TYPE_BUILDER_SCOPE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gtk_builder_list_item_factory_init (GtkBuilderListItemFactory *self)
{
}
/**
* gtk_builder_list_item_factory_new_from_bytes:
* @scope: (nullable) (transfer none): A scope to use when instantiating
* @bytes: the `GBytes` containing the ui file to instantiate
*
* Creates a new `GtkBuilderListItemFactory` that instantiates widgets
* using @bytes as the data to pass to `GtkBuilder`.
*
* Returns: a new `GtkBuilderListItemFactory`
**/
GtkListItemFactory *
gtk_builder_list_item_factory_new_from_bytes (GtkBuilderScope *scope,
GBytes *bytes)
{
g_return_val_if_fail (bytes != NULL, NULL);
return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
"bytes", bytes,
"scope", scope,
NULL);
}
/**
* gtk_builder_list_item_factory_new_from_resource:
* @scope: (nullable) (transfer none): A scope to use when instantiating
* @resource_path: valid path to a resource that contains the data
*
* Creates a new `GtkBuilderListItemFactory` that instantiates widgets
* using data read from the given @resource_path to pass to `GtkBuilder`.
*
* Returns: a new `GtkBuilderListItemFactory`
**/
GtkListItemFactory *
gtk_builder_list_item_factory_new_from_resource (GtkBuilderScope *scope,
const char *resource_path)
{
g_return_val_if_fail (scope == NULL || GTK_IS_BUILDER_SCOPE (scope), NULL);
g_return_val_if_fail (resource_path != NULL, NULL);
return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
"resource", resource_path,
"scope", scope,
NULL);
}
/**
* gtk_builder_list_item_factory_get_bytes: (attributes org.gtk.Method.get_property=bytes)
* @self: a `GtkBuilderListItemFactory`
*
* Gets the data used as the `GtkBuilder` UI template for constructing
* listitems.
*
* Returns: (transfer none): The `GtkBuilder` data
*/
GBytes *
gtk_builder_list_item_factory_get_bytes (GtkBuilderListItemFactory *self)
{
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
return self->bytes;
}
/**
* gtk_builder_list_item_factory_get_resource: (attributes org.gtk.Method.get_property=resource)
* @self: a `GtkBuilderListItemFactory`
*
* If the data references a resource, gets the path of that resource.
*
* Returns: (transfer none) (nullable): The path to the resource
*/
const char *
gtk_builder_list_item_factory_get_resource (GtkBuilderListItemFactory *self)
{
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
return self->resource;
}
/**
* gtk_builder_list_item_factory_get_scope: (attributes org.gtk.Method.get_property=scope)
* @self: a `GtkBuilderListItemFactory`
*
* Gets the scope used when constructing listitems.
*
* Returns: (transfer none) (nullable): The scope used when constructing listitems
*/
GtkBuilderScope *
gtk_builder_list_item_factory_get_scope (GtkBuilderListItemFactory *self)
{
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
return self->scope;
}