/*
* Copyright (C) 2009 Canonical, Ltd.
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* version 3.0 as published by the Free Software Foundation.
*
* 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 version 3.0 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* .
*
* Authored by:
* Neil Jagdish Patel
* Mikkel Kamstrup Erlandsen
*
* NB: Inspiration for column storage taken from GtkListStore
* API inspired by ClutterModel by Matthew Allumn
* Neil Patel
* Emmanuele Bassi
*/
/**
* SECTION:dee-model
* @short_description: A generic table model interface
* @include: dee.h
*
* #DeeModel is a generic table model that can holds #GVariants as
* column values. Each column is restricted to hold variants with some
* predefined type signature. This is known as the
* column schema.
*
*
* Indexes - Access by Key or Full Text Analysis
*
* Instead of forcing you to search the rows and columns for given values
* or patterns #DeeModel is integrated with a powerful #DeeIndex that allows
* you to create custom indexes over the model content that are updated
* automatically as the model changes.
*
*
* Indexes can be created for integer keys, string keys (fx. URIs), or for
* full text search into the model contents. The indexing API is flexible
* and extensible and can provide huge optimizations in terms of access times
* if you find yourself iterating over the model searching for something.
*
*
*
*
* Sorting
*
* As a simpler alternative to using indexes you can rely on sorted models.
* This is done by using the dee_model_insert_sorted() and
* dee_model_find_sorted() family of APIs. Some model classes have
* accelerated implementations of sorted inserts and lookups.
* Notably #DeeSequenceModel.
*
*
*
*
* Tags - Attach Arbitrary Data to Rows
*
* It's a very common pattern that you want to render a #DeeModel into some
* view in a classinc MVC pattern. If the view needs to reflect changes in the
* model dynamically you often find yourself creating ad-hoc mappings between
* the rows of the model and the widgets in your view.
*
*
* In situations where you need to pair the rows in a model with some external
* data structure the tags API may come in handy.
* It consists of the functions dee_model_register_tag(), dee_model_set_tag(),
* dee_model_get_tag(), and dee_model_clear_tag().
*
*
*
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include "dee-model.h"
#include "dee-marshal.h"
#include "trace-log.h"
typedef DeeModelIface DeeModelInterface;
G_DEFINE_INTERFACE (DeeModel, dee_model, G_TYPE_OBJECT)
enum
{
/* Public signals */
DEE_MODEL_SIGNAL_ROW_ADDED,
DEE_MODEL_SIGNAL_ROW_REMOVED,
DEE_MODEL_SIGNAL_ROW_CHANGED,
DEE_MODEL_SIGNAL_CHANGESET_STARTED,
DEE_MODEL_SIGNAL_CHANGESET_FINISHED,
DEE_MODEL_LAST_SIGNAL
};
static guint32 dee_model_signals[DEE_MODEL_LAST_SIGNAL] = { 0 };
#define CHECK_SCHEMA(self,out_num_cols,return_expression) \
if (G_UNLIKELY (dee_model_get_schema (self, out_num_cols) == NULL)) \
{ \
g_critical ("The model %s@%p doesn't have a schema", \
G_OBJECT_TYPE_NAME (self), self); \
return_expression; \
}
static void dee_model_set_schema_valist (DeeModel *self,
va_list *args);
static void dee_model_set_column_names_valist (DeeModel *self,
const gchar *first_column_name,
va_list *args);
static DeeModelIter* dee_model_append_valist (DeeModel *self,
va_list *args);
static DeeModelIter* dee_model_prepend_valist (DeeModel *self,
va_list *args);
static DeeModelIter* dee_model_insert_valist (DeeModel *self,
guint pos,
va_list *args);
static DeeModelIter* dee_model_insert_before_valist (DeeModel *self,
DeeModelIter *iter,
va_list *args);
static void dee_model_set_valist (DeeModel *self,
DeeModelIter *iter,
va_list *args);
static void dee_model_get_valist (DeeModel *self,
DeeModelIter *iter,
va_list args);
static GVariant** dee_model_build_row_valist (DeeModel *self,
GVariant **out_row_members,
va_list *args);
/*
* We provide here a couple of DeeModelIter functions, so that they're usable
* from introspected languages.
*/
static gpointer
dee_model_iter_copy (gpointer boxed)
{
/* FIXME: this implementation will work fine with DeeSequenceModel, but what
* about others? */
return boxed;
}
static void
dee_model_iter_free (gpointer boxed)
{
}
GType dee_model_iter_get_type (void)
{
static GType dee_model_iter_type = 0;
if (dee_model_iter_type == 0)
{
dee_model_iter_type = g_boxed_type_register_static ("DeeModelIter",
dee_model_iter_copy,
dee_model_iter_free);
}
return dee_model_iter_type;
}
static void
dee_model_default_init (DeeModelInterface *klass)
{
/**
* DeeModel::row-added:
* @self: the #DeeModel on which the signal is emitted
* @iter: (transfer none) (type Dee.ModelIter): a #DeeModelIter pointing to the newly added row
*
* Connect to this signal to be notified when a row is added to @self.
**/
dee_model_signals[DEE_MODEL_SIGNAL_ROW_ADDED] =
g_signal_new ("row-added",
DEE_TYPE_MODEL,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (DeeModelIface,row_added),
NULL, NULL,
g_cclosure_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
DEE_TYPE_MODEL_ITER);
/**
* DeeModel::row-removed:
* @self: the #DeeModel on which the signal is emitted
* @iter: (transfer none) (type Dee.ModelIter): a #DeeModelIter pointing to the removed row
*
* Connect to this signal to be notified when a row is removed from @self.
* The row is still valid while the signal is being emitted.
**/
dee_model_signals[DEE_MODEL_SIGNAL_ROW_REMOVED] =
g_signal_new ("row-removed",
DEE_TYPE_MODEL,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (DeeModelIface,row_removed),
NULL, NULL,
g_cclosure_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
DEE_TYPE_MODEL_ITER);
/**
* DeeModel::row-changed:
* @self: the #DeeModel on which the signal is emitted
* @iter: (transfer none) (type Dee.ModelIter): a #DeeModelIter pointing to the changed row
*
* Connect to this signal to be notified when a row is changed.
**/
dee_model_signals[DEE_MODEL_SIGNAL_ROW_CHANGED] =
g_signal_new ("row-changed",
DEE_TYPE_MODEL,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (DeeModelIface,row_changed),
NULL, NULL,
g_cclosure_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
DEE_TYPE_MODEL_ITER);
/**
* DeeModel::changeset-started
* @self: the #DeeModel on which the signal is emitted
*
* Connect to this signal to be notified when a changeset that can contain
* multiple row additions / changes / removals is about to be committed
* to the model.
* Note that not all model implementations use the changeset approach and
* you might still get a row change signal outside of changeset-started and
* changeset-finished signals. It also isn't guaranteed that a changeset
* would always be non-empty.
*/
dee_model_signals[DEE_MODEL_SIGNAL_CHANGESET_STARTED] =
g_signal_new ("changeset-started",
DEE_TYPE_MODEL,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (DeeModelIface, changeset_started),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* DeeModel::changeset-finished
* @self: the #DeeModel on which the signal is emitted
*
* Connect to this signal to be notified when a changeset that can contain
* multiple row additions / changes / removals has been committed
* to the model.
*/
dee_model_signals[DEE_MODEL_SIGNAL_CHANGESET_FINISHED] =
g_signal_new ("changeset-finished",
DEE_TYPE_MODEL,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (DeeModelIface, changeset_finished),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
/**
* dee_model_set_schema:
* @self: The #DeeModel to set the column layout for
* @VarArgs: A list of #GVariant type strings terminated by a %NULL
*
* Set the #GVariant types and the number of columns used by @self.
* This method must be called exactly once before using @self. Note that
* some constructors will do this for you.
*
* To create a model with three columns; a 32 bit integer, a string,
* and lastly an array of strings, you would do:
*
* DeeModel *model;
* model = dee_sequence_model_new ();
* dee_model_set_schema (model, "i", "s", "as", NULL);
*
*/
void
dee_model_set_schema (DeeModel *self,
...)
{
va_list args;
g_return_if_fail (DEE_IS_MODEL (self));
va_start (args, self);
dee_model_set_schema_valist(self, &args);
va_end (args);
}
/**
* dee_model_set_schema_valist: (skip)
* @self: The #DeeModel to change
* @VarArgs: A list of #GVariant type strings terminated by a %NULL
*
* Like dee_model_set_schema() but for language bindings.
*/
static void
dee_model_set_schema_valist (DeeModel *self,
va_list *args)
{
DeeModelIface *iface;
GSList *columns, *iter;
const gchar *column_schema;
gchar **column_schemas;
guint n_columns, i;
g_return_if_fail (DEE_IS_MODEL (self));
/* Extract and validate the column schema strings from the va_list */
column_schema = va_arg (*args, const gchar*);
n_columns = 0;
columns = NULL;
while (column_schema != NULL)
{
if (!g_variant_type_string_is_valid (column_schema))
{
g_critical ("When setting schema for DeeModel %p: '%s' is not a "
"valid GVariant type string", self, column_schema);
return;
}
columns = g_slist_prepend (columns, g_strdup (column_schema));
column_schema = va_arg (*args, const gchar*);
n_columns++;
}
/* Construct a string array with the validated column schemas */
columns = g_slist_reverse (columns);
column_schemas = g_new0 (gchar*, n_columns + 1);
for ((i = 0, iter = columns); iter; (i++, iter = iter->next))
{
column_schemas[i] = iter->data; // steal the duped type string
}
#ifdef ENABLE_TRACE_LOG
gchar* schema = g_strjoinv (", ", column_schemas);
trace_object (self, "Set schema: (%s)", schema);
g_free (schema);
#endif
iface = DEE_MODEL_GET_IFACE (self);
(* iface->set_schema_full) (self, (const gchar**) column_schemas, n_columns);
g_slist_free (columns);
g_strfreev (column_schemas);
}
/**
* dee_model_set_schema_full:
* @self: The #DeeModel to set the column layout for
* @column_schemas: (array length=num_columns zero-terminated=1) (element-type utf8) (transfer none): A list of #GVariant type strings terminated by a %NULL
* @num_columns: an integer specifying the array length for @VarArgs
*
* Set the #GVariant types and the number of columns used by @self.
* This method must be called exactly once before using @self. Note that
* some constructors will do this for you.
*/
void
dee_model_set_schema_full (DeeModel *self,
const gchar* const *column_schemas,
guint num_columns)
{
DeeModelIface *iface;
g_return_if_fail (DEE_IS_MODEL (self));
if (dee_model_get_schema (self, NULL) != NULL)
{
g_critical ("The model %s@%p already has a schema set",
G_OBJECT_TYPE_NAME (self), self);
return;
}
iface = DEE_MODEL_GET_IFACE (self);
(* iface->set_schema_full) (self, column_schemas, num_columns);
}
void
dee_model_set_column_names (DeeModel *self, const gchar *first_column_name,
...)
{
va_list args;
g_return_if_fail (DEE_IS_MODEL (self));
va_start (args, first_column_name);
dee_model_set_column_names_valist (self, first_column_name, &args);
va_end (args);
}
static void
dee_model_set_column_names_valist (DeeModel *self,
const gchar *first_column_name,
va_list *args)
{
DeeModelIface *iface;
const gchar **column_names;
guint n_columns, i;
g_return_if_fail (DEE_IS_MODEL (self));
n_columns = dee_model_get_n_columns (self);
g_return_if_fail (n_columns != 0);
column_names = g_alloca (sizeof (gchar*) * n_columns);
column_names[0] = first_column_name;
i = 1;
/* Extract and validate the column schema strings from the va_list */
while (i < n_columns)
{
gchar *name = va_arg (*args, gchar*);
column_names[i++] = name;
if (name == NULL) break;
}
iface = DEE_MODEL_GET_IFACE (self);
(* iface->set_column_names_full) (self, column_names, i);
}
/**
* dee_model_set_column_names_full:
* @self: A #DeeModel.
* @column_names: (array length=num_columns zero-terminated=1) (element-type utf8) (transfer none): A list of column names terminated by a %NULL
* @num_columns: an integer specifying the array length for @annotations
*
* Set column names used by @self.
* This method must be called exactly once, but only after setting
* a schema of the model. Note that some constructors will do this for you.
*/
void
dee_model_set_column_names_full (DeeModel *self,
const gchar **column_names,
guint num_columns)
{
DeeModelIface *iface;
g_return_if_fail (DEE_IS_MODEL (self));
if (dee_model_get_schema (self, NULL) == NULL)
{
g_critical ("The model %s@%p doesn't have a schema set",
G_OBJECT_TYPE_NAME (self), self);
return;
}
iface = DEE_MODEL_GET_IFACE (self);
(* iface->set_column_names_full) (self, column_names, num_columns);
}
/**
* dee_model_get_column_names:
* @self: The #DeeModel to get the the schema for
* @num_columns: (out) (allow-none): Address of an integer in which to store the
* number of columns in @self. Or %NULL to ignore the array length.
*
* Get a %NULL-terminated array of column names for the columns of @self.
* These names can be used in calls to dee_model_build_named_row().
*
* Returns: (array length=num_columns) (element-type utf8) (transfer none):
* A %NULL-terminated array of #GVariant type strings. The length of
* the returned array is written to @num_columns. The returned array
* should not be freed or modified. It is owned by the model.
*/
const gchar**
dee_model_get_column_names (DeeModel *self,
guint *num_columns)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_column_names) (self, num_columns);
}
/**
* dee_model_get_schema:
* @self: The #DeeModel to get the the schema for
* @num_columns: (out) (allow-none): Address of an integer in which to store the
* number of columns in @self. Or %NULL to ignore the array length.
*
* Get a %NULL-terminated array of #GVariant type strings that defines the
* required formats for the columns of @self.
*
* Returns: (array length=num_columns) (element-type utf8) (transfer none):
* A %NULL-terminated array of #GVariant type strings. The length of
* the returned array is written to @num_columns. The returned array
* should not be freed or modified. It is owned by the model.
*/
const gchar* const*
dee_model_get_schema (DeeModel *self,
guint *num_columns)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_schema) (self, num_columns);
}
/**
* dee_model_get_column_schema:
* @self: a #DeeModel
* @column: the column to get retrieve the #GVariant type string of
*
* Get the #GVariant signature of a column
*
* Return value: the #GVariant signature of the column at index @column
*/
const gchar*
dee_model_get_column_schema (DeeModel *self,
guint column)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_column_schema) (self, column);
}
/**
* dee_model_get_field_schema:
* @self: a #DeeModel
* @field_name: name of vardict field to get schema of
* @out_column: (out): column index of the associated vardict
*
* Get the #GVariant signature of field previously registered with
* dee_model_register_vardict_schema().
*
* Return value: the #GVariant signature for the field, or %NULL if given field
* wasn't registered with dee_model_register_vardict_schema().
*/
const gchar*
dee_model_get_field_schema (DeeModel *self,
const gchar *field_name,
guint *out_column)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_field_schema) (self, field_name, out_column);
}
/**
* dee_model_get_column_index:
* @self: a #DeeModel
* @column_name: the column name to retrieve the index of
*
* Get the column index of a column.
*
* Return value: 0-based index of the column or -1 if column with this name
* wasn't found
*/
gint
dee_model_get_column_index (DeeModel *self,
const gchar *column_name)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), -1);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_column_index) (self, column_name);
}
/**
* dee_model_register_vardict_schema:
* @self: a #DeeModel
* @column: the column index to register the schemas with
* @schemas: (element-type utf8 utf8): hashtable with keys specifying
* names of the fields and values defining their schema
*
* Register schema for fields in a model containing column with variant
* dictionary schema ('a{sv}').
* The keys registered with this function can be later used
* with dee_model_build_named_row() function, as well as
* dee_model_get_value_by_name(). Note that it is possible to register
* the same field name for multiple columns, in which case you need to use
* fully-qualified "column_name::field" name in the calls to
* dee_model_build_named_row() and dee_model_get_field_schema().
*/
void
dee_model_register_vardict_schema (DeeModel *self,
guint column,
GHashTable *schemas)
{
DeeModelIface *iface;
g_return_if_fail (DEE_IS_MODEL (self));
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->register_vardict_schema) (self, column, schemas);
}
/**
* dee_model_get_vardict_schema:
* @self: a #DeeModel
* @column: the column index to get the schemas for
*
* Get a schema for variant dictionary column previously registered using
* dee_model_register_vardict_schema().
*
* Return value: (transfer container) (element-type utf8 utf8): Hashtable
* containing a mapping from field names to schemas or NULL.
* Note that keys and values in the hashtable may be owned
* by the model, so you need to create a deep copy if you
* intend to keep the hashtable around.
*/
GHashTable*
dee_model_get_vardict_schema (DeeModel *self, guint column)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_vardict_schema) (self, column);
}
/**
* dee_model_get_n_columns:
* @self: a #DeeModel
*
* Gets the number of columns in @self
*
* Return value: the number of columns per row in @self
**/
guint
dee_model_get_n_columns (DeeModel *self)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), 0);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_n_columns) (self);
}
/**
* dee_model_get_n_rows:
* @self: a #DeeModel
*
* Gets the number of rows in @self
*
* Return value: the number of rows in @self
**/
guint
dee_model_get_n_rows (DeeModel *self)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), 0);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_n_rows) (self);
}
/**
* dee_model_begin_changeset:
* @self: a #DeeModel
*
* Notify listeners that the model is about to be changed, which means that
* multiple row additions / changes / removals will follow.
* The default implementation of this method will emit
* the ::changeset-started signal.
*
* It is not stricly necessary to enclose every change to a model
* in a dee_model_begin_changeset() and dee_model_end_changeset() calls, but
* doing so is highly recommended and allows implementing various optimizations.
*
* The usual way to perform multiple changes to a model is as follows:
*
*
* void update_model (DeeModel *model)
* {
* GVariant **added_row_data1 = ...;
* GVariant **added_row_data2 = ...;
*
* dee_model_begin_changeset (model);
*
* dee_model_remove (model, dee_model_get_first_iter (model));
* dee_model_append_row (model, added_row_data1);
* dee_model_append_row (model, added_row_data2);
*
* dee_model_end_changeset (model);
* }
*
*/
void
dee_model_begin_changeset (DeeModel *self)
{
DeeModelIface *iface;
g_return_if_fail (DEE_IS_MODEL (self));
iface = DEE_MODEL_GET_IFACE (self);
if (iface->begin_changeset)
(* iface->begin_changeset) (self);
else
g_signal_emit (self, dee_model_signals[DEE_MODEL_SIGNAL_CHANGESET_STARTED], 0);
}
/**
* dee_model_end_changeset:
* @self: a #DeeModel
*
* Notify listeners that all changes have been committed to the model.
* The default implementation of this method will emit
* the ::changeset-finished signal.
*
* See also dee_model_begin_changeset().
*/
void
dee_model_end_changeset (DeeModel *self)
{
DeeModelIface *iface;
g_return_if_fail (DEE_IS_MODEL (self));
iface = DEE_MODEL_GET_IFACE (self);
if (iface->end_changeset)
(* iface->end_changeset) (self);
else
g_signal_emit (self, dee_model_signals[DEE_MODEL_SIGNAL_CHANGESET_FINISHED], 0);
}
static GVariant*
collect_variant (const gchar* col_schema, va_list *args)
{
const gchar *col_string;
GVariant *result;
if (g_variant_type_is_basic (G_VARIANT_TYPE (col_schema)))
{
switch (col_schema[0])
{
case 's':
case 'o':
case 'g':
col_string = va_arg (*args, const gchar*);
result = g_variant_new (col_schema, col_string ? col_string : "");
break;
default:
result = g_variant_new_va (col_schema, NULL, args);
}
}
else
result = va_arg (*args, GVariant*);
return result;
}
/**
* dee_model_build_row:
* @self: The model to create a row for
* @out_row_members: An array to write the values to or %NULL to allocate
* a new array. If non-%NULL it must have a length
* that is longer or equal to the number of columns in @self
* @VarArgs: A list with values matching the column schemas of @self.
* Basic variant types are passed directly while any other
* types must be boxed in a #GVariant. It's important to note that
* any floating references on variants passed to this method will be
* not be consumed. A %NULL value for a string
* type column will be converted to the empty string.
*
* Build an array of #GVariants with values from the variadic argument
* list according to the model schema for @self. The caller must call
* g_variant_ref_sink() and g_variant_unref() on all the returned variants and
* g_free() the array itself if %NULL was passed as @out_row_members.
*
* This is utility function and will not touch or modify @self in any way.
*
* Returns: If @out_row_members is %NULL a newly allocated array of variants
* will be returned and the array must be freed with g_free().
* If @out_row_members is non-%NULL it will be reused, and variants in
* the array may or may not have floating references, which means the
* caller must make sure that g_variant_ref_sink() and
* g_variant_unref() are called on them.
*
*/
GVariant**
dee_model_build_row (DeeModel *self,
GVariant **out_row_members,
...)
{
va_list args;
GVariant **result;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, NULL, return NULL);
va_start (args, out_row_members);
result = dee_model_build_row_valist (self, out_row_members, &args);
va_end (args);
return result;
}
/**
* dee_model_build_row_valist: (skip):
* @self: The model to build a row for
* @out_row_members: An array to write the values to or %NULL to allocate
* a new array
* @args: A %va_list of arguments as described in dee_model_build_row()
*
* Like dee_model_build_row() but intended for language bindings.
*
* Returns: See dee_model_build_row()
*/
static GVariant**
dee_model_build_row_valist (DeeModel *self,
GVariant **out_row_members,
va_list *args)
{
guint i, n_cols;
const gchar *col_schema;
const gchar *const *schema;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
schema = dee_model_get_schema (self, &n_cols);
if (out_row_members == NULL)
out_row_members = g_new0 (GVariant*, n_cols);
for (i = 0; i < n_cols; i++)
{
col_schema = schema[i];
out_row_members[i] = collect_variant (col_schema, args);
if (G_UNLIKELY (out_row_members[i] == NULL))
{
g_critical ("Trying to build a row with a NULL member on position"
" %i. This is probably an error in an application using"
" libdee", i);
return NULL;
}
}
return out_row_members;
}
/**
* dee_model_build_named_row:
* @self: The model to create a row for
* @out_row_members: An array to write the values to or %NULL to allocate
* a new array. If non-%NULL it must have a length
* that is longer or equal to the number of columns in @self
* @first_column_name: A column name
* @VarArgs: Value for given column, followed by more name/value pairs,
* followed by %NULL. The passed names have to match the column names
* (or field names registered with
* dee_model_register_vardict_schema()) and values have to be set
* according to schema of the given column or field.
* Basic variant types are passed directly while any other types
* must be boxed in a #GVariant, similar to dee_model_build_row().
*
* Build an array of #GVariants with values from the variadic argument
* list according to the column names and model schema for @self.
* The caller must call g_variant_ref_sink() and g_variant_unref()
* on all the returned variants and g_free() the array itself if %NULL
* was passed as @out_row_members.
*
* This is utility function and will not touch or modify @self in any way.
*
* For example, to append a row to model with signature ("s", "u", "s") and
* column names set to ("uri", "count", "description") you could do:
*
* GVariant *row_buf[3];
*
* dee_model_append_row (model,
* dee_model_build_named_row (model, row_buf,
* "uri", "http://example.org",
* "count", 435,
* "description", "Example.org site", NULL));
*
*
* Returns: If @out_row_members is %NULL a newly allocated array of variants
* will be returned and the array must be freed with g_free().
* If @out_row_members is non-%NULL it will be reused, and variants in
* the array may or may not have floating references, which means the
* caller must make sure that g_variant_ref_sink() and
* g_variant_unref() are called on them.
*
*/
GVariant**
dee_model_build_named_row (DeeModel *self,
GVariant **out_row_members,
const gchar *first_column_name,
...)
{
va_list args;
GVariant **result;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, NULL, return NULL);
va_start (args, first_column_name);
result = dee_model_build_named_row_valist (self, out_row_members,
first_column_name, &args);
va_end (args);
return result;
}
/**
* dee_model_build_named_row_sunk:
* @self: The model to create a row for
* @out_row_members: (array): An array to write the values to or %NULL to
* allocate a new array. If non-%NULL it must have a length
* that is longer or equal to the number of columns in @self
* @out_array_length: (out): Length of the returned variant array
* @first_column_name: A column name
* @VarArgs: Value for given column, followed by more name/value pairs,
* followed by %NULL. The passed names have to match the column names
* and values have to be set according to schema of @self.
* Basic variant types are passed directly while any other types
* must be boxed in a #GVariant, similar to dee_model_build_row().
*
* Version of dee_model_build_named_row() for language bindings - as opposed to
* dee_model_build_named_row(), the returned variants will be strong
* references, therefore you always have to call g_variant_unref() on the items
* and g_free() the array itself if %NULL was passed as @out_row_members.
*
* If @out_row_members is non-%NULL, g_variant_unref() will be called
* on its elements (if also non-%NULL), which allows easy reuse of the array
* memory in loops.
*
* This is utility function and will not touch or modify @self in any way.
*
* Example of memory management for model with schema ("s", "i") and
* column names ("uri", "count"):
*
* GVariant **row_buf;
*
* row_buf = dee_model_build_named_row_sunk (model, NULL, "uri", "file:///",
* "count", 0, NULL);
* dee_model_append_row (model, row_buf);
*
* for (int i = 1; i < 100; i++)
* {
* dee_model_append_row (model,
* dee_model_build_named_row_sunk (model, row_buf, "uri", "file:///",
* "count", i, NULL));
* }
*
* g_variant_unref (row_buf[0]);
* g_variant_unref (row_buf[1]);
* g_free (row_buf);
*
*
* Returns: (array length=out_array_length): If @out_row_members is %NULL
* a newly allocated array of variants will be returned and the array
* must be freed with g_free().
* If @out_row_members is non-%NULL it will be reused. Variants in
* the array will have strong references, which means the
* caller must make sure that g_variant_unref() is called on them.
*
*/
GVariant**
dee_model_build_named_row_sunk (DeeModel *self,
GVariant **out_row_members,
guint *out_array_length,
const gchar *first_column_name,
...)
{
va_list args;
guint num_columns, i;
GVariant **result;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, &num_columns, return NULL);
if (out_row_members)
{
for (i = 0; i < num_columns; i++)
{
if (out_row_members[i]) g_variant_unref (out_row_members[i]);
}
}
va_start (args, first_column_name);
result = dee_model_build_named_row_valist (self, out_row_members,
first_column_name, &args);
va_end (args);
if (result)
{
for (i = 0; i < num_columns; i++)
{
g_variant_ref_sink (result[i]);
}
}
if (out_array_length)
*out_array_length = result != NULL ? num_columns : 0;
return result;
}
GVariant**
dee_model_build_named_row_valist (DeeModel *self,
GVariant **out_row_members,
const gchar *first_column_name,
va_list *args)
{
DeeModelIface *iface;
guint n_cols, i;
gint col_idx, last_unset_col;
gboolean *variant_set;
GVariantBuilder **builders;
const gchar *col_name, *col_schema;
const gchar *const *schema;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
schema = dee_model_get_schema (self, &n_cols);
if (out_row_members == NULL)
out_row_members = g_new0 (GVariant*, n_cols);
variant_set = g_alloca (n_cols * sizeof (gboolean));
memset (variant_set, 0, n_cols * sizeof (gboolean));
builders = g_alloca (n_cols * sizeof (GVariantBuilder*));
memset (builders, 0, n_cols * sizeof (GVariantBuilder*));
iface = DEE_MODEL_GET_IFACE (self);
col_name = first_column_name;
while (col_name != NULL)
{
col_idx = (* iface->get_column_index) (self, col_name);
if (col_idx >= 0)
{
col_schema = schema[col_idx];
out_row_members[col_idx] = collect_variant (col_schema, args);
if (G_UNLIKELY (out_row_members[col_idx] == NULL))
{
g_critical ("Trying to build a row with a NULL member for column"
" %s. This is probably an error in an application using"
" libdee", col_name);
break;
}
else
{
variant_set[col_idx] = TRUE;
}
}
else
{
// check if we have hints
col_schema = (* iface->get_field_schema) (self, col_name, (guint*) &col_idx);
if (col_schema != NULL)
{
const gchar *key_name;
if (builders[col_idx] == NULL)
{
builders[col_idx] = g_variant_builder_new (G_VARIANT_TYPE (schema[col_idx]));
}
key_name = strstr (col_name, "::");
key_name = key_name != NULL ? key_name + 2 : col_name;
g_variant_builder_add (builders[col_idx], "{sv}",
key_name,
collect_variant (col_schema, args));
}
else
{
g_warning ("Unable to find column index for \"%s\"", col_name);
/* need to break, cause there's no way to know size of the value */
break;
}
}
col_name = va_arg (*args, const gchar*);
}
/* Finish builders */
for (i = 0; i < n_cols; i++)
{
if (builders[i])
{
out_row_members[i] = g_variant_builder_end (builders[i]);
g_variant_builder_unref (builders[i]);
variant_set[i] = TRUE;
}
}
/* Check if all columns were set */
last_unset_col = -1;
for (i = 0; i < n_cols; i++)
{
if (!variant_set[i])
{
/* Create empty a{sv} if needed */
if (g_variant_type_is_subtype_of (G_VARIANT_TYPE (schema[i]),
G_VARIANT_TYPE_VARDICT))
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE (schema[i]));
out_row_members[i] = g_variant_builder_end (&builder);
variant_set[i] = TRUE;
}
else
{
last_unset_col = i;
}
}
}
if (last_unset_col >= 0)
{
/* Be nice and unref the variants we created */
for (i = 0; i < n_cols; i++)
{
if (variant_set[i])
{
g_variant_unref (g_variant_ref_sink (out_row_members[i]));
out_row_members[i] = NULL;
}
}
const gchar **names = dee_model_get_column_names (self, NULL);
g_critical ("Unable to build row: Column %d '%s' wasn't set",
last_unset_col, names ? names[last_unset_col] : "unnamed");
return NULL;
}
return out_row_members;
}
/**
* dee_model_append:
* @self: a #DeeModel
* @VarArgs: A list of values matching the column schemas of @self.
* Any basic variant type is passed as the standard C type while
* any other type must be boxed in a #GVariant. Any floating
* references will be consumed. A %NULL value for a string
* type column will be converted to the empty string.
*
* Creates and appends a new row to the end of a #DeeModel, setting the row
* values upon creation.
*
* For and example see dee_model_insert_before().
*
* Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row
*/
DeeModelIter*
dee_model_append (DeeModel *self,
...)
{
DeeModelIter *iter;
va_list args;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
va_start (args, self);
iter = dee_model_append_valist (self, &args);
va_end (args);
return iter;
}
/**
* dee_model_append_valist: (skip):
* @self: A #DeeModel
* @args: A pointer to a variable argument list
*
* Returns: A #DeeModelIter pointing to the new row
*/
static DeeModelIter*
dee_model_append_valist (DeeModel *self,
va_list *args)
{
DeeModelIface *iface;
DeeModelIter *iter;
GVariant **row_members;
guint num_columns;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, &num_columns, return NULL);
iface = DEE_MODEL_GET_IFACE (self);
row_members = g_alloca (num_columns * sizeof (gpointer));
dee_model_build_row_valist (self, row_members, args);
iter = (* iface->append_row) (self, row_members);
return iter;
}
/**
* dee_model_append_row:
* @self: The model to prepend a row to
* @row_members: (array zero-terminated=1): An array of #GVariants with type
* signature matching those of the column schemas of @self.
* If any of the variants have floating references they will be
* consumed
*
* Like dee_model_append() but intended for language bindings or
* situations where you work with models on a meta level and may not have
* a prior knowledge of the column schemas of the models. See also
* dee_model_build_row().
*
* Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row
*/
DeeModelIter*
dee_model_append_row (DeeModel *self,
GVariant **row_members)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, NULL, return NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->append_row) (self, row_members);
}
/**
* dee_model_prepend:
* @self: a #DeeModel
* @VarArgs: A list of values matching the column schemas of @self.
* Any basic variant type is passed as the standard C type while
* any other type must be boxed in a #GVariant. Any floating
* references will be consumed. A %NULL value for a string
* type column will be converted to the empty string.
*
* Creates and prepends a new row to the beginning of a #DeeModel, setting the
* row values upon creation.
*
* Example:
*
*
* DeeModel *model;
* model = ...
* dee_model_set_schema (model, "i", "s", NULL);
* dee_model_prepend (model, 10, "Rooney");
*
*
* Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row
*/
DeeModelIter*
dee_model_prepend (DeeModel *self,
...)
{
DeeModelIter *iter;
va_list args;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
va_start (args, self);
iter = dee_model_prepend_valist (self, &args);
va_end (args);
return iter;
}
/**
* dee_model_prepend_valist: (skip):
* @self: A #DeeModel
* @args: A pointer to a variable argument list
*
* Returns: A #DeeModelIter pointing to the new row
*/
static DeeModelIter*
dee_model_prepend_valist (DeeModel *self,
va_list *args)
{
DeeModelIface *iface;
DeeModelIter *iter;
GVariant **row_members;
guint num_columns;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, &num_columns, return NULL);
iface = DEE_MODEL_GET_IFACE (self);
row_members = g_alloca (num_columns * sizeof (gpointer));
dee_model_build_row_valist (self, row_members, args);
iter = (* iface->prepend_row) (self, row_members);
return iter;
}
/**
* dee_model_prepend_row:
* @self: The model to prepend a row to
* @row_members: (array zero-terminated=1): An array of
* #GVariants with type signature matching those of
* the column schemas of @self. If any of the variants have
* floating references they will be consumed.
*
* Like dee_model_prepend() but intended for language bindings or
* situations where you work with models on a meta level and may not have
* a priori knowledge of the column schemas of the models. See also
* dee_model_build_row().
*
* Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row
*/
DeeModelIter*
dee_model_prepend_row (DeeModel *self,
GVariant **row_members)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, NULL, return NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->prepend_row) (self, row_members);
}
/**
* dee_model_insert:
* @self: a #DeeModel
* @pos: The index to insert the row on. The existing row will be pushed down
* @VarArgs: A list of values matching the column schemas of @self.
* Any basic variant type is passed as the standard C type while
* any other type must be boxed in a #GVariant. Any floating
* references will be consumed. A %NULL value for a string
* type column will be converted to the empty string.
*
* Creates and inserts a new row into a #DeeModel, pushing the existing
* rows down.
*
* For and example see dee_model_insert_before().
*
* Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row
*/
DeeModelIter*
dee_model_insert (DeeModel *self,
guint pos,
...)
{
DeeModelIter *iter;
va_list args;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
va_start (args, pos);
iter = dee_model_insert_valist (self, pos, &args);
va_end (args);
return iter;
}
/**
* dee_model_insert_valist: (skip):
* @self: A #DeeModel
* @pos: The index to insert the row on. The existing row will be pushed down
* @args: A pointer to a variable argument list
*
* Returns: A #DeeModelIter pointing to the new row
*/
static DeeModelIter*
dee_model_insert_valist (DeeModel *self,
guint pos,
va_list *args)
{
DeeModelIface *iface;
DeeModelIter *iter;
GVariant **row_members;
guint num_columns;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, &num_columns, return NULL);
iface = DEE_MODEL_GET_IFACE (self);
row_members = g_alloca (num_columns * sizeof (gpointer));
dee_model_build_row_valist (self, row_members, args);
iter = (* iface->insert_row) (self, pos, row_members);
return iter;
}
/**
* dee_model_insert_row:
* @self: a #DeeModel
* @pos: The index to insert the row on. The existing row will be pushed down.
* @row_members: (array zero-terminated=1): An array of
* #GVariants with type signature matching those of
* the column schemas of @self. If any of the variants have
* floating references they will be consumed.
*
* As dee_model_insert(), but intended for language bindings or
* situations where you work with models on a meta level and may not have
* a priori knowledge of the column schemas of the models. See also
* dee_model_build_row().
*
* Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row
*/
DeeModelIter*
dee_model_insert_row (DeeModel *self,
guint pos,
GVariant **row_members)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, NULL, return NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->insert_row) (self, pos, row_members);
}
/**
* dee_model_insert_before:
* @self: a #DeeModel
* @iter: An iter pointing to the row before which to insert the new one
* @VarArgs: A list of values matching the column schemas of @self.
* Any basic variant type is passed as the standard C type while
* any other type must be boxed in a #GVariant. Any floating
* references will be consumed. A %NULL value for a string
* type column will be converted to the empty string.
*
* Creates and inserts a new row into a #DeeModel just before the row pointed
* to by @iter.
*
* For example, to insert a new row in a model with schema ("u", "s", "as")
* you would do:
*
*
* DeeModelIter *iter;
* GVariantBuilder b;
*
* g_variant_builder_init (&b, "as");
* g_variant_builder_add (&b, "s", "Hello");
* g_variant_builder_add (&b, "s", "World");
*
* iter = find_my_special_row (model);
* dee_model_insert_before (model, iter,
* 27,
* "Howdy",
* g_variant_builder_end (&b));
*
*
* Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row
*/
DeeModelIter*
dee_model_insert_before (DeeModel *self,
DeeModelIter *iter,
...)
{
va_list args;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
va_start (args, iter);
iter = dee_model_insert_before_valist (self, iter, &args);
va_end (args);
return iter;
}
/**
* dee_model_insert_before_valist: (skip):
* @self: a #DeeModel
* @iter: An iter pointing to the row before which to insert the new one
* @args: See dee_model_insert_before()
*
* As dee_model_insert_before(), but intended for language bindings.
*
* Returns: A #DeeModelIter pointing to the new row
*/
static DeeModelIter*
dee_model_insert_before_valist (DeeModel *self,
DeeModelIter *iter,
va_list *args)
{
DeeModelIface *iface;
GVariant **row_members;
guint num_columns;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, &num_columns, return NULL);
iface = DEE_MODEL_GET_IFACE (self);
row_members = g_alloca (num_columns * sizeof (gpointer));
dee_model_build_row_valist (self, row_members, args);
iter = (* iface->insert_row_before) (self, iter, row_members);
return iter;
}
/**
* dee_model_insert_row_before:
* @self: a #DeeModel
* @iter: An iter pointing to the row before which to insert the new one
* @row_members: (array zero-terminated=1): An array of
* #GVariants with type signature matching those of the
* column schemas of @self. If any of the variants have floating
* references they will be consumed.
*
* As dee_model_insert_before(), but intended for language bindings or
* situations where you work with models on a meta level and may not have
* a priori knowledge of the column schemas of the models. See also
* dee_model_build_row().
*
* Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row
**/
DeeModelIter*
dee_model_insert_row_before (DeeModel *self,
DeeModelIter *iter,
GVariant **row_members)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, NULL, return NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->insert_row_before) (self, iter, row_members);
}
/* Translates DeeCompareRowFunc callback into DeeCompareRowSizedFunc */
static gint
dee_model_cmp_func_translate_func (GVariant **row1,
GVariant **row2,
gpointer data)
{
gpointer *all_data = (gpointer*) data;
DeeCompareRowSizedFunc cmp_func = (DeeCompareRowSizedFunc) all_data[0];
gpointer user_data = all_data[1];
guint array_length = GPOINTER_TO_UINT (all_data[2]);
return cmp_func (row1, array_length, row2, array_length, user_data);
}
/**
* dee_model_insert_row_sorted:
* @self: The model to do a sorted insert on
* @row_members: (array zero-terminated=1): An array of
* #GVariants with type signature matching those of the
* column schemas of @self. If any of the variants have floating
* references they will be consumed.
* @cmp_func: (scope call): Callback used for comparison or rows
* @user_data: (closure): Arbitrary pointer passed to @cmp_func during search
*
* Inserts a row in @self according to the sorting specified by @cmp_func.
* If you use this method for insertion you should not use other methods as this
* method assumes the model to be already sorted by @cmp_func.
*
* Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row
*/
DeeModelIter*
dee_model_insert_row_sorted (DeeModel *self,
GVariant **row_members,
DeeCompareRowFunc cmp_func,
gpointer user_data)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, NULL, return NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->insert_row_sorted) (self, row_members, cmp_func, user_data);
}
/**
* dee_model_insert_row_sorted_with_sizes:
* @self: The model to do a sorted insert on
* @row_members: (array zero-terminated=1): An array of
* #GVariants with type signature matching those of the
* column schemas of @self. If any of the variants have floating
* references they will be consumed.
* @cmp_func: (scope call): Callback used for comparison or rows
* @user_data: (closure): Arbitrary pointer passed to @cmp_func during search
*
* Inserts a row in @self according to the sorting specified by @cmp_func.
* If you use this method for insertion you should not use other methods as this
* method assumes the model to be already sorted by @cmp_func.
*
* Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row
*/
DeeModelIter*
dee_model_insert_row_sorted_with_sizes (DeeModel *self,
GVariant **row_members,
DeeCompareRowSizedFunc cmp_func,
gpointer user_data)
{
gpointer all_data[3];
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
all_data[0] = cmp_func;
all_data[1] = user_data;
all_data[2] = GUINT_TO_POINTER (dee_model_get_n_columns (self));
return dee_model_insert_row_sorted (self, row_members,
dee_model_cmp_func_translate_func,
all_data);
}
/**
* dee_model_insert_sorted:
* @self: The model to do a sorted insert on
* @cmp_func: (scope call): Callback used for comparison or rows
* @user_data: (closure): Arbitrary pointer passed to @cmp_func during search
* @VarArgs: Specifies the row to insert. A collection of #GVariants
* matching the number of columns @self
*
* Convenience function for calling dee_model_insert_row_sorted().
* Inserts a row in @self according to the sorting specified by @cmp_func.
* If you use this method for insertion you should not use other methods as this
* method assumes the model to be already sorted by @cmp_func.
*
* Returns: (transfer none) (type Dee.ModelIter): A #DeeModelIter pointing to the new row
*/
DeeModelIter*
dee_model_insert_sorted (DeeModel *self,
DeeCompareRowFunc cmp_func,
gpointer user_data,
...)
{
DeeModelIface *iface;
DeeModelIter *iter;
GVariant **row_members;
guint num_columns;
va_list args;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, &num_columns, return NULL);
iface = DEE_MODEL_GET_IFACE (self);
row_members = g_alloca (num_columns * sizeof (gpointer));
va_start (args, user_data);
dee_model_build_row_valist (self, row_members, &args);
va_end (args);
iter = (* iface->insert_row_sorted) (self, row_members, cmp_func, user_data);
return iter;
}
/**
* dee_model_find_row_sorted:
* @self: The model to search
* @row_spec: (array zero-terminated=1): An array of
* #GVariants with type signature matching those of the
* column schemas of @self. No references will be taken on the variants.
* @cmp_func: (scope call): Callback used for comparison or rows
* @user_data: (closure): Arbitrary pointer passed to @cmp_func during search
* @out_was_found: (out): A place to store a boolean value that will be set when
* this method returns. If %TRUE then an exact match was found.
* If %FALSE then the returned iter points to a row just after
* where @row_spec would have been inserted.
* Pass %NULL to ignore.
*
* Finds a row in @self according to the sorting specified by @cmp_func.
* This method will assume that @self is already sorted by @cmp_func.
*
* If you use this method for searching you should only use
* dee_model_insert_row_sorted() to insert rows in the model.
*
* Returns: (transfer none) (type Dee.ModelIter): If @out_was_found is set to
* %TRUE then a #DeeModelIter pointing to the last matching row.
* If it is %FALSE then the iter pointing to the row just after where
* @row_spec_would have been inserted.
*/
DeeModelIter*
dee_model_find_row_sorted (DeeModel *self,
GVariant **row_spec,
DeeCompareRowFunc cmp_func,
gpointer user_data,
gboolean *out_was_found)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, NULL, return NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->find_row_sorted) (self, row_spec, cmp_func,
user_data, out_was_found);
}
/**
* dee_model_find_row_sorted_with_sizes:
* @self: The model to search
* @row_spec: (array zero-terminated=1): An array of
* #GVariants with type signature matching those of the
* column schemas of @self. No references will be taken on the variants.
* @cmp_func: (scope call): Callback used for comparison or rows
* @user_data: (closure): Arbitrary pointer passed to @cmp_func during search
* @out_was_found: (out): A place to store a boolean value that will be set when
* this method returns. If %TRUE then an exact match was found.
* If %FALSE then the returned iter points to a row just after
* where @row_spec would have been inserted.
* Pass %NULL to ignore.
*
* Like dee_model_find_row_sorted(), but uses DeeCompareRowSizedFunc and
* therefore doesn't cause trouble when used from introspected languages.
*
* Finds a row in @self according to the sorting specified by @cmp_func.
* This method will assume that @self is already sorted by @cmp_func.
*
* If you use this method for searching you should only use
* dee_model_insert_row_sorted() (or dee_model_insert_row_sorted_with_sizes())
* to insert rows in the model.
*
* Returns: (transfer none) (type Dee.ModelIter): If @out_was_found is set to
* %TRUE then a #DeeModelIter pointing to the last matching row.
* If it is %FALSE then the iter pointing to the row just after where
* @row_spec_would have been inserted.
*/
DeeModelIter*
dee_model_find_row_sorted_with_sizes (DeeModel *self,
GVariant **row_spec,
DeeCompareRowSizedFunc cmp_func,
gpointer user_data,
gboolean *out_was_found)
{
gpointer all_data[3];
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
all_data[0] = cmp_func;
all_data[1] = user_data;
all_data[2] = GUINT_TO_POINTER (dee_model_get_n_columns (self));
return dee_model_find_row_sorted (self, row_spec,
dee_model_cmp_func_translate_func,
all_data, out_was_found);
}
/**
* dee_model_find_sorted:
* @self: The model to search
* @cmp_func: (scope call): Callback used for comparison or rows
* @user_data: (closure): Arbitrary pointer passed to @cmp_func during search
* @out_was_found: (out): A place to store a boolean value that will be set when
* this method returns. If %TRUE then an exact match was found.
* If %FALSE then the returned iter points to a row just after
* where @row_spec would have been inserted.
* Pass %NULL to ignore.
* @VarArgs: A sequence of variables with type signature matching those of the
* column schemas of @self.
*
* Finds a row in @self according to the sorting specified by @cmp_func.
* This method will assume that @self is already sorted by @cmp_func.
*
* If you use this method for searching you should only use
* dee_model_insert_row_sorted() to insert rows in the model.
*
* Returns: (transfer none) (type Dee.ModelIter): If @out_was_found is set to
* %TRUE then a #DeeModelIter pointing to the last matching row.
* If it is %FALSE then the iter pointing to the row just after where
* @row_spec_would have been inserted.
*/
DeeModelIter*
dee_model_find_sorted (DeeModel *self,
DeeCompareRowFunc cmp_func,
gpointer user_data,
gboolean *out_was_found,
...)
{
DeeModelIface *iface;
DeeModelIter *iter;
GVariant **row_members;
guint num_columns;
va_list args;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
CHECK_SCHEMA (self, &num_columns, return NULL);
iface = DEE_MODEL_GET_IFACE (self);
row_members = g_alloca (num_columns * sizeof (gpointer));
va_start (args, out_was_found);
dee_model_build_row_valist (self, row_members, &args);
va_end (args);
iter = (* iface->find_row_sorted) (self, row_members, cmp_func,
user_data, out_was_found);
return iter;
}
/**
* dee_model_remove:
* @self: a #DeeModel
* @iter: a #DeeModelIter pointing to the row to remove
*
* Removes the row at the given position from the model.
*/
void
dee_model_remove (DeeModel *self,
DeeModelIter *iter)
{
DeeModelIface *iface;
g_return_if_fail (DEE_IS_MODEL (self));
CHECK_SCHEMA (self, NULL, return);
iface = DEE_MODEL_GET_IFACE (self);
(* iface->remove) (self, iter);
}
/**
* dee_model_clear:
* @self: a #DeeModel object to clear
*
* Removes all rows in the model. Signals are emitted for each row in the model
*/
void
dee_model_clear (DeeModel *self)
{
DeeModelIface *iface;
g_return_if_fail (DEE_IS_MODEL (self));
CHECK_SCHEMA (self, NULL, return);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->clear) (self);
}
/**
* dee_model_set:
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @VarArgs: A list of values to set matching the column schemas.
* Any basic variant type is passed as the standard C type while
* any other type must be boxed in a #GVariant. Any floating
* references will be consumed. A %NULL value for a string
* type column will be converted to the empty string.
*
* Sets all values across the entire row referenced by @iter. The
* variable argument list should contain values that match the column schemas
* for the model. All basic variant type (see g_variant_type_is_basic()) are
* passed in as their raw C type while all other types are passed in boxed in
* a #GVariant. Any floating references on variants passed to this method are
* consumed.
*
* For example, to set the values for a row on model with the schema
* ("u", "s", "as"):
*
* GVariantBuilder b;
*
* g_variant_builder_init (&b, "as");
* g_variant_builder_add (&b, "Hello");
* g_variant_builder_add (&b, "World");
*
* dee_model_set (model, iter, 27, "foo", g_variant_builder_end (&b));
*
**/
void
dee_model_set (DeeModel *self,
DeeModelIter *iter,
...)
{
va_list args;
g_return_if_fail (DEE_IS_MODEL (self));
/* Update data */
va_start (args, iter);
dee_model_set_valist (self, iter, &args);
va_end (args);
}
/**
* dee_model_set_valist: (skip):
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @args: See dee_model_set()
*
* See dee_model_set(). This version takes a va_list for language bindings.
*/
static void
dee_model_set_valist (DeeModel *self,
DeeModelIter *iter,
va_list *args)
{
DeeModelIface *iface;
GVariant **row_members;
guint num_columns;
g_return_if_fail (DEE_IS_MODEL (self));
iface = DEE_MODEL_GET_IFACE (self);
num_columns = dee_model_get_n_columns (self);
row_members = g_alloca (num_columns * sizeof (gpointer));
dee_model_build_row_valist (self, row_members, args);
(* iface->set_row) (self, iter, row_members);
}
/**
* dee_model_set_value:
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @column: column number to set the value
* @value: New value for cell. If @value is a floating reference the model
* will assume ownership of the variant
*
* Sets the data in @column for the row @iter points to, to @value. The type
* of @value must be convertible to the type of the column.
*
* When this method call completes the model will emit ::row-changed. You can
* edit the model in place without triggering the change signals by calling
* dee_model_set_value_silently().
*/
void
dee_model_set_value (DeeModel *self,
DeeModelIter *iter,
guint column,
GVariant *value)
{
DeeModelIface *iface;
g_return_if_fail (DEE_IS_MODEL (self));
CHECK_SCHEMA (self, NULL, return);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->set_value) (self, iter, column, value);
}
/**
* dee_model_set_row:
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @row_members: (array): And array of
* #GVariants with type signature matching
* those from the model schema. If any of the variants have
* floating references these will be consumed
*
* Sets all columns in the row @iter points to, to those found in
* @row_members. The variants in @row_members must match the types defined in
* the model's schema.
*/
void
dee_model_set_row (DeeModel *self,
DeeModelIter *iter,
GVariant **row_members)
{
DeeModelIface *iface;
g_return_if_fail (DEE_IS_MODEL (self));
CHECK_SCHEMA (self, NULL, return);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->set_row) (self, iter, row_members);
}
/**
* dee_model_get:
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @VarArgs: a list of return locations matching the types defined in the
* column schemas. To ignore the data in a specific column pass
* a %NULL on that position
*
* Gets all the values across the entire row referenced by @iter. The
* variable argument list should contain pointers to variables that match
* the column schemas of this model.
*
* For all basic variant types (see g_variant_type_is_basic()) this method
* expects pointers to their native C types while for all other types it
* expects a pointer to a pointer to a #GVariant.
*
* For string values you are passed a constant reference which is owned by the
* model, but any returned variants must be freed with g_variant_unref ().
*
* For example, to get all values a model with signature ("u", "s", "as") you
* would do:
*
* guint32 u;
* const gchar *s;
* GVariant *v;
*
* dee_model_get (model, iter, &u, &s, &v);
*
* // do stuff
*
* g_variant_unref (v);
*
**/
void
dee_model_get (DeeModel *self,
DeeModelIter *iter,
...)
{
va_list args;
g_return_if_fail (DEE_IS_MODEL (self));
g_return_if_fail (iter);
va_start (args, iter);
dee_model_get_valist (self, iter, args);
va_end (args);
}
/**
* dee_model_get_valist: (skip):
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @args: a list of column/return location pairs, terminated by -1
*
* See #dee_model_get(). This version takes a va_list for language bindings.
**/
static void
dee_model_get_valist (DeeModel *self,
DeeModelIter *iter,
va_list args)
{
GVariant *val;
const GVariantType *val_t;
guint col, n_cols;
gpointer *col_data;
g_return_if_fail (DEE_IS_MODEL (self));
g_return_if_fail (iter != NULL);
n_cols = dee_model_get_n_columns (self);
for (col = 0; col < n_cols; col++)
{
col_data = va_arg (args, gpointer*);
/* Skip past here if this column's data was not request */
if (col_data == NULL)
{
continue;
}
val = dee_model_get_value (self, iter, col);
val_t = g_variant_get_type (val);
/* Basic types are passed back unboxed, and non-basic types are passed
* back wrapped in variants. Strings are special because we pass them
* back without copying them */
if (g_variant_type_is_basic (val_t))
{
if (g_variant_type_equal (val_t, G_VARIANT_TYPE_SIGNATURE) ||
g_variant_type_equal (val_t, G_VARIANT_TYPE_STRING) ||
g_variant_type_equal (val_t, G_VARIANT_TYPE_OBJECT_PATH))
{
/* We need to cast away the constness */
*col_data = (gpointer) g_variant_get_string (val, NULL);
}
else
g_variant_get (val, dee_model_get_column_schema (self, col),
col_data);
/* dee_model_get_value() returns a ref we need to free */
g_variant_unref (val);
}
else
{
/* For complex types the ref on val is transfered to the caller */
*col_data = val;
}
}
}
/**
* dee_model_get_row:
* @self: A #DeeModel to get a row from
* @iter: A #DeeModelIter pointing to the row to get
* @out_row_members: (array) (out) (allow-none) (default NULL):
* An array of variants with a length bigger than or equal to
* the number of columns in @self, or %NULL. If you pass
* %NULL here a new array will be allocated for you. The
* returned variants will have a non-floating reference
*
* Returns: (array zero-terminated=1): @out_row_members if it was not %NULL
* or a newly allocated array otherwise which you must free
* with g_free(). The variants in the array will have a strong
* reference and needs to be freed with g_variant_unref().
*/
GVariant**
dee_model_get_row (DeeModel *self,
DeeModelIter *iter,
GVariant **out_row_members)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_row) (self, iter, out_row_members);
}
/**
* dee_model_get_value:
* @self: The #DeeModel to inspect
* @iter: a #DeeModelIter pointing to the row to inspect
* @column: column number to retrieve the value from
*
* Returns: (transfer full): A, guaranteed non-floating, reference to a
* #GVariant containing the row data. Free with g_variant_unref().
*/
GVariant*
dee_model_get_value (DeeModel *self,
DeeModelIter *iter,
guint column)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_value) (self, iter, column);
}
/**
* dee_model_get_value_by_name:
* @self: The #DeeModel to inspect
* @iter: a #DeeModelIter pointing to the row to inspect
* @column: column name to retrieve the value from
*
* Returns: (transfer full): A, guaranteed non-floating, reference to a
* #GVariant containing the row data. Free with g_variant_unref().
*/
GVariant*
dee_model_get_value_by_name (DeeModel *self,
DeeModelIter *iter,
const gchar *column_name)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_value_by_name) (self, iter, column_name);
}
/**
* dee_model_get_bool:
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @column: the column to retrieve a boolean from
*
* Return value: if @iter and @column are valid, the boolean stored at @column.
* Otherwise %FALSE
*/
gboolean
dee_model_get_bool (DeeModel *self,
DeeModelIter *iter,
guint column)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), FALSE);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_bool) (self, iter, column);
}
/**
* dee_model_get_uchar:
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @column: the column to retrieve a uchar from
*
* Return value: if @iter and @column are valid, the uchar stored at @column.
* Otherwise 0.
**/
guchar
dee_model_get_uchar (DeeModel *self,
DeeModelIter *iter,
guint column)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), '\0');
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_uchar) (self, iter, column);
}
/**
* dee_model_get_int32:
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @column: the column to retrieve a int from
*
* Return value: if @iter and @column are valid, the int stored at @column.
* Otherwise 0.
**/
gint32
dee_model_get_int32 (DeeModel *self,
DeeModelIter *iter,
guint column)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), 0);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_int32) (self, iter, column);
}
/**
* dee_model_get_uint32:
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @column: the column to retrieve a uint from
*
* Return value: if @iter and @column are valid, the uint stored at @column.
* Otherwise 0.
**/
guint32
dee_model_get_uint32 (DeeModel *self,
DeeModelIter *iter,
guint column)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), 0);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_uint32) (self, iter, column);
}
/**
* dee_model_get_int64:
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @column: the column to retrieve a int64 from
*
* Return value: if @iter and @column are valid, the int64 stored at @column.
* Otherwise 0.
**/
gint64
dee_model_get_int64 (DeeModel *self,
DeeModelIter *iter,
guint column)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), 0);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_int64) (self, iter, column);
}
/**
* dee_model_get_uint64:
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @column: the column to retrieve a uint64 from
*
* Return value: if @iter and @column are valid, the uint64 stored at @column.
* Otherwise 0.
**/
guint64
dee_model_get_uint64 (DeeModel *self,
DeeModelIter *iter,
guint column)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), 0);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_uint64) (self, iter, column);
}
/**
* dee_model_get_double:
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @column: the column to retrieve a double from
*
* Return value: if @iter and @column are valid, the double stored at @column.
* Otherwise 0.
**/
gdouble
dee_model_get_double (DeeModel *self,
DeeModelIter *iter,
guint column)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), 0);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_double) (self, iter, column);
}
/**
* dee_model_get_string:
* @self: a #DeeModel
* @iter: a #DeeModelIter
* @column: the column to retrieve a string from
*
* Return value: if @iter and @column are valid, the string stored at @column.
* Otherwise %NULL.
**/
const gchar*
dee_model_get_string (DeeModel *self,
DeeModelIter *iter,
guint column)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_string) (self, iter, column);
}
/**
* dee_model_get_first_iter:
* @self: a #DeeModel
*
* Retrieves a #DeeModelIter representing the first row in @self.
*
* Return value: (transfer none): A #DeeModelIter (owned by @self, do not
* free it)
*/
DeeModelIter*
dee_model_get_first_iter (DeeModel *self)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_first_iter) (self);
}
/**
* dee_model_get_last_iter:
* @self: a #DeeModel
*
* Retrieves a #DeeModelIter pointing right after the
* last row in @self. This is refered to also the the
* end iter.
*
* As with other iters the end iter, in particular, is stable over inserts,
* changes, or removals.
*
* Return value: (transfer none): A #DeeModelIter (owned by @self, do not
* free it)
**/
DeeModelIter*
dee_model_get_last_iter (DeeModel *self)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_last_iter) (self);
}
/**
* dee_model_get_iter_at_row:
* @self: a #DeeModel
* @row: position of the row to retrieve
*
* Retrieves a #DeeModelIter representing the row at the given index.
*
* Note that this method does not have any performance guarantees. In particular
* it is not guaranteed to be O(1).
*
* Return value: (transfer none): A new #DeeModelIter, or %NULL if @row
* was out of bounds. The returned iter is owned by @self, so do not free it.
**/
DeeModelIter*
dee_model_get_iter_at_row (DeeModel *self, guint row)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_iter_at_row) (self, row);
}
/**
* dee_model_next:
* @self: a #DeeModel
* @iter: a #DeeModelIter
*
* Returns a #DeeModelIter that points to the next position in the model.
*
* Return value: (transfer none): A #DeeModelIter, pointing to the next row in
* the model. The iter is owned by @self, do not free it.
**/
DeeModelIter*
dee_model_next (DeeModel *self,
DeeModelIter *iter)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->next) (self, iter);
}
/**
* dee_model_prev:
* @self: a #DeeModel
* @iter: a #DeeModelIter
*
* Returns a #DeeModelIter that points to the previous position in the model.
*
* Return value: (transfer none): A #DeeModelIter, pointing to the previous
* row in the model. The iter is owned by @self, do not free it.
**/
DeeModelIter *
dee_model_prev (DeeModel *self,
DeeModelIter *iter)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->prev) (self, iter);
}
/**
* dee_model_is_first:
* @self: a #DeeModel
* @iter: a #DeeModelIter
*
* Checks if @iter is the very first iter @self.
*
* Return value: #TRUE if @iter is the first iter in the model
*/
gboolean
dee_model_is_first (DeeModel *self,
DeeModelIter *iter)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), FALSE);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->is_first) (self, iter);
}
/**
* dee_model_is_last:
* @self: a #DeeModel
* @iter: a #DeeModelIter
*
* Whether @iter is the end iter of @self. Note that the end iter points
* right after the last valid row in @self.
*
* Return value: #TRUE if @iter is the last iter in the model
*/
gboolean
dee_model_is_last (DeeModel *self,
DeeModelIter *iter)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), FALSE);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->is_last) (self, iter);
}
/**
* dee_model_get_position:
* @self: The model to inspect
* @iter: The iter to get the position of
*
* Get the numeric offset of @iter into @self. Note that this method is
* not guaranteed to be O(1).
*
* Returns: The integer offset of @iter in @self
*/
guint
dee_model_get_position (DeeModel *self,
DeeModelIter *iter)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), -1);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_position) (self, iter);
}
/**
* dee_model_register_tag:
* @self: The model to register a tag on
* @tag_destroy: Function called when a tagged row is removed from the model.
* This function will also be called on all tagged rows when the
* model is finalized.
*
* Register a new tag on a #DeeModel. A tag is an extra
* value attached to a given row on a model. The tags are invisible to all
* that doesn't have the tag handle returned by this method. #DeeModel
* implementations must ensure that dee_model_get_tag() is an O(1) operation.
*
* Tags can be very useful in associating some extra data to a row in a model
* and have that automatically synced when the model changes. If you're
* writing a tiled view for a model you might want to tag each row with the
* tile widget for that row. That way you have very convenient access to the
* tile widget given any row in the model.
*
* The private nature of tags and the fact that you can store arbitrary pointers
* and binary data in them also means that they are not serialized if you
* utilize a model implementation that exposes the #DeeSerializable interface.
*
* Return value: (transfer none) (type Dee.ModelTag): A #DeeModelTag handle
* that you can use to set and get tags with
*/
DeeModelTag*
dee_model_register_tag (DeeModel *self,
GDestroyNotify tag_destroy)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->register_tag) (self, tag_destroy);
}
/**
* dee_model_get_tag:
* @self: The model to get a tag from
* @iter: A #DeeModelIter pointing to the row to get the tag from
* @tag: The tag handle to retrieve the tag value for
*
* Look up a tag value for a given row in a model. This method is guaranteed
* to be O(1).
*
* Return value: (transfer none): Returns %NULL if @tag is unset otherwise the
* value of the tag as it was set with dee_model_set_tag().
*/
gpointer
dee_model_get_tag (DeeModel *self,
DeeModelIter *iter,
DeeModelTag *tag)
{
DeeModelIface *iface;
g_return_val_if_fail (DEE_IS_MODEL (self), NULL);
iface = DEE_MODEL_GET_IFACE (self);
return (* iface->get_tag) (self, iter, tag);
}
/**
* dee_model_set_tag:
* @self: The model to set a tag on
* @iter: The row to set the tag on
* @tag: The tag handle for the tag as obtained from dee_model_register_tag()
* @value: The value to set for @tag. Note that %NULL represents an unset tag
*
* Set a tag on a row in a model. This function is guaranteed to be O(1).
* See also dee_model_register_tag().
*
* If @tag is already set on this row the existing tag value will be destroyed
* with the #GDestroyNotify passed to the dee_model_register_tag().
*/
void
dee_model_set_tag (DeeModel *self,
DeeModelIter *iter,
DeeModelTag *tag,
gpointer value)
{
DeeModelIface *iface;
g_return_if_fail (DEE_IS_MODEL (self));
iface = DEE_MODEL_GET_IFACE (self);
(* iface->set_tag) (self, iter, tag, value);
}
/**
* dee_model_clear_tag:
* @self: The model to clear a tag on
* @iter: The row to clear the tag from
* @tag: The tag to clear from @iter
*
* This method is purely syntactic sugar for calling dee_model_set_tag() with
* a @value of %NULL. It's included in order to help developers write more
* readable code.
*/
void
dee_model_clear_tag (DeeModel *self,
DeeModelIter *iter,
DeeModelTag *tag)
{
DeeModelIface *iface;
g_return_if_fail (DEE_IS_MODEL (self));
iface = DEE_MODEL_GET_IFACE (self);
(* iface->set_tag) (self, iter, tag, NULL);
}