mirror of
https://github.com/brl/mutter.git
synced 2024-11-26 18:11:05 -05:00
6bffd407fd
This adds a custom "rows" property, that allows to define the rows of a ClutterModel. A single row can either an array of all columns or an object with column-name : column-value pairs. http://bugzilla.clutter-project.org/show_bug.cgi?id=2528
2422 lines
64 KiB
C
2422 lines
64 KiB
C
/*
|
|
* Clutter.
|
|
*
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
|
* Neil Jagdish Patel <njp@o-hand.com>
|
|
* Emmanuele Bassi <ebassi@openedhand.com>
|
|
*
|
|
* Copyright (C) 2006 OpenedHand
|
|
*
|
|
* 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 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*
|
|
*
|
|
* NB: Inspiration for column storage taken from GtkListStore
|
|
*/
|
|
|
|
/**
|
|
* SECTION:clutter-model
|
|
* @short_description: A generic model implementation
|
|
*
|
|
* #ClutterModel is a generic list model API which can be used to implement
|
|
* the model-view-controller architectural pattern in Clutter.
|
|
*
|
|
* The #ClutterModel class is a list model which can accept most GObject
|
|
* types as a column type.
|
|
*
|
|
* Creating a simple clutter model:
|
|
* <informalexample><programlisting>
|
|
* enum
|
|
* {
|
|
* COLUMN_INT,
|
|
* COLUMN_STRING,
|
|
*
|
|
* N_COLUMNS
|
|
* };
|
|
*
|
|
* {
|
|
* ClutterModel *model;
|
|
* gint i;
|
|
*
|
|
* model = clutter_model_default_new (N_COLUMNS,
|
|
* /<!-- -->* column type, column title *<!-- -->/
|
|
* G_TYPE_INT, "my integers",
|
|
* G_TYPE_STRING, "my strings");
|
|
* for (i = 0; i < 10; i++)
|
|
* {
|
|
* gchar *string = g_strdup_printf ("String %d", i);
|
|
* clutter_model_append (model,
|
|
* COLUMN_INT, i,
|
|
* COLUMN_STRING, string,
|
|
* -1);
|
|
* g_free (string);
|
|
* }
|
|
*
|
|
*
|
|
* }
|
|
* </programlisting></informalexample>
|
|
*
|
|
* Iterating through the model consists of retrieving a new #ClutterModelIter
|
|
* pointing to the starting row, and calling clutter_model_iter_next() or
|
|
* clutter_model_iter_prev() to move forward or backwards, repectively.
|
|
*
|
|
* A valid #ClutterModelIter represents the position between two rows in the
|
|
* model. For example, the "first" iterator represents the gap immediately
|
|
* before the first row, and the "last" iterator represents the gap immediately
|
|
* after the last row. In an empty sequence, the first and last iterators are
|
|
* the same.
|
|
*
|
|
* Iterating a #ClutterModel:
|
|
* <informalexample><programlisting>
|
|
* enum
|
|
* {
|
|
* COLUMN_INT,
|
|
* COLUMN_STRING.
|
|
*
|
|
* N_COLUMNS
|
|
* };
|
|
*
|
|
* {
|
|
* ClutterModel *model;
|
|
* ClutterModelIter *iter = NULL;
|
|
*
|
|
* /<!-- -->* Fill the model *<!-- -->/
|
|
* model = populate_model ();
|
|
*
|
|
* /<!-- -->* Get the first iter *<!-- -->/
|
|
* iter = clutter_model_get_first_iter (model);
|
|
* while (!clutter_model_iter_is_last (iter))
|
|
* {
|
|
* print_row (iter);
|
|
*
|
|
* iter = clutter_model_iter_next (iter);
|
|
* }
|
|
*
|
|
* /<!-- -->* Make sure to unref the iter *<!-- -->/
|
|
* g_object_unref (iter);
|
|
* }
|
|
* </programlisting></informalexample>
|
|
*
|
|
* #ClutterModel is an abstract class. Clutter provides a list model
|
|
* implementation called #ClutterListModel which has been optimised
|
|
* for insertion and look up in sorted lists.
|
|
*
|
|
* <refsect2 id="ClutterModel-script">
|
|
* <title>ClutterModel custom properties for #ClutterScript</title>
|
|
* <para>#ClutterModel defines a custom property "columns" for #ClutterScript
|
|
* which allows defining the column names and types. It also defines a custom
|
|
* "rows" property which allows filling the #ClutterModel with some
|
|
* data.</para>
|
|
* <example id="ClutterModel-script-example">
|
|
* <title>Example of the "columns" and "rows" custom properties</title>
|
|
* <para>The definition below will create a #ClutterListModel with three
|
|
* columns: the first one with name "Name" and containing strings; the
|
|
* second one with name "Score" and containing integers; the third one with
|
|
* name "Icon" and containing #ClutterTexture<!-- -->s. The model is filled
|
|
* with three rows. A row can be defined either with an array that holds
|
|
* all columns of a row, or an object that holds "column-name" :
|
|
* "column-value" pairs.
|
|
* </para>
|
|
* <programlisting>
|
|
* {
|
|
* "type" : "ClutterListModel",
|
|
* "id" : "teams-model",
|
|
* "columns" : [
|
|
* [ "Name", "gchararray" ],
|
|
* [ "Score", "gint" ],
|
|
* [ "Icon", "ClutterTexture" ]
|
|
* ],
|
|
* "rows" : [
|
|
* [ "Team 1", 42, { "type" : "ClutterTexture", "filename" : "team1.png" } ],
|
|
* [ "Team 2", 23, "team2-icon-script-id" ],
|
|
* { "Name" : "Team 3", "Icon" : "team3-icon-script-id" }
|
|
* ]
|
|
* }
|
|
* </programlisting>
|
|
* </example>
|
|
* </refsect2>
|
|
*
|
|
* #ClutterModel is available since Clutter 0.6
|
|
*/
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <glib-object.h>
|
|
#include <gobject/gvaluecollector.h>
|
|
|
|
#include "clutter-model.h"
|
|
#include "clutter-model-private.h"
|
|
|
|
#include "clutter-marshal.h"
|
|
#include "clutter-private.h"
|
|
#include "clutter-debug.h"
|
|
#include "clutter-scriptable.h"
|
|
#include "clutter-script-private.h"
|
|
|
|
static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
|
|
|
|
G_DEFINE_ABSTRACT_TYPE_WITH_CODE
|
|
(ClutterModel, clutter_model, G_TYPE_OBJECT,
|
|
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
|
|
clutter_scriptable_iface_init));
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_FILTER_SET
|
|
};
|
|
|
|
enum
|
|
{
|
|
ROW_ADDED,
|
|
ROW_REMOVED,
|
|
ROW_CHANGED,
|
|
|
|
SORT_CHANGED,
|
|
FILTER_CHANGED,
|
|
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint model_signals[LAST_SIGNAL] = { 0, };
|
|
|
|
#define CLUTTER_MODEL_GET_PRIVATE(obj) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_MODEL, ClutterModelPrivate))
|
|
|
|
struct _ClutterModelPrivate
|
|
{
|
|
GType *column_types;
|
|
gchar **column_names;
|
|
|
|
/* we use an integer here because we want to be able to use -1 as a
|
|
* guard value, to allow calling set_names() and set_types() from
|
|
* sub-classes of ClutterModel. see bug:
|
|
*
|
|
* http://bugzilla.openedhand.com/show_bug.cgi?id=2032
|
|
*
|
|
* for a reference.
|
|
*/
|
|
gint n_columns;
|
|
|
|
ClutterModelFilterFunc filter_func;
|
|
gpointer filter_data;
|
|
GDestroyNotify filter_notify;
|
|
|
|
gint sort_column;
|
|
ClutterModelSortFunc sort_func;
|
|
gpointer sort_data;
|
|
GDestroyNotify sort_notify;
|
|
};
|
|
|
|
static GType
|
|
clutter_model_real_get_column_type (ClutterModel *model,
|
|
guint column)
|
|
{
|
|
ClutterModelPrivate *priv = model->priv;
|
|
|
|
if (column < 0 || column >= clutter_model_get_n_columns (model))
|
|
return G_TYPE_INVALID;
|
|
|
|
return priv->column_types[column];
|
|
}
|
|
|
|
static const gchar *
|
|
clutter_model_real_get_column_name (ClutterModel *model,
|
|
guint column)
|
|
{
|
|
ClutterModelPrivate *priv = model->priv;
|
|
|
|
if (column < 0 || column >= clutter_model_get_n_columns (model))
|
|
return NULL;
|
|
|
|
if (priv->column_names && priv->column_names[column])
|
|
return priv->column_names[column];
|
|
|
|
return g_type_name (priv->column_types[column]);
|
|
}
|
|
|
|
static guint
|
|
clutter_model_real_get_n_columns (ClutterModel *model)
|
|
{
|
|
ClutterModelPrivate *priv = model->priv;
|
|
|
|
if (priv->n_columns < 0)
|
|
return 0;
|
|
|
|
return priv->n_columns;
|
|
}
|
|
|
|
static guint
|
|
clutter_model_real_get_n_rows (ClutterModel *model)
|
|
{
|
|
ClutterModelIter *iter;
|
|
guint row_count;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL (model), 0);
|
|
|
|
iter = clutter_model_get_first_iter (model);
|
|
if (iter == NULL)
|
|
return 0;
|
|
|
|
row_count = 0;
|
|
while (!clutter_model_iter_is_last (iter))
|
|
{
|
|
if (clutter_model_filter_iter (model, iter))
|
|
row_count += 1;
|
|
|
|
iter = clutter_model_iter_next (iter);
|
|
}
|
|
|
|
g_object_unref (iter);
|
|
|
|
return row_count;
|
|
}
|
|
|
|
static void
|
|
clutter_model_finalize (GObject *object)
|
|
{
|
|
ClutterModelPrivate *priv = CLUTTER_MODEL (object)->priv;
|
|
gint i;
|
|
|
|
if (priv->sort_notify)
|
|
priv->sort_notify (priv->sort_data);
|
|
|
|
if (priv->filter_notify)
|
|
priv->filter_notify (priv->filter_data);
|
|
|
|
g_free (priv->column_types);
|
|
|
|
if (priv->column_names != NULL)
|
|
{
|
|
/* the column_names vector might have holes in it, so we need
|
|
* to use the columns number to clear up everything
|
|
*/
|
|
for (i = 0; i < priv->n_columns; i++)
|
|
g_free (priv->column_names[i]);
|
|
|
|
g_free (priv->column_names);
|
|
}
|
|
|
|
G_OBJECT_CLASS (clutter_model_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
clutter_model_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterModelPrivate *priv = CLUTTER_MODEL (gobject)->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_FILTER_SET:
|
|
g_value_set_boolean (value, priv->filter_func != NULL);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_model_class_init (ClutterModelClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GParamSpec *pspec;
|
|
|
|
gobject_class->get_property = clutter_model_get_property;
|
|
gobject_class->finalize = clutter_model_finalize;
|
|
|
|
g_type_class_add_private (gobject_class, sizeof (ClutterModelPrivate));
|
|
|
|
klass->get_column_name = clutter_model_real_get_column_name;
|
|
klass->get_column_type = clutter_model_real_get_column_type;
|
|
klass->get_n_columns = clutter_model_real_get_n_columns;
|
|
klass->get_n_rows = clutter_model_real_get_n_rows;
|
|
|
|
/**
|
|
* ClutterModel:filter-set:
|
|
*
|
|
* Whether the #ClutterModel has a filter set
|
|
*
|
|
* This property is set to %TRUE if a filter function has been
|
|
* set using clutter_model_set_filter()
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_boolean ("filter-set",
|
|
"Filter Set",
|
|
"Whether the model has a filter",
|
|
FALSE,
|
|
CLUTTER_PARAM_READABLE);
|
|
g_object_class_install_property (gobject_class, PROP_FILTER_SET, pspec);
|
|
|
|
/**
|
|
* ClutterModel::row-added:
|
|
* @model: the #ClutterModel on which the signal is emitted
|
|
* @iter: a #ClutterModelIter pointing to the new row
|
|
*
|
|
* The ::row-added signal is emitted when a new row has been added.
|
|
* The data on the row has already been set when the ::row-added signal
|
|
* has been emitted.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
model_signals[ROW_ADDED] =
|
|
g_signal_new ("row-added",
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterModelClass, row_added),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
CLUTTER_TYPE_MODEL_ITER);
|
|
/**
|
|
* ClutterModel::row-removed:
|
|
* @model: the #ClutterModel on which the signal is emitted
|
|
* @iter: a #ClutterModelIter pointing to the removed row
|
|
*
|
|
* The ::row-removed signal is emitted when a row has been removed.
|
|
* The data on the row pointed by the passed iterator is still valid
|
|
* when the ::row-removed signal has been emitted.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
model_signals[ROW_REMOVED] =
|
|
g_signal_new ("row-removed",
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterModelClass, row_removed),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
CLUTTER_TYPE_MODEL_ITER);
|
|
/**
|
|
* ClutterModel::row-changed:
|
|
* @model: the #ClutterModel on which the signal is emitted
|
|
* @iter: a #ClutterModelIter pointing to the changed row
|
|
*
|
|
* The ::row-removed signal is emitted when a row has been changed.
|
|
* The data on the row has already been updated when the ::row-changed
|
|
* signal has been emitted.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
model_signals[ROW_CHANGED] =
|
|
g_signal_new ("row-changed",
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterModelClass, row_changed),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
CLUTTER_TYPE_MODEL_ITER);
|
|
/**
|
|
* ClutterModel::sort-changed:
|
|
* @model: the #ClutterModel on which the signal is emitted
|
|
*
|
|
* The ::sort-changed signal is emitted after the model has been sorted
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
model_signals[SORT_CHANGED] =
|
|
g_signal_new ("sort-changed",
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterModelClass, sort_changed),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
/**
|
|
* ClutterModel::filter-changed:
|
|
* @model: the #ClutterModel on which the signal is emitted
|
|
*
|
|
* The ::filter-changed signal is emitted when a new filter has been applied
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
model_signals[FILTER_CHANGED] =
|
|
g_signal_new ("filter-changed",
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterModelClass, filter_changed),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
}
|
|
|
|
static void
|
|
clutter_model_init (ClutterModel *self)
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
|
|
self->priv = priv = CLUTTER_MODEL_GET_PRIVATE (self);
|
|
|
|
priv->n_columns = -1;
|
|
priv->column_types = NULL;
|
|
priv->column_names = NULL;
|
|
|
|
priv->filter_func = NULL;
|
|
priv->filter_data = NULL;
|
|
priv->filter_notify = NULL;
|
|
|
|
priv->sort_column = -1;
|
|
priv->sort_func = NULL;
|
|
priv->sort_data = NULL;
|
|
priv->sort_notify = NULL;
|
|
}
|
|
|
|
/* XXX - is this whitelist really necessary? we accept every fundamental
|
|
* type.
|
|
*/
|
|
gboolean
|
|
clutter_model_check_type (GType gtype)
|
|
{
|
|
gint i = 0;
|
|
static const GType type_list[] =
|
|
{
|
|
G_TYPE_BOOLEAN,
|
|
G_TYPE_CHAR,
|
|
G_TYPE_UCHAR,
|
|
G_TYPE_INT,
|
|
G_TYPE_UINT,
|
|
G_TYPE_LONG,
|
|
G_TYPE_ULONG,
|
|
G_TYPE_INT64,
|
|
G_TYPE_UINT64,
|
|
G_TYPE_ENUM,
|
|
G_TYPE_FLAGS,
|
|
G_TYPE_FLOAT,
|
|
G_TYPE_DOUBLE,
|
|
G_TYPE_STRING,
|
|
G_TYPE_POINTER,
|
|
G_TYPE_BOXED,
|
|
G_TYPE_OBJECT,
|
|
G_TYPE_INVALID
|
|
};
|
|
|
|
if (! G_TYPE_IS_VALUE_TYPE (gtype))
|
|
return FALSE;
|
|
|
|
while (type_list[i] != G_TYPE_INVALID)
|
|
{
|
|
if (g_type_is_a (gtype, type_list[i]))
|
|
return TRUE;
|
|
i++;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
gchar *name;
|
|
GType type;
|
|
} ColumnInfo;
|
|
|
|
static gboolean
|
|
clutter_model_parse_custom_node (ClutterScriptable *scriptable,
|
|
ClutterScript *script,
|
|
GValue *value,
|
|
const gchar *name,
|
|
JsonNode *node)
|
|
{
|
|
if (strcmp (name, "columns") == 0)
|
|
{
|
|
GSList *columns = NULL;
|
|
GList *elements, *l;
|
|
|
|
if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
|
|
return FALSE;
|
|
|
|
elements = json_array_get_elements (json_node_get_array (node));
|
|
|
|
for (l = elements; l != NULL; l = l->next)
|
|
{
|
|
JsonNode *child_node = l->data;
|
|
JsonArray *array = json_node_get_array (child_node);
|
|
ColumnInfo *cinfo;
|
|
const gchar *column_name;
|
|
const gchar *type_name;
|
|
|
|
if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY ||
|
|
json_array_get_length (array) != 2)
|
|
{
|
|
g_warning ("A column must be an array of "
|
|
"[\"column-name\", \"GType-name\"] pairs");
|
|
return FALSE;
|
|
}
|
|
|
|
column_name = json_array_get_string_element (array, 0);
|
|
type_name = json_array_get_string_element (array, 1);
|
|
|
|
cinfo = g_slice_new0 (ColumnInfo);
|
|
cinfo->name = g_strdup (column_name);
|
|
cinfo->type = clutter_script_get_type_from_name (script, type_name);
|
|
|
|
columns = g_slist_prepend (columns, cinfo);
|
|
}
|
|
|
|
g_list_free (elements);
|
|
|
|
g_value_init (value, G_TYPE_POINTER);
|
|
g_value_set_pointer (value, g_slist_reverse (columns));
|
|
|
|
return TRUE;
|
|
}
|
|
else if (strcmp (name, "rows") == 0)
|
|
{
|
|
GSList *rows = NULL;
|
|
GList *elements, *l;
|
|
|
|
if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
|
|
return FALSE;
|
|
|
|
/*
|
|
* at this point we have no information about the column types, so
|
|
* we just copy the json elements and resolve them in the
|
|
* set_custom_property method
|
|
*/
|
|
elements = json_array_get_elements (json_node_get_array (node));
|
|
for (l = elements; l != NULL; l = l->next)
|
|
rows = g_slist_prepend (rows, json_node_copy (l->data));
|
|
g_list_free (elements);
|
|
|
|
g_value_init (value, G_TYPE_POINTER);
|
|
g_value_set_pointer (value, g_slist_reverse (rows));
|
|
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
clutter_model_set_custom_property (ClutterScriptable *scriptable,
|
|
ClutterScript *script,
|
|
const gchar *name,
|
|
const GValue *value)
|
|
{
|
|
if (strcmp (name, "columns") == 0)
|
|
{
|
|
ClutterModel *model = CLUTTER_MODEL (scriptable);
|
|
GSList *columns, *l;
|
|
guint n_columns;
|
|
gint i;
|
|
|
|
columns = g_value_get_pointer (value);
|
|
n_columns = g_slist_length (columns);
|
|
|
|
clutter_model_set_n_columns (model, n_columns, TRUE, TRUE);
|
|
|
|
for (i = 0, l = columns; l != NULL; l = l->next, i++)
|
|
{
|
|
ColumnInfo *cinfo = l->data;
|
|
|
|
clutter_model_set_column_name (model, i, cinfo->name);
|
|
clutter_model_set_column_type (model, i, cinfo->type);
|
|
|
|
g_free (cinfo->name);
|
|
g_slice_free (ColumnInfo, cinfo);
|
|
}
|
|
|
|
g_slist_free (columns);
|
|
}
|
|
else if (strcmp (name, "rows") == 0)
|
|
{
|
|
ClutterModel *model = CLUTTER_MODEL (scriptable);
|
|
GSList *rows, *l;
|
|
guint n_columns, row = 0;
|
|
|
|
rows = g_value_get_pointer (value);
|
|
n_columns = clutter_model_get_n_columns (model);
|
|
|
|
for (l = rows; l; l = l->next)
|
|
{
|
|
JsonNode *node = l->data;
|
|
guint *columns, i, n_values = 0;
|
|
GValueArray *values;
|
|
|
|
if (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY)
|
|
{
|
|
JsonArray *array = json_node_get_array (node);
|
|
if (json_array_get_length (array) != n_columns)
|
|
{
|
|
g_warning ("Row %d contains the wrong count of columns",
|
|
g_slist_position (rows, l) + 1);
|
|
row++;
|
|
continue;
|
|
}
|
|
|
|
n_values = n_columns;
|
|
columns = g_new (guint, n_values);
|
|
values = g_value_array_new (n_values);
|
|
|
|
for (i = 0; i < n_values; i++)
|
|
{
|
|
GType column_type;
|
|
const gchar *column_name;
|
|
GValue v = { 0, };
|
|
|
|
column_type = clutter_model_get_column_type (model, i);
|
|
column_name = clutter_model_get_column_name (model, i);
|
|
columns[i] = i;
|
|
g_value_init (&v, column_type);
|
|
clutter_script_parse_node (script, &v, column_name,
|
|
json_array_get_element (array, i),
|
|
NULL);
|
|
g_value_array_append (values, &v);
|
|
g_value_unset (&v);
|
|
}
|
|
}
|
|
else if (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT)
|
|
{
|
|
JsonObject *object = json_node_get_object (node);
|
|
GList *members, *m;
|
|
guint column = 0;
|
|
|
|
n_values = json_object_get_size (object);
|
|
columns = g_new (guint, n_values);
|
|
values = g_value_array_new (n_values);
|
|
|
|
members = json_object_get_members (object);
|
|
for (m = members; m; m = m->next)
|
|
{
|
|
const gchar *mname = m->data;
|
|
|
|
for (i = 0; i < clutter_model_get_n_columns (model); i++)
|
|
{
|
|
const gchar *cname;
|
|
|
|
cname = clutter_model_get_column_name (model, i);
|
|
if (strcmp (mname, cname) == 0)
|
|
{
|
|
JsonNode *member;
|
|
GType col_type;
|
|
const gchar *col_name;
|
|
GValue v = { 0, };
|
|
|
|
col_type = clutter_model_get_column_type (model, i);
|
|
col_name = clutter_model_get_column_name (model, i);
|
|
columns[column] = i;
|
|
g_value_init (&v, col_type);
|
|
member = json_object_get_member (object, mname);
|
|
clutter_script_parse_node (script, &v,
|
|
col_name, member,
|
|
NULL);
|
|
g_value_array_append (values, &v);
|
|
g_value_unset (&v);
|
|
break;
|
|
}
|
|
}
|
|
column++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
row++;
|
|
continue;
|
|
}
|
|
|
|
clutter_model_insertv (model, row, n_values, columns, values->values);
|
|
g_value_array_free (values);
|
|
g_free (columns);
|
|
json_node_free (node);
|
|
row++;
|
|
}
|
|
g_slist_free (rows);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
clutter_scriptable_iface_init (ClutterScriptableIface *iface)
|
|
{
|
|
iface->parse_custom_node = clutter_model_parse_custom_node;
|
|
iface->set_custom_property = clutter_model_set_custom_property;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_resort:
|
|
* @model: a #ClutterModel
|
|
*
|
|
* Force a resort on the @model. This function should only be
|
|
* used by subclasses of #ClutterModel.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_resort (ClutterModel *model)
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
ClutterModelClass *klass;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
priv = model->priv;
|
|
|
|
klass = CLUTTER_MODEL_GET_CLASS (model);
|
|
|
|
if (klass->resort)
|
|
klass->resort (model, priv->sort_func, priv->sort_data);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_filter_row:
|
|
* @model: a #ClutterModel
|
|
* @row: the row to filter
|
|
*
|
|
* Checks whether @row should be filtered or not using the
|
|
* filtering function set on @model.
|
|
*
|
|
* This function should be used only by subclasses of #ClutterModel.
|
|
*
|
|
* Return value: %TRUE if the row should be displayed,
|
|
* %FALSE otherwise
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
gboolean
|
|
clutter_model_filter_row (ClutterModel *model,
|
|
guint row)
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
ClutterModelIter *iter;
|
|
gboolean res = TRUE;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL (model), TRUE);
|
|
|
|
priv = model->priv;
|
|
|
|
if (!priv->filter_func)
|
|
return TRUE;
|
|
|
|
iter = clutter_model_get_iter_at_row (model, row);
|
|
if (iter == NULL)
|
|
return FALSE;
|
|
|
|
res = priv->filter_func (model, iter, priv->filter_data);
|
|
|
|
g_object_unref (iter);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_filter_iter:
|
|
* @model: a #ClutterModel
|
|
* @iter: the row to filter
|
|
*
|
|
* Checks whether the row pointer by @iter should be filtered or not using
|
|
* the filtering function set on @model.
|
|
*
|
|
* This function should be used only by subclasses of #ClutterModel.
|
|
*
|
|
* Return value: %TRUE if the row should be displayed,
|
|
* %FALSE otherwise
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
gboolean
|
|
clutter_model_filter_iter (ClutterModel *model,
|
|
ClutterModelIter *iter)
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL (model), TRUE);
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), TRUE);
|
|
|
|
priv = model->priv;
|
|
|
|
if (!priv->filter_func)
|
|
return TRUE;
|
|
|
|
return priv->filter_func (model, iter, priv->filter_data);
|
|
}
|
|
|
|
/*
|
|
* clutter_model_set_n_columns:
|
|
* @model: a #ClutterModel
|
|
* @n_columns: number of columns
|
|
* @set_types: set the columns type
|
|
* @set_names: set the columns name
|
|
*
|
|
* Sets the number of columns in @model to @n_columns. If @set_types
|
|
* or @set_names are %TRUE, initialises the columns type and name
|
|
* arrays as well.
|
|
*
|
|
* This function can only be called once.
|
|
*/
|
|
void
|
|
clutter_model_set_n_columns (ClutterModel *model,
|
|
gint n_columns,
|
|
gboolean set_types,
|
|
gboolean set_names)
|
|
{
|
|
ClutterModelPrivate *priv = model->priv;
|
|
|
|
if (priv->n_columns > 0 && priv->n_columns != n_columns)
|
|
return;
|
|
|
|
priv->n_columns = n_columns;
|
|
|
|
if (set_types && !priv->column_types)
|
|
priv->column_types = g_new0 (GType, n_columns);
|
|
|
|
if (set_names && !priv->column_names)
|
|
priv->column_names = g_new0 (gchar*, n_columns);
|
|
}
|
|
|
|
/*
|
|
* clutter_model_set_column_type:
|
|
* @model: a #ClutterModel
|
|
* @column: column index
|
|
* @gtype: type of the column
|
|
*
|
|
* Sets the type of @column inside @model
|
|
*/
|
|
void
|
|
clutter_model_set_column_type (ClutterModel *model,
|
|
gint column,
|
|
GType gtype)
|
|
{
|
|
ClutterModelPrivate *priv = model->priv;
|
|
|
|
priv->column_types[column] = gtype;
|
|
}
|
|
|
|
/*
|
|
* clutter_model_set_column_name:
|
|
* @model: a #ClutterModel
|
|
* @column: column index
|
|
* @name: name of the column, or %NULL
|
|
*
|
|
* Sets the name of @column inside @model
|
|
*/
|
|
void
|
|
clutter_model_set_column_name (ClutterModel *model,
|
|
gint column,
|
|
const gchar *name)
|
|
{
|
|
ClutterModelPrivate *priv = model->priv;
|
|
|
|
priv->column_names[column] = g_strdup (name);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_set_types:
|
|
* @model: a #ClutterModel
|
|
* @n_columns: number of columns for the model
|
|
* @types: (array length=n_columns): an array of #GType types
|
|
*
|
|
* Sets the types of the columns inside a #ClutterModel.
|
|
*
|
|
* This function is meant primarily for #GObjects that inherit from
|
|
* #ClutterModel, and should only be used when contructing a #ClutterModel.
|
|
* It will not work after the initial creation of the #ClutterModel.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_set_types (ClutterModel *model,
|
|
guint n_columns,
|
|
GType *types)
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
gint i;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
g_return_if_fail (n_columns > 0);
|
|
|
|
priv = model->priv;
|
|
|
|
g_return_if_fail (priv->n_columns < 0 || priv->n_columns == n_columns);
|
|
g_return_if_fail (priv->column_types == NULL);
|
|
|
|
clutter_model_set_n_columns (model, n_columns, TRUE, FALSE);
|
|
|
|
for (i = 0; i < n_columns; i++)
|
|
{
|
|
if (!clutter_model_check_type (types[i]))
|
|
{
|
|
g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i]));
|
|
return;
|
|
}
|
|
|
|
clutter_model_set_column_type (model, i, types[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_model_set_names:
|
|
* @model: a #ClutterModel
|
|
* @n_columns: the number of column names
|
|
* @names: (array length=n_columns): an array of strings
|
|
*
|
|
* Assigns a name to the columns of a #ClutterModel.
|
|
*
|
|
* This function is meant primarily for #GObjects that inherit from
|
|
* #ClutterModel, and should only be used when contructing a #ClutterModel.
|
|
* It will not work after the initial creation of the #ClutterModel.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_set_names (ClutterModel *model,
|
|
guint n_columns,
|
|
const gchar * const names[])
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
gint i;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
g_return_if_fail (n_columns > 0);
|
|
|
|
priv = model->priv;
|
|
|
|
g_return_if_fail (priv->n_columns < 0 || priv->n_columns == n_columns);
|
|
g_return_if_fail (priv->column_names == NULL);
|
|
|
|
clutter_model_set_n_columns (model, n_columns, FALSE, TRUE);
|
|
|
|
for (i = 0; i < n_columns; i++)
|
|
clutter_model_set_column_name (model, i, names[i]);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_get_n_columns:
|
|
* @model: a #ClutterModel
|
|
*
|
|
* Retrieves the number of columns inside @model.
|
|
*
|
|
* Return value: the number of columns
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
guint
|
|
clutter_model_get_n_columns (ClutterModel *model)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL (model), 0);
|
|
|
|
return CLUTTER_MODEL_GET_CLASS (model)->get_n_columns (model);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_appendv:
|
|
* @model: a #ClutterModel
|
|
* @n_columns: the number of columns and values
|
|
* @columns: (array length=n_columns): a vector with the columns to set
|
|
* @values: (array length=n_columns): a vector with the values
|
|
*
|
|
* Creates and appends a new row to the #ClutterModel, setting the row
|
|
* values for the given @columns upon creation.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_appendv (ClutterModel *model,
|
|
guint n_columns,
|
|
guint *columns,
|
|
GValue *values)
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
ClutterModelIter *iter;
|
|
gint i;
|
|
gboolean resort = FALSE;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
g_return_if_fail (n_columns <= clutter_model_get_n_columns (model));
|
|
g_return_if_fail (columns != NULL);
|
|
g_return_if_fail (values != NULL);
|
|
|
|
priv = model->priv;
|
|
|
|
iter = CLUTTER_MODEL_GET_CLASS (model)->insert_row (model, -1);
|
|
g_assert (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
for (i = 0; i < n_columns; i++)
|
|
{
|
|
if (priv->sort_column == columns[i])
|
|
resort = TRUE;
|
|
|
|
clutter_model_iter_set_value (iter, columns[i], &values[i]);
|
|
}
|
|
|
|
g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
|
|
|
|
if (resort)
|
|
clutter_model_resort (model);
|
|
|
|
g_object_unref (iter);
|
|
}
|
|
|
|
/* forward declaration */
|
|
static void clutter_model_iter_set_internal_valist (ClutterModelIter *iter,
|
|
va_list args);
|
|
|
|
/**
|
|
* clutter_model_append:
|
|
* @model: a #ClutterModel
|
|
* @Varargs: pairs of column number and value, terminated with -1
|
|
*
|
|
* Creates and appends a new row to the #ClutterModel, setting the
|
|
* row values upon creation. For example, to append a new row where
|
|
* column 0 is type %G_TYPE_INT and column 1 is of type %G_TYPE_STRING:
|
|
*
|
|
* <informalexample><programlisting>
|
|
* ClutterModel *model;
|
|
* model = clutter_model_default_new (2,
|
|
* G_TYPE_INT, "Score",
|
|
* G_TYPE_STRING, "Team");
|
|
* clutter_model_append (model, 0, 42, 1, "Team #1", -1);
|
|
* </programlisting></informalexample>
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_append (ClutterModel *model,
|
|
...)
|
|
{
|
|
ClutterModelIter *iter;
|
|
va_list args;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
|
|
iter = CLUTTER_MODEL_GET_CLASS (model)->insert_row (model, -1);
|
|
g_assert (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
/* do not emit the ::row-changed signal */
|
|
va_start (args, model);
|
|
clutter_model_iter_set_internal_valist (iter, args);
|
|
va_end (args);
|
|
|
|
g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
|
|
|
|
g_object_unref (iter);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_prependv:
|
|
* @model: a #ClutterModel
|
|
* @n_columns: the number of columns and values to set
|
|
* @columns: (array length=n_columns): a vector containing the columns to set
|
|
* @values: (array length=n_columns): a vector containing the values for the cells
|
|
*
|
|
* Creates and prepends a new row to the #ClutterModel, setting the row
|
|
* values for the given @columns upon creation.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_prependv (ClutterModel *model,
|
|
guint n_columns,
|
|
guint *columns,
|
|
GValue *values)
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
ClutterModelIter *iter;
|
|
gint i;
|
|
gboolean resort = FALSE;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
g_return_if_fail (n_columns <= clutter_model_get_n_columns (model));
|
|
g_return_if_fail (columns != NULL);
|
|
g_return_if_fail (values != NULL);
|
|
|
|
priv = model->priv;
|
|
|
|
iter = CLUTTER_MODEL_GET_CLASS (model)->insert_row (model, 0);
|
|
g_assert (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
for (i = 0; i < n_columns; i++)
|
|
{
|
|
if (priv->sort_column == columns[i])
|
|
resort = TRUE;
|
|
|
|
clutter_model_iter_set_value (iter, columns[i], &values[i]);
|
|
}
|
|
|
|
g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
|
|
|
|
if (resort)
|
|
clutter_model_resort (model);
|
|
|
|
g_object_unref (iter);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_prepend:
|
|
* @model: a #ClutterModel
|
|
* @Varargs: pairs of column number and value, terminated with -1
|
|
*
|
|
* Creates and prepends a new row to the #ClutterModel, setting the row
|
|
* values upon creation. For example, to prepend a new row where column 0
|
|
* is type %G_TYPE_INT and column 1 is of type %G_TYPE_STRING:
|
|
*
|
|
* <informalexample><programlisting>
|
|
* ClutterModel *model;
|
|
* model = clutter_model_default_new (2,
|
|
* G_TYPE_INT, "Score",
|
|
* G_TYPE_STRING, "Team");
|
|
* clutter_model_prepend (model, 0, 42, 1, "Team #1", -1);
|
|
* </programlisting></informalexample>
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_prepend (ClutterModel *model,
|
|
...)
|
|
{
|
|
ClutterModelIter *iter;
|
|
va_list args;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
|
|
iter = CLUTTER_MODEL_GET_CLASS (model)->insert_row (model, 0);
|
|
g_assert (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
va_start (args, model);
|
|
clutter_model_iter_set_internal_valist (iter, args);
|
|
va_end (args);
|
|
|
|
g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
|
|
|
|
g_object_unref (iter);
|
|
}
|
|
|
|
|
|
/**
|
|
* clutter_model_insert:
|
|
* @model: a #ClutterModel
|
|
* @row: the position to insert the new row
|
|
* @Varargs: pairs of column number and value, terminated with -1
|
|
*
|
|
* Inserts a new row to the #ClutterModel at @row, setting the row
|
|
* values upon creation. For example, to insert a new row at index 100,
|
|
* where column 0 is type %G_TYPE_INT and column 1 is of type
|
|
* %G_TYPE_STRING:
|
|
*
|
|
* <informalexample><programlisting>
|
|
* ClutterModel *model;
|
|
* model = clutter_model_default_new (2,
|
|
* G_TYPE_INT, "Score",
|
|
* G_TYPE_STRING, "Team");
|
|
* clutter_model_insert (model, 3, 0, 42, 1, "Team #1", -1);
|
|
* </programlisting></informalexample>
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_insert (ClutterModel *model,
|
|
guint row,
|
|
...)
|
|
{
|
|
ClutterModelIter *iter;
|
|
va_list args;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
|
|
iter = CLUTTER_MODEL_GET_CLASS (model)->insert_row (model, row);
|
|
g_assert (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
/* set_valist() will call clutter_model_resort() if one of the
|
|
* passed columns matches the model sorting column index
|
|
*/
|
|
va_start (args, row);
|
|
clutter_model_iter_set_internal_valist (iter, args);
|
|
va_end (args);
|
|
|
|
g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
|
|
|
|
g_object_unref (iter);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_insertv:
|
|
* @model: a #ClutterModel
|
|
* @row: row index
|
|
* @n_columns: the number of columns and values to set
|
|
* @columns: (array length=n_columns): a vector containing the columns to set
|
|
* @values: (array length=n_columns): a vector containing the values for the cells
|
|
*
|
|
* Inserts data at @row into the #ClutterModel, setting the row
|
|
* values for the given @columns upon creation.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_insertv (ClutterModel *model,
|
|
guint row,
|
|
guint n_columns,
|
|
guint *columns,
|
|
GValue *values)
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
ClutterModelIter *iter;
|
|
gint i;
|
|
gboolean resort = FALSE;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
g_return_if_fail (n_columns <= clutter_model_get_n_columns (model));
|
|
g_return_if_fail (columns != NULL);
|
|
g_return_if_fail (values != NULL);
|
|
|
|
priv = model->priv;
|
|
|
|
iter = CLUTTER_MODEL_GET_CLASS (model)->insert_row (model, row);
|
|
g_assert (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
for (i = 0; i < n_columns; i++)
|
|
{
|
|
if (priv->sort_column == columns[i])
|
|
resort = TRUE;
|
|
|
|
clutter_model_iter_set_value (iter, columns[i], &values[i]);
|
|
}
|
|
|
|
g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
|
|
|
|
if (resort)
|
|
clutter_model_resort (model);
|
|
|
|
g_object_unref (iter);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_insert_value:
|
|
* @model: a #ClutterModel
|
|
* @row: position of the row to modify
|
|
* @column: column to modify
|
|
* @value: new value for the cell
|
|
*
|
|
* Sets the data in the cell specified by @iter and @column. The type of
|
|
* @value must be convertable to the type of the column. If the row does
|
|
* not exist then it is created.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_insert_value (ClutterModel *model,
|
|
guint row,
|
|
guint column,
|
|
const GValue *value)
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
ClutterModelClass *klass;
|
|
ClutterModelIter *iter;
|
|
gboolean added = FALSE;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
|
|
priv = model->priv;
|
|
klass = CLUTTER_MODEL_GET_CLASS (model);
|
|
|
|
iter = klass->get_iter_at_row (model, row);
|
|
if (!iter)
|
|
{
|
|
iter = klass->insert_row (model, row);
|
|
added = TRUE;
|
|
}
|
|
|
|
g_assert (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
clutter_model_iter_set_value (iter, column, value);
|
|
|
|
if (added)
|
|
g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
|
|
|
|
if (priv->sort_column == column)
|
|
clutter_model_resort (model);
|
|
|
|
g_object_unref (iter);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_remove:
|
|
* @model: a #ClutterModel
|
|
* @row: position of row to remove
|
|
*
|
|
* Removes the row at the given position from the model.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_remove (ClutterModel *model,
|
|
guint row)
|
|
{
|
|
ClutterModelClass *klass;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
|
|
klass = CLUTTER_MODEL_GET_CLASS (model);
|
|
if (klass->remove_row)
|
|
klass->remove_row (model, row);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_get_column_name:
|
|
* @model: #ClutterModel
|
|
* @column: the column number
|
|
*
|
|
* Retrieves the name of the @column
|
|
*
|
|
* Return value: the name of the column. The model holds the returned
|
|
* string, and it should not be modified or freed
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
G_CONST_RETURN gchar *
|
|
clutter_model_get_column_name (ClutterModel *model,
|
|
guint column)
|
|
{
|
|
ClutterModelClass *klass;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL (model), NULL);
|
|
|
|
if (column < 0 || column >= clutter_model_get_n_columns (model))
|
|
{
|
|
g_warning ("%s: Invalid column id value %d\n", G_STRLOC, column);
|
|
return NULL;
|
|
}
|
|
|
|
klass = CLUTTER_MODEL_GET_CLASS (model);
|
|
if (klass->get_column_name)
|
|
return klass->get_column_name (model, column);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_get_column_type:
|
|
* @model: #ClutterModel
|
|
* @column: the column number
|
|
*
|
|
* Retrieves the type of the @column.
|
|
*
|
|
* Return value: the type of the column.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
GType
|
|
clutter_model_get_column_type (ClutterModel *model,
|
|
guint column)
|
|
{
|
|
ClutterModelClass *klass;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL (model), G_TYPE_INVALID);
|
|
|
|
if (column < 0 || column >= clutter_model_get_n_columns (model))
|
|
{
|
|
g_warning ("%s: Invalid column id value %d\n", G_STRLOC, column);
|
|
return G_TYPE_INVALID;
|
|
}
|
|
|
|
klass = CLUTTER_MODEL_GET_CLASS (model);
|
|
if (klass->get_column_type)
|
|
return klass->get_column_type (model, column);
|
|
|
|
return G_TYPE_INVALID;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_get_iter_at_row:
|
|
* @model: a #ClutterModel
|
|
* @row: position of the row to retrieve
|
|
*
|
|
* Retrieves a #ClutterModelIter representing the row at the given index.
|
|
*
|
|
* If a filter function has been set using clutter_model_set_filter()
|
|
* then the @model implementation will return the first non filtered
|
|
* row.
|
|
*
|
|
* Return value: (transfer full): A new #ClutterModelIter, or %NULL if @row was
|
|
* out of bounds. When done using the iterator object, call g_object_unref()
|
|
* to deallocate its resources
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
ClutterModelIter *
|
|
clutter_model_get_iter_at_row (ClutterModel *model,
|
|
guint row)
|
|
{
|
|
ClutterModelClass *klass;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL (model), NULL);
|
|
|
|
klass = CLUTTER_MODEL_GET_CLASS (model);
|
|
if (klass->get_iter_at_row)
|
|
return klass->get_iter_at_row (model, row);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* clutter_model_get_first_iter:
|
|
* @model: a #ClutterModel
|
|
*
|
|
* Retrieves a #ClutterModelIter representing the first non-filtered
|
|
* row in @model.
|
|
*
|
|
* Return value: (transfer full): A new #ClutterModelIter.
|
|
* Call g_object_unref() when done using it
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
ClutterModelIter *
|
|
clutter_model_get_first_iter (ClutterModel *model)
|
|
{
|
|
ClutterModelIter *retval;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL (model), NULL);
|
|
|
|
retval = clutter_model_get_iter_at_row (model, 0);
|
|
if (retval != NULL)
|
|
{
|
|
g_assert (clutter_model_filter_iter (model, retval) != FALSE);
|
|
g_assert (clutter_model_iter_get_row (retval) == 0);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_get_last_iter:
|
|
* @model: a #ClutterModel
|
|
*
|
|
* Retrieves a #ClutterModelIter representing the last non-filtered
|
|
* row in @model.
|
|
*
|
|
* Return value: (transfer full): A new #ClutterModelIter.
|
|
* Call g_object_unref() when done using it
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
ClutterModelIter *
|
|
clutter_model_get_last_iter (ClutterModel *model)
|
|
{
|
|
ClutterModelIter *retval;
|
|
guint length;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL (model), NULL);
|
|
|
|
length = clutter_model_get_n_rows (model);
|
|
retval = clutter_model_get_iter_at_row (model, length - 1);
|
|
if (retval != NULL)
|
|
g_assert (clutter_model_filter_iter (model, retval) != FALSE);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_get_n_rows:
|
|
* @model: a #ClutterModel
|
|
*
|
|
* Retrieves the number of rows inside @model, eventually taking
|
|
* into account any filtering function set using clutter_model_set_filter().
|
|
*
|
|
* Return value: The length of the @model. If there is a filter set, then
|
|
* the length of the filtered @model is returned.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
guint
|
|
clutter_model_get_n_rows (ClutterModel *model)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL (model), 0);
|
|
|
|
return CLUTTER_MODEL_GET_CLASS (model)->get_n_rows (model);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_set_sorting_column:
|
|
* @model: a #ClutterModel
|
|
* @column: the column of the @model to sort, or -1
|
|
*
|
|
* Sets the model to sort by @column. If @column is a negative value
|
|
* the sorting column will be unset.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_set_sorting_column (ClutterModel *model,
|
|
gint column)
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
priv = model->priv;
|
|
|
|
/* The extra comparison for >= 0 is because column gets promoted to
|
|
unsigned in the second comparison */
|
|
if (column >= 0 && column >= clutter_model_get_n_columns (model))
|
|
{
|
|
g_warning ("%s: Invalid column id value %d\n", G_STRLOC, column);
|
|
return;
|
|
}
|
|
|
|
priv->sort_column = column;
|
|
|
|
if (priv->sort_column >= 0)
|
|
clutter_model_resort (model);
|
|
|
|
g_signal_emit (model, model_signals[SORT_CHANGED], 0);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_get_sorting_column:
|
|
* @model: a #ClutterModel
|
|
*
|
|
* Retrieves the number of column used for sorting the @model.
|
|
*
|
|
* Return value: a column number, or -1 if the model is not sorted
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
gint
|
|
clutter_model_get_sorting_column (ClutterModel *model)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL (model), -1);
|
|
|
|
return model->priv->sort_column;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_foreach:
|
|
* @model: a #ClutterModel
|
|
* @func: (scope call): a #ClutterModelForeachFunc
|
|
* @user_data: user data to pass to @func
|
|
*
|
|
* Calls @func for each row in the model.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_foreach (ClutterModel *model,
|
|
ClutterModelForeachFunc func,
|
|
gpointer user_data)
|
|
{
|
|
ClutterModelIter *iter;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
|
|
iter = clutter_model_get_first_iter (model);
|
|
if (!iter)
|
|
return;
|
|
|
|
while (!clutter_model_iter_is_last (iter))
|
|
{
|
|
if (clutter_model_filter_iter (model, iter))
|
|
{
|
|
if (!func (model, iter, user_data))
|
|
break;
|
|
}
|
|
|
|
iter = clutter_model_iter_next (iter);
|
|
}
|
|
|
|
g_object_unref (iter);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_set_sort:
|
|
* @model: a #ClutterModel
|
|
* @column: the column to sort on
|
|
* @func: (allow-none): a #ClutterModelSortFunc, or #NULL
|
|
* @user_data: user data to pass to @func, or #NULL
|
|
* @notify: destroy notifier of @user_data, or #NULL
|
|
*
|
|
* Sorts @model using the given sorting function.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_set_sort (ClutterModel *model,
|
|
gint column,
|
|
ClutterModelSortFunc func,
|
|
gpointer user_data,
|
|
GDestroyNotify notify)
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
g_return_if_fail ((func != NULL && column >= 0) ||
|
|
(func == NULL && column == -1));
|
|
|
|
priv = model->priv;
|
|
|
|
if (priv->sort_notify)
|
|
priv->sort_notify (priv->sort_data);
|
|
|
|
priv->sort_func = func;
|
|
priv->sort_data = user_data;
|
|
priv->sort_notify = notify;
|
|
|
|
/* This takes care of calling _model_sort & emitting the signal*/
|
|
clutter_model_set_sorting_column (model, column);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_set_filter:
|
|
* @model: a #ClutterModel
|
|
* @func: (allow-none): a #ClutterModelFilterFunc, or #NULL
|
|
* @user_data: user data to pass to @func, or #NULL
|
|
* @notify: destroy notifier of @user_data, or #NULL
|
|
*
|
|
* Filters the @model using the given filtering function.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_set_filter (ClutterModel *model,
|
|
ClutterModelFilterFunc func,
|
|
gpointer user_data,
|
|
GDestroyNotify notify)
|
|
{
|
|
ClutterModelPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL (model));
|
|
priv = model->priv;
|
|
|
|
if (priv->filter_notify)
|
|
priv->filter_notify (priv->filter_data);
|
|
|
|
priv->filter_func = func;
|
|
priv->filter_data = user_data;
|
|
priv->filter_notify = notify;
|
|
|
|
g_signal_emit (model, model_signals[FILTER_CHANGED], 0);
|
|
g_object_notify (G_OBJECT (model), "filter-set");
|
|
}
|
|
|
|
/**
|
|
* clutter_model_get_filter_set:
|
|
* @model: a #ClutterModel
|
|
*
|
|
* Returns whether the @model has a filter in place, set
|
|
* using clutter_model_set_filter()
|
|
*
|
|
* Return value: %TRUE if a filter is set
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
gboolean
|
|
clutter_model_get_filter_set (ClutterModel *model)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL (model), FALSE);
|
|
|
|
return model->priv->filter_func != NULL;
|
|
}
|
|
|
|
/*
|
|
* ClutterModelIter Object
|
|
*/
|
|
|
|
/**
|
|
* SECTION:clutter-model-iter
|
|
* @short_description: Iterates through a model
|
|
*
|
|
* #ClutterModelIter is an object used for iterating through all the rows
|
|
* of a #ClutterModel. It allows setting and getting values on the row
|
|
* which is currently pointing at.
|
|
*
|
|
* A #ClutterModelIter represents a position between two elements
|
|
* of the sequence. For example, the iterator returned by
|
|
* clutter_model_get_first_iter() represents the gap immediately before
|
|
* the first row of the #ClutterModel, and the iterator returned by
|
|
* clutter_model_get_last_iter() represents the gap immediately after the
|
|
* last row.
|
|
*
|
|
* A #ClutterModelIter can only be created by a #ClutterModel implementation
|
|
* and it is valid as long as the model does not change.
|
|
*
|
|
* #ClutterModelIter is available since Clutter 0.6
|
|
*/
|
|
|
|
G_DEFINE_ABSTRACT_TYPE (ClutterModelIter, clutter_model_iter, G_TYPE_OBJECT);
|
|
|
|
#define CLUTTER_MODEL_ITER_GET_PRIVATE(obj) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
|
|
CLUTTER_TYPE_MODEL_ITER, ClutterModelIterPrivate))
|
|
|
|
struct _ClutterModelIterPrivate
|
|
{
|
|
ClutterModel *model;
|
|
|
|
gint row;
|
|
|
|
guint ignore_sort : 1;
|
|
};
|
|
|
|
enum
|
|
{
|
|
ITER_PROP_0,
|
|
|
|
ITER_PROP_MODEL,
|
|
ITER_PROP_ROW
|
|
};
|
|
|
|
static ClutterModel *
|
|
clutter_model_iter_real_get_model (ClutterModelIter *iter)
|
|
{
|
|
return iter->priv->model;
|
|
}
|
|
|
|
static guint
|
|
clutter_model_iter_real_get_row (ClutterModelIter *iter)
|
|
{
|
|
return iter->priv->row;
|
|
}
|
|
|
|
/* private function */
|
|
void
|
|
clutter_model_iter_set_row (ClutterModelIter *iter,
|
|
guint row)
|
|
{
|
|
iter->priv->row = row;
|
|
}
|
|
|
|
static void
|
|
clutter_model_iter_get_value_unimplemented (ClutterModelIter *iter,
|
|
guint column,
|
|
GValue *value)
|
|
{
|
|
g_warning ("%s: Iterator of type '%s' does not implement the "
|
|
"ClutterModelIter::get_value() virtual function",
|
|
G_STRLOC,
|
|
g_type_name (G_OBJECT_TYPE (iter)));
|
|
}
|
|
|
|
static void
|
|
clutter_model_iter_set_value_unimplemented (ClutterModelIter *iter,
|
|
guint column,
|
|
const GValue *value)
|
|
{
|
|
g_warning ("%s: Iterator of type '%s' does not implement the "
|
|
"ClutterModelIter::set_value() virtual function",
|
|
G_STRLOC,
|
|
g_type_name (G_OBJECT_TYPE (iter)));
|
|
}
|
|
|
|
static gboolean
|
|
clutter_model_iter_is_first_unimplemented (ClutterModelIter *iter)
|
|
{
|
|
g_warning ("%s: Iterator of type '%s' does not implement the "
|
|
"ClutterModelIter::is_first() virtual function",
|
|
G_STRLOC,
|
|
g_type_name (G_OBJECT_TYPE (iter)));
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
clutter_model_iter_is_last_unimplemented (ClutterModelIter *iter)
|
|
{
|
|
g_warning ("%s: Iterator of type '%s' does not implement the "
|
|
"ClutterModelIter::is_last() virtual function",
|
|
G_STRLOC,
|
|
g_type_name (G_OBJECT_TYPE (iter)));
|
|
return FALSE;
|
|
}
|
|
|
|
static ClutterModelIter *
|
|
clutter_model_iter_next_unimplemented (ClutterModelIter *iter)
|
|
{
|
|
g_warning ("%s: Iterator of type '%s' does not implement the "
|
|
"ClutterModelIter::next() virtual function",
|
|
G_STRLOC,
|
|
g_type_name (G_OBJECT_TYPE (iter)));
|
|
return NULL;
|
|
}
|
|
|
|
static ClutterModelIter *
|
|
clutter_model_iter_prev_unimplemented (ClutterModelIter *iter)
|
|
{
|
|
g_warning ("%s: Iterator of type '%s' does not implement the "
|
|
"ClutterModelIter::prev() virtual function",
|
|
G_STRLOC,
|
|
g_type_name (G_OBJECT_TYPE (iter)));
|
|
return NULL;
|
|
}
|
|
|
|
static ClutterModelIter *
|
|
clutter_model_iter_copy_unimplemented (ClutterModelIter *iter)
|
|
{
|
|
g_warning ("%s: Iterator of type '%s' does not implement the "
|
|
"ClutterModelIter::copy() virtual function",
|
|
G_STRLOC,
|
|
g_type_name (G_OBJECT_TYPE (iter)));
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
clutter_model_iter_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterModelIter *iter = CLUTTER_MODEL_ITER (object);
|
|
ClutterModelIterPrivate *priv = iter->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case ITER_PROP_MODEL:
|
|
g_value_set_object (value, priv->model);
|
|
break;
|
|
case ITER_PROP_ROW:
|
|
g_value_set_uint (value, priv->row);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_model_iter_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterModelIter *iter = CLUTTER_MODEL_ITER (object);
|
|
ClutterModelIterPrivate *priv = iter->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case ITER_PROP_MODEL:
|
|
priv->model = g_value_get_object (value);
|
|
break;
|
|
case ITER_PROP_ROW:
|
|
priv->row = g_value_get_uint (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_model_iter_class_init (ClutterModelIterClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GParamSpec *pspec;
|
|
|
|
gobject_class->get_property = clutter_model_iter_get_property;
|
|
gobject_class->set_property = clutter_model_iter_set_property;
|
|
|
|
klass->get_model = clutter_model_iter_real_get_model;
|
|
klass->get_row = clutter_model_iter_real_get_row;
|
|
klass->is_first = clutter_model_iter_is_first_unimplemented;
|
|
klass->is_last = clutter_model_iter_is_last_unimplemented;
|
|
klass->next = clutter_model_iter_next_unimplemented;
|
|
klass->prev = clutter_model_iter_prev_unimplemented;
|
|
klass->get_value = clutter_model_iter_get_value_unimplemented;
|
|
klass->set_value = clutter_model_iter_set_value_unimplemented;
|
|
klass->copy = clutter_model_iter_copy_unimplemented;
|
|
|
|
/* Properties */
|
|
|
|
/**
|
|
* ClutterModelIter:model:
|
|
*
|
|
* A reference to the #ClutterModel that this iter belongs to.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
pspec = g_param_spec_object ("model",
|
|
"Model",
|
|
"The model to which the iterator belongs to",
|
|
CLUTTER_TYPE_MODEL,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, ITER_PROP_MODEL, pspec);
|
|
|
|
/**
|
|
* ClutterModelIter:row:
|
|
*
|
|
* The row number to which this iter points to.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
pspec = g_param_spec_uint ("row",
|
|
"Row",
|
|
"The row to which the iterator points to",
|
|
0, G_MAXUINT, 0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, ITER_PROP_ROW, pspec);
|
|
|
|
g_type_class_add_private (gobject_class, sizeof (ClutterModelIterPrivate));
|
|
}
|
|
|
|
static void
|
|
clutter_model_iter_init (ClutterModelIter *self)
|
|
{
|
|
ClutterModelIterPrivate *priv;
|
|
|
|
self->priv = priv = CLUTTER_MODEL_ITER_GET_PRIVATE (self);
|
|
|
|
priv->model = NULL;
|
|
priv->row = 0;
|
|
|
|
priv->ignore_sort = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Public functions
|
|
*/
|
|
|
|
static void
|
|
clutter_model_iter_set_internal_valist (ClutterModelIter *iter,
|
|
va_list args)
|
|
{
|
|
ClutterModel *model;
|
|
ClutterModelIterPrivate *priv;
|
|
guint column = 0;
|
|
gboolean sort = FALSE;
|
|
|
|
priv = iter->priv;
|
|
model = priv->model;
|
|
g_assert (CLUTTER_IS_MODEL (model));
|
|
|
|
column = va_arg (args, gint);
|
|
|
|
/* Don't want to sort while setting lots of fields, leave that till the end
|
|
*/
|
|
priv->ignore_sort = TRUE;
|
|
while (column != -1)
|
|
{
|
|
GValue value = { 0, };
|
|
gchar *error = NULL;
|
|
|
|
if (column < 0 || column >= clutter_model_get_n_columns (model))
|
|
{
|
|
g_warning ("%s: Invalid column number %d added to iter "
|
|
"(remember to end you list of columns with a -1)",
|
|
G_STRLOC, column);
|
|
break;
|
|
}
|
|
g_value_init (&value, clutter_model_get_column_type (model, column));
|
|
|
|
G_VALUE_COLLECT (&value, args, 0, &error);
|
|
if (error)
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, error);
|
|
g_free (error);
|
|
|
|
/* Leak value as it might not be in a sane state */
|
|
break;
|
|
}
|
|
|
|
clutter_model_iter_set_value (iter, column, &value);
|
|
|
|
g_value_unset (&value);
|
|
|
|
if (column == clutter_model_get_sorting_column (model))
|
|
sort = TRUE;
|
|
|
|
column = va_arg (args, gint);
|
|
}
|
|
|
|
priv->ignore_sort = FALSE;
|
|
if (sort)
|
|
clutter_model_resort (model);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_iter_set_valist:
|
|
* @iter: a #ClutterModelIter
|
|
* @args: va_list of column/value pairs, terminiated by -1
|
|
*
|
|
* See clutter_model_iter_set(); this version takes a va_list for language
|
|
* bindings.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_iter_set_valist (ClutterModelIter *iter,
|
|
va_list args)
|
|
{
|
|
ClutterModelIterPrivate *priv;
|
|
ClutterModel *model;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
clutter_model_iter_set_internal_valist (iter, args);
|
|
|
|
priv = iter->priv;
|
|
model = priv->model;
|
|
g_assert (CLUTTER_IS_MODEL (model));
|
|
|
|
g_signal_emit (model, model_signals[ROW_CHANGED], 0, iter);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_iter_get:
|
|
* @iter: a #ClutterModelIter
|
|
* @Varargs: a list of column/return location pairs, terminated by -1
|
|
*
|
|
* Gets the value of one or more cells in the row referenced by @iter. The
|
|
* variable argument list should contain integer column numbers, each column
|
|
* column number followed by a place to store the value being retrieved. The
|
|
* list is terminated by a -1.
|
|
*
|
|
* For example, to get a value from column 0 with type %G_TYPE_STRING use:
|
|
* <informalexample><programlisting>
|
|
* clutter_model_iter_get (iter, 0, &place_string_here, -1);
|
|
* </programlisting></informalexample>
|
|
*
|
|
* where place_string_here is a gchar* to be filled with the string. If
|
|
* appropriate, the returned values have to be freed or unreferenced.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_iter_get (ClutterModelIter *iter,
|
|
...)
|
|
{
|
|
va_list args;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
va_start (args, iter);
|
|
clutter_model_iter_get_valist (iter, args);
|
|
va_end (args);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_iter_get_value:
|
|
* @iter: a #ClutterModelIter
|
|
* @column: column number to retrieve the value from
|
|
* @value: (out): an empty #GValue to set
|
|
*
|
|
* Sets an initializes @value to that at @column. When done with @value,
|
|
* g_value_unset() needs to be called to free any allocated memory.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_iter_get_value (ClutterModelIter *iter,
|
|
guint column,
|
|
GValue *value)
|
|
{
|
|
ClutterModelIterClass *klass;
|
|
ClutterModel *model;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
model = iter->priv->model;
|
|
|
|
g_value_init (value, clutter_model_get_column_type (model, column));
|
|
|
|
klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
|
|
if (klass && klass->get_value)
|
|
klass->get_value (iter, column, value);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_iter_get_valist:
|
|
* @iter: a #ClutterModelIter
|
|
* @args: a list of column/return location pairs, terminated by -1
|
|
*
|
|
* See clutter_model_iter_get(). This version takes a va_list for language
|
|
* bindings.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_iter_get_valist (ClutterModelIter *iter,
|
|
va_list args)
|
|
{
|
|
ClutterModelIterPrivate *priv;
|
|
ClutterModel *model;
|
|
guint column = 0;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
priv = iter->priv;
|
|
model = priv->model;
|
|
g_assert (CLUTTER_IS_MODEL (model));
|
|
|
|
column = va_arg (args, gint);
|
|
|
|
while (column != -1)
|
|
{
|
|
GValue value = { 0, };
|
|
gchar *error = NULL;
|
|
|
|
if (column < 0 || column >= clutter_model_get_n_columns (model))
|
|
{
|
|
g_warning ("%s: Invalid column number %d added to iter "
|
|
"(remember to end you list of columns with a -1)",
|
|
G_STRLOC, column);
|
|
break;
|
|
}
|
|
|
|
/* this one will take care of initialising value to the
|
|
* correct type
|
|
*/
|
|
clutter_model_iter_get_value (iter, column, &value);
|
|
|
|
G_VALUE_LCOPY (&value, args, 0, &error);
|
|
if (error)
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, error);
|
|
g_free (error);
|
|
|
|
/* Leak value as it might not be in a sane state */
|
|
break;
|
|
}
|
|
|
|
g_value_unset (&value);
|
|
|
|
column = va_arg (args, gint);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_model_iter_set:
|
|
* @iter: a #ClutterModelIter
|
|
* @Varargs: a list of column/return location pairs, terminated by -1
|
|
*
|
|
* Sets the value of one or more cells in the row referenced by @iter. The
|
|
* variable argument list should contain integer column numbers, each column
|
|
* column number followed by the value to be set. The list is terminated by a
|
|
* -1.
|
|
*
|
|
* For example, to set column 0 with type %G_TYPE_STRING, use:
|
|
* <informalexample><programlisting>
|
|
* clutter_model_iter_set (iter, 0, "foo", -1);
|
|
* </programlisting></informalexample>
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_iter_set (ClutterModelIter *iter,
|
|
...)
|
|
{
|
|
va_list args;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
va_start (args, iter);
|
|
clutter_model_iter_set_valist (iter, args);
|
|
va_end (args);
|
|
}
|
|
|
|
|
|
/**
|
|
* clutter_model_iter_set_value:
|
|
* @iter: a #ClutterModelIter
|
|
* @column: column number to retrieve the value from
|
|
* @value: new value for the cell
|
|
*
|
|
* Sets the data in the cell specified by @iter and @column. The type of
|
|
* @value must be convertable to the type of the column.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_model_iter_set_value (ClutterModelIter *iter,
|
|
guint column,
|
|
const GValue *value)
|
|
{
|
|
ClutterModelIterClass *klass;
|
|
|
|
g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter));
|
|
|
|
klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
|
|
if (klass && klass->set_value)
|
|
klass->set_value (iter, column, value);
|
|
}
|
|
|
|
/**
|
|
* clutter_model_iter_is_first:
|
|
* @iter: a #ClutterModelIter
|
|
*
|
|
* Gets whether the current iterator is at the beginning of the model
|
|
* to which it belongs.
|
|
*
|
|
* Return value: #TRUE if @iter is the first iter in the filtered model
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
gboolean
|
|
clutter_model_iter_is_first (ClutterModelIter *iter)
|
|
{
|
|
ClutterModelIterClass *klass;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), FALSE);
|
|
|
|
klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
|
|
if (klass && klass->is_first)
|
|
return klass->is_first (iter);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_iter_is_last:
|
|
* @iter: a #ClutterModelIter
|
|
*
|
|
* Gets whether the iterator is at the end of the model to which it
|
|
* belongs.
|
|
*
|
|
* Return value: #TRUE if @iter is the last iter in the filtered model.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
gboolean
|
|
clutter_model_iter_is_last (ClutterModelIter *iter)
|
|
{
|
|
ClutterModelIterClass *klass;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), FALSE);
|
|
|
|
klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
|
|
if (klass && klass->is_last)
|
|
return klass->is_last (iter);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_iter_next:
|
|
* @iter: a #ClutterModelIter
|
|
*
|
|
* Updates the @iter to point at the next position in the model.
|
|
* The model implementation should take into account the presence of
|
|
* a filter function.
|
|
*
|
|
* Return value: (transfer none): The passed iterator, updated to point at the next
|
|
* row in the model.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
ClutterModelIter *
|
|
clutter_model_iter_next (ClutterModelIter *iter)
|
|
{
|
|
ClutterModelIterClass *klass;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), NULL);
|
|
|
|
klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
|
|
if (klass && klass->next)
|
|
return klass->next (iter);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_iter_prev:
|
|
* @iter: a #ClutterModelIter
|
|
*
|
|
* Sets the @iter to point at the previous position in the model.
|
|
* The model implementation should take into account the presence of
|
|
* a filter function.
|
|
*
|
|
* Return value: (transfer none): The passed iterator, updated to point at the previous
|
|
* row in the model.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
ClutterModelIter *
|
|
clutter_model_iter_prev (ClutterModelIter *iter)
|
|
{
|
|
ClutterModelIterClass *klass;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), NULL);
|
|
|
|
klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
|
|
if (klass && klass->prev)
|
|
return klass->prev (iter);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_iter_get_model:
|
|
* @iter: a #ClutterModelIter
|
|
*
|
|
* Retrieves a pointer to the #ClutterModel that this iter is part of.
|
|
*
|
|
* Return value: (transfer none): a pointer to a #ClutterModel.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
ClutterModel *
|
|
clutter_model_iter_get_model (ClutterModelIter *iter)
|
|
{
|
|
ClutterModelIterClass *klass;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), NULL);
|
|
|
|
klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
|
|
if (klass && klass->get_model)
|
|
return klass->get_model (iter);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_iter_get_row:
|
|
* @iter: a #ClutterModelIter
|
|
*
|
|
* Retrieves the position of the row that the @iter points to.
|
|
*
|
|
* Return value: the position of the @iter in the model
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
guint
|
|
clutter_model_iter_get_row (ClutterModelIter *iter)
|
|
{
|
|
ClutterModelIterClass *klass;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), 0);
|
|
|
|
klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
|
|
if (klass && klass->get_row)
|
|
return klass->get_row (iter);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* clutter_model_iter_copy:
|
|
* @iter: a #ClutterModelIter
|
|
*
|
|
* Copies the passed iterator.
|
|
*
|
|
* Return value: (transfer full): a copy of the iterator, or %NULL
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
ClutterModelIter *
|
|
clutter_model_iter_copy (ClutterModelIter *iter)
|
|
{
|
|
ClutterModelIterClass *klass;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), NULL);
|
|
|
|
klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
|
|
if (klass->copy)
|
|
return klass->copy (iter);
|
|
|
|
return NULL;
|
|
}
|