/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * Neil Jagdish Patel * * 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, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * NB: Inspiration for column storage taken from GtkListStore */ /** * SECTION:clutter-model * @short_description: A generic model implementation * * #ClutterModel is a generic list model which can be used to implement the * model-view-controller architectural pattern in Clutter. * * The #ClutterModel object is a list model which can accept most GObject * types as a column type. Internally, it will keep a local copy of the data * passed in (such as a string or a boxed pointer). * * Creating a simple clutter model: * * enum { * COLUMN_INT, * COLUMN_STRING. * N_COLUMNS * }; * * { * ClutterModel *model; * gint i; * * model = clutter_model_new (2, G_TYPE_INT, G_TYPE_STRING); * 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); * } * * * } * * * 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 @begin iterator represents the gap immediately * before the first row, and the @end iterator represents the gap immediately * after the last row. In an empty sequence, the begin and end iterators are * the same. * * Iterating a #ClutterModel: * * enum { * COLUMN_INT, * COLUMN_STRING. * N_COLUMNS * }; * * { * ClutterModel *model; * ClutterModelIter *iter = NULL; * * model = clutter_model_new (2, G_TYPE_INT, G_TYPE_STRING); * * /* Fill the model */ * populate_model (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); * } * * * #ClutterModel is available since Clutter 0.6 */ #include "config.h" #include #include #include #include "clutter-model.h" #include "clutter-debug.h" G_DEFINE_TYPE (ClutterModel, clutter_model, G_TYPE_OBJECT); 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 { GSequence *sequence; GType *column_types; gint n_columns; ClutterModelFilterFunc filter; gpointer filter_data; GDestroyNotify filter_notify; ClutterModelSortFunc sort; guint sort_column; gpointer sort_data; GDestroyNotify sort_notify; }; /* Forwards */ static ClutterModelIter * _model_get_iter_at_row (ClutterModel *model, guint index_); static void clutter_model_real_remove (ClutterModel *model, GSequenceIter *iter); /* GObject stuff */ static void clutter_model_dispose (GObject *object) { ClutterModel *self = CLUTTER_MODEL(object); ClutterModelPrivate *priv; priv = self->priv; G_OBJECT_CLASS (clutter_model_parent_class)->dispose (object); } static void clutter_model_finalize (GObject *object) { ClutterModelPrivate *priv = CLUTTER_MODEL (object)->priv; GSequenceIter *iter; iter = g_sequence_get_end_iter (priv->sequence); while (!g_sequence_iter_is_end (iter)) { clutter_model_real_remove (CLUTTER_MODEL (object), iter); iter = g_sequence_iter_next (iter); } g_sequence_free (priv->sequence); if (priv->sort && priv->sort_data && priv->sort_notify) priv->sort_notify (priv->sort_data); if (priv->filter && priv->filter_data && priv->filter_notify) priv->filter_notify (priv->filter_data); G_OBJECT_CLASS (clutter_model_parent_class)->finalize (object); } static void clutter_model_class_init (ClutterModelClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = clutter_model_finalize; gobject_class->dispose = clutter_model_dispose; klass->get_iter_at_row = _model_get_iter_at_row; /** * 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 * * 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, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); /** * 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 * * 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, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); /** * 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 * * 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, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); /** * 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, g_cclosure_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, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); g_type_class_add_private (gobject_class, sizeof (ClutterModelPrivate)); } static void clutter_model_init (ClutterModel *self) { ClutterModelPrivate *priv; self->priv = priv = CLUTTER_MODEL_GET_PRIVATE (self); priv->sequence = g_sequence_new (NULL); priv->column_types = NULL; priv->n_columns = 0; priv->filter = NULL; priv->filter_data = NULL; priv->filter_notify = NULL; priv->sort = NULL; priv->sort_data = NULL; priv->sort_column = 0; priv->sort_notify = NULL; } static gboolean clutter_model_check_type (type) { 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 (type)) return FALSE; while (type_list[i] != G_TYPE_INVALID) { if (g_type_is_a (type, type_list[i])) return TRUE; i++; } return FALSE; } static gint _model_sort_func (GValueArray *val_a, GValueArray *val_b, ClutterModel *model) { ClutterModelPrivate *priv; const GValue *a; const GValue *b; g_return_val_if_fail (CLUTTER_IS_MODEL (model), 0); g_return_val_if_fail (val_a, 0); g_return_val_if_fail (val_b, 0); priv = model->priv; a = g_value_array_get_nth (val_a, priv->sort_column); b = g_value_array_get_nth (val_b, priv->sort_column); return priv->sort (model, a, b, priv->sort_data); } static void _model_sort (ClutterModel *model) { ClutterModelPrivate *priv; g_return_if_fail (CLUTTER_IS_MODEL (model)); priv = model->priv; if (!priv->sort) return; g_sequence_sort (priv->sequence, (GCompareDataFunc)_model_sort_func, model); } static gboolean _model_filter (ClutterModel *model, ClutterModelIter *iter) { ClutterModelPrivate *priv; gboolean res = TRUE; g_return_val_if_fail (CLUTTER_IS_MODEL (model), TRUE); priv = model->priv; if (!priv->filter) return TRUE; res = priv->filter (model, iter, priv->filter_data); return res; } static void clutter_model_set_n_columns (ClutterModel *model, gint n_columns) { ClutterModelPrivate *priv; GType *new_columns; g_return_if_fail (CLUTTER_IS_MODEL (model)); priv = model->priv; new_columns = g_new0 (GType, n_columns); priv->column_types = new_columns; priv->n_columns = n_columns; } static void clutter_model_set_column_type (ClutterModel *model, gint column, GType type) { ClutterModelPrivate *priv; g_return_if_fail (CLUTTER_IS_MODEL (model)); priv = model->priv; priv->column_types[column] = type; } /** * clutter_model_new: * @n_columns: number of columns in the model * @Varargs: all #GType types for the columns, from first to last * * Creates a new model with @n_columns columns with the types passed in. * * For example, clutter_model_new (3, G_TYPE_INT, G_TYPE_STRING, * GDK_TYPE_PIXBUF); will create a new #ClutterModel with three * columns of type int, string and #GdkPixbuf respectively. * * Return value: a new #ClutterModel * * Since 0.6 */ ClutterModel * clutter_model_new (guint n_columns, ...) { ClutterModel *model; va_list args; gint i; g_return_val_if_fail (n_columns > 0, NULL); model = g_object_new (CLUTTER_TYPE_MODEL, NULL); clutter_model_set_n_columns (model, n_columns); va_start (args, n_columns); for (i = 0; i < n_columns; i++) { GType type = va_arg (args, GType); if (!clutter_model_check_type (type)) { g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type)); g_object_unref (model); return NULL; } clutter_model_set_column_type (model, i, type); } va_end (args); return model; } /** * clutter_model_newv: * @n_columns: number of columns in the model * @types: an array of #GType types for the columns, from first to last * * Non-vararg creation function. Used primarily by language bindings. * * Return value: a new #ClutterModel * * Since 0.6 */ ClutterModel * clutter_model_newv (guint n_columns, GType *types) { ClutterModel *model; gint i; g_return_val_if_fail (n_columns > 0, NULL); model = g_object_new (CLUTTER_TYPE_MODEL, NULL); clutter_model_set_n_columns (model, n_columns); 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])); g_object_unref (model); return NULL; } clutter_model_set_column_type (model, i, types[i]); } return model; } /** * clutter_model_set_types: * @model: a #ClutterModel * @n_columns: number of columns for the model * @types: an array of #GType types * * 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); clutter_model_set_n_columns (model, n_columns); 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]); } } static GValueArray * clutter_model_new_row (ClutterModel *model) { ClutterModelPrivate *priv; GValueArray *row; gint i; g_return_val_if_fail (CLUTTER_IS_MODEL (model), NULL); priv = model->priv; row = g_value_array_new (priv->n_columns); for (i = 0; i < priv->n_columns; i++) { GValue value = { 0, }; g_value_init (&value, priv->column_types[i]); g_value_array_append (row, &value); g_value_unset (&value); } return row; } /** * 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 rows 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, you would write * clutter_model_append (model, 0, 100, 1, "foo", -1);. * * Return value: #TRUE if a new row was successfully added. * * Since 0.6 */ gboolean clutter_model_append (ClutterModel *model, ...) { ClutterModelPrivate *priv; ClutterModelIter *iter; GSequenceIter *seq_iter; GValueArray *row; va_list args; g_return_val_if_fail (CLUTTER_IS_MODEL (model), FALSE); priv = model->priv; row = clutter_model_new_row (model); seq_iter = g_sequence_append (priv->sequence, row); iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, "iter", seq_iter, NULL); va_start (args, model); clutter_model_iter_set_valist (iter, args); va_end (args); /*FIXME: Sort the model if necessary */ g_signal_emit (model, model_signals[ROW_ADDED], 0, iter); g_object_unref (iter); return TRUE; } /** * 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 rows 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, you would write * clutter_model_prepend (model, 0, 100, 1, "string", -1);. * * Return value: #TRUE if a new row was successfully added. * * Since 0.6 */ gboolean clutter_model_prepend (ClutterModel *model, ...) { ClutterModelPrivate *priv; ClutterModelIter *iter; GSequenceIter *seq_iter; GValueArray *row; va_list args; g_return_val_if_fail (CLUTTER_IS_MODEL (model), FALSE); priv = model->priv; row = clutter_model_new_row (model); seq_iter = g_sequence_prepend (priv->sequence, row); iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, "iter", seq_iter, NULL); va_start (args, model); clutter_model_iter_set_valist (iter, args); va_end (args); /*FIXME: Sort the model if necessary */ g_signal_emit (model, model_signals[ROW_ADDED], 0, iter); g_object_unref (iter); return TRUE; } /** * clutter_model_insert: * @model: a #ClutterModel * @index_: 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 @index_, setting the rows 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, you would write * clutter_model_insert (model, 100, 0, 100, 1, "string", -1); * . * * Return value: #TRUE if a new row was successfully added. * * Since 0.6 */ gboolean clutter_model_insert (ClutterModel *model, guint index_, ...) { ClutterModelPrivate *priv; ClutterModelIter *iter; GValueArray *row; GSequenceIter *seq_iter; va_list args; g_return_val_if_fail (CLUTTER_IS_MODEL (model), FALSE); priv = model->priv; row = clutter_model_new_row (model); seq_iter = g_sequence_get_iter_at_pos (priv->sequence, index_); seq_iter = g_sequence_insert_before (seq_iter, row); iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, "iter", seq_iter, NULL); va_start (args, index_); clutter_model_iter_set_valist (iter, args); va_end (args); /* FIXME: Sort? */ g_signal_emit (model, model_signals[ROW_ADDED], 0, iter); g_object_unref (iter); return TRUE; } /** * clutter_model_insert_value: * @model: a #ClutterModel * @index_: 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. * * Return value: #TRUE if the @value was successfully inserted. * * Since 0.6 */ gboolean clutter_model_insert_value (ClutterModel *model, guint index_, guint column, const GValue *value) { ClutterModelPrivate *priv; ClutterModelIter *iter; GSequenceIter *seq_iter; g_return_val_if_fail (CLUTTER_IS_MODEL (model), FALSE); priv = model->priv; seq_iter = g_sequence_get_iter_at_pos (priv->sequence, index_); iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, "iter", seq_iter, NULL); clutter_model_iter_set_value (iter, column, value); if (priv->sort_column == column) _model_sort (model); g_signal_emit (model, model_signals[ROW_CHANGED], 0, iter); g_object_unref (iter); return TRUE; } static void clutter_model_real_remove (ClutterModel *model, GSequenceIter *iter) { ClutterModelPrivate *priv; GValueArray *value_array; g_return_if_fail (CLUTTER_IS_MODEL (model)); priv = model->priv; value_array = g_sequence_get (iter); g_value_array_free (value_array); } /** * clutter_model_remove: * @model: a #ClutterModel * @index_: position of row to remove * * Removes a row at @index_ from the model. * * Since 0.6 */ void clutter_model_remove (ClutterModel *model, guint index_) { ClutterModelPrivate *priv; ClutterModelIter *iter; GSequenceIter *seq_iter; gint i = 0; g_return_if_fail (CLUTTER_IS_MODEL (model)); priv = model->priv; seq_iter = g_sequence_get_begin_iter (priv->sequence); iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, NULL); while (!g_sequence_iter_is_end (seq_iter)) { g_object_set (iter, "iter", seq_iter, NULL); if (_model_filter (model, iter)) { if (i == index_) { g_signal_emit (model, model_signals[ROW_REMOVED], 0, iter); clutter_model_real_remove (model, seq_iter); g_sequence_remove (seq_iter); break; } i++; } seq_iter = g_sequence_iter_next (seq_iter); } g_object_unref (iter); } static ClutterModelIter * _model_get_iter_at_row (ClutterModel *model, guint index_) { g_return_val_if_fail (CLUTTER_IS_MODEL (model), NULL); return g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, "row", index_, NULL); } /** * clutter_model_get_iter_at_row: * @model: a #ClutterModel * @index_: position of the row to retrieve * * Retrieves a #ClutterModelIter representing the row at @index_. * * Return value: A new #ClutterModelIter, or NULL if @index_ was out-of-bounds * * Since 0.6 **/ ClutterModelIter * clutter_model_get_iter_at_row (ClutterModel *model, guint index_) { 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, index_); return NULL; } /** * clutter_model_get_first_iter: * @model: a #ClutterModel * * Retrieves a #ClutterModelIter representing the first row in @model. It calls * #clutter_model_get_iter_at_row with 0 as the index_ parameter. * * Return value: A new #ClutterModelIter * * Since 0.6 **/ ClutterModelIter * clutter_model_get_first_iter (ClutterModel *model) { g_return_val_if_fail (CLUTTER_IS_MODEL (model), NULL); return clutter_model_get_iter_at_row (model, 0); } /** * clutter_model_get_last_iter: * @model: a #ClutterModel * * Retrieves a #ClutterModelIter representing the last row in @model. It calls * #clutter_model_get_iter_at_row with model_length -1 as the index_ parameter. * * Return value: A new #ClutterModelIter * * Since 0.6 **/ ClutterModelIter * clutter_model_get_last_iter (ClutterModel *model) { guint length; g_return_val_if_fail (CLUTTER_IS_MODEL (model), NULL); length = clutter_model_get_n_rows (model); return clutter_model_get_iter_at_row (model, length-1); } /** * clutter_model_get_n_rows: * @model: a #ClutterModel * * Retrieves the number of rows inside @model. * * Return value: The length of the @model. If there is a filter set, then * thelength of the filtered @model is returned. * * Since 0.6 */ guint clutter_model_get_n_rows (ClutterModel *model) { ClutterModelPrivate *priv; ClutterModelIter *iter; GSequenceIter *seq_iter; guint i = 0; g_return_val_if_fail (CLUTTER_IS_MODEL (model), 0); priv = model->priv; seq_iter = g_sequence_get_begin_iter (priv->sequence); iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, NULL); while (!g_sequence_iter_is_end (seq_iter)) { g_object_set (iter, "iter", seq_iter, NULL); if (_model_filter (model, iter)) { i++; } seq_iter = g_sequence_iter_next (seq_iter); } g_object_unref (iter); return i; } /** * clutter_model_set_sorting_column: * @model: a #ClutterModel * @column: the column of the @model to sort * * Sets the model to sort by @column. * * Since 0.6 */ void clutter_model_set_sorting_column (ClutterModel *model, guint column) { ClutterModelPrivate *priv; g_return_if_fail (CLUTTER_IS_MODEL (model)); priv = model->priv; if (column < 0 || column > priv->n_columns) { g_warning ("%s: Invalid column id value %d\n", G_STRLOC, column); return; } priv->sort_column = column; _model_sort (model); g_signal_emit (model, model_signals[SORT_CHANGED], 0); } /** * clutter_model_get_sorting_column: * @model: a #ClutterModelIter * * Retrieves the number of column used for sorting the @model. * * Return value: a column number * * Since 0.6 */ guint clutter_model_get_sorting_column (ClutterModel *model) { g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (model), 0); return model->priv->sort_column; } /** * clutter_model_foreach: * @model: a #ClutterModel * @func: 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) { ClutterModelPrivate *priv; ClutterModelIter *iter; GSequenceIter *seq_iter; g_return_if_fail (CLUTTER_IS_MODEL (model)); priv = model->priv; seq_iter = g_sequence_get_begin_iter (priv->sequence); iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, NULL); while (!g_sequence_iter_is_end (seq_iter)) { g_object_set (iter, "iter", seq_iter, NULL); if (_model_filter (model, iter)) { if (!func (model, iter, user_data)) break; } seq_iter = g_sequence_iter_next (seq_iter); } g_object_unref (iter); } /** * clutter_model_set_sort: * @model: a #ClutterModel * @column: the column to sort on * @func: a #ClutterModelSortFunc, or #NULL * @user_data: user data to pass to @func, or #NULL * @notify: destroy notifier of @user_data, or #NULL * * Sorts the @model using @func. * * Since 0.6 */ void clutter_model_set_sort (ClutterModel *model, guint column, ClutterModelSortFunc func, gpointer user_data, GDestroyNotify notify) { ClutterModelPrivate *priv; g_return_if_fail (CLUTTER_IS_MODEL (model)); priv = model->priv; if (priv->sort_data && priv->sort_notify) priv->sort_notify (priv->sort_data); priv->sort = 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: 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 @func. * * 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_data && priv->filter_notify) priv->filter_notify (priv->filter_data); priv->filter = func; priv->filter_data = user_data; priv->filter_notify = notify; g_signal_emit (model, model_signals[FILTER_CHANGED], 0); } /* * 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 and it is * valid as long as the model does not change. * * #ClutterModelIter is available since Clutter 0.6 */ G_DEFINE_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; GSequenceIter *seq_iter; gboolean ignore_sort; }; enum { ITER_PROP_0, ITER_PROP_MODEL, ITER_PROP_ROW, ITER_PROP_ITER, }; /* Forwards */ static void _model_iter_get_value (ClutterModelIter *iter, guint column, GValue *value); static void _model_iter_set_value (ClutterModelIter *iter, guint column, const GValue *value); static gboolean _model_iter_is_first (ClutterModelIter *iter); static gboolean _model_iter_is_last (ClutterModelIter *iter); static ClutterModelIter * _model_iter_next (ClutterModelIter *iter); static ClutterModelIter * _model_iter_prev (ClutterModelIter *iter); static ClutterModel * _model_iter_get_model (ClutterModelIter *iter); static guint _model_iter_get_row (ClutterModelIter *iter); static void _model_iter_set_row (ClutterModelIter *iter, guint row); /* GObject stuff */ static void clutter_model_iter_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterModelIter *iter = CLUTTER_MODEL_ITER (object); ClutterModelIterPrivate *priv; g_return_if_fail (CLUTTER_IS_MODEL_ITER (object)); 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, clutter_model_iter_get_row (iter)); break; case ITER_PROP_ITER: g_value_set_pointer (value, priv->seq_iter); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } 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; g_return_if_fail (CLUTTER_IS_MODEL_ITER (object)); priv = iter->priv; switch (prop_id) { case ITER_PROP_MODEL: priv->model = g_value_get_object (value); break; case ITER_PROP_ROW: _model_iter_set_row (iter, g_value_get_uint (value)); break; case ITER_PROP_ITER: priv->seq_iter = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void clutter_model_iter_class_init (ClutterModelIterClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->get_property = clutter_model_iter_get_property; gobject_class->set_property = clutter_model_iter_set_property; klass->get_value = _model_iter_get_value; klass->set_value = _model_iter_set_value; klass->is_first = _model_iter_is_first; klass->is_last = _model_iter_is_last; klass->next = _model_iter_next; klass->prev = _model_iter_prev; klass->get_model = _model_iter_get_model; klass->get_row = _model_iter_get_row; /* Properties */ /** * ClutterModelIter:model: * * A reference to the #ClutterModel that this iter belongs to. * * Since: 0.6 */ g_object_class_install_property (gobject_class, ITER_PROP_MODEL, g_param_spec_object ("model", "Model", "A ClutterModel", CLUTTER_TYPE_MODEL, G_PARAM_READWRITE)); /** * ClutterModelIter:row: * * A reference to the row number that this iter represents. * * Since: 0.6 */ g_object_class_install_property (gobject_class, ITER_PROP_ROW, g_param_spec_uint ("row", "Row", "The row number", 0, 65535, 0, G_PARAM_READWRITE)); /** * ClutterModelIter:iter: * * An internal iter reference. This property should only be used when * sub-classing #ClutterModelIter. * * Since: 0.6 */ g_object_class_install_property (gobject_class, ITER_PROP_ITER, g_param_spec_pointer ("iter", "Iter", "The internal iter reference", G_PARAM_READWRITE)); 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->seq_iter = NULL; priv->model = NULL; priv->ignore_sort = FALSE; } static void _model_iter_get_value (ClutterModelIter *iter, guint column, GValue *value) { ClutterModelIterPrivate *priv; GValueArray *value_array; GValue *iter_value; GValue real_value = { 0, }; gboolean converted = FALSE; g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter)); priv = iter->priv; g_return_if_fail (CLUTTER_IS_MODEL (priv->model)); g_return_if_fail (priv->seq_iter); value_array = g_sequence_get (priv->seq_iter); iter_value = g_value_array_get_nth (value_array, column); if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (iter_value))) { if (!g_value_type_compatible (G_VALUE_TYPE (value), G_VALUE_TYPE (iter_value)) && !g_value_type_compatible (G_VALUE_TYPE (iter_value), G_VALUE_TYPE (value))) { g_warning ("%s: Unable to convert from %s to %s\n", G_STRLOC, g_type_name (G_VALUE_TYPE (value)), g_type_name (G_VALUE_TYPE (iter_value))); return; } if (!g_value_transform (value, &real_value)) { g_warning ("%s: Unable to make conversion from %s to %s\n", G_STRLOC, g_type_name (G_VALUE_TYPE (value)), g_type_name (G_VALUE_TYPE (iter_value))); g_value_unset (&real_value); } converted = TRUE; } if (converted) g_value_copy (&real_value, value); else g_value_copy (iter_value, value); } static void _model_iter_set_value (ClutterModelIter *iter, guint column, const GValue *value) { ClutterModelPrivate *model_priv; ClutterModelIterPrivate *priv; GValueArray *value_array; GValue *iter_value; GValue real_value = { 0, }; gboolean converted = FALSE; g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter)); priv = iter->priv; g_return_if_fail (CLUTTER_IS_MODEL (priv->model)); g_return_if_fail (priv->seq_iter); model_priv = priv->model->priv; value_array = g_sequence_get (priv->seq_iter); iter_value = g_value_array_get_nth (value_array, column); if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (iter_value))) { if (!g_value_type_compatible (G_VALUE_TYPE (value), G_VALUE_TYPE (iter_value)) && !g_value_type_compatible (G_VALUE_TYPE (iter_value), G_VALUE_TYPE (value))) { g_warning ("%s: Unable to convert from %s to %s\n", G_STRLOC, g_type_name (G_VALUE_TYPE (value)), g_type_name (G_VALUE_TYPE (iter_value))); return; } if (!g_value_transform (value, &real_value)) { g_warning ("%s: Unable to make conversion from %s to %s\n", G_STRLOC, g_type_name (G_VALUE_TYPE (value)), g_type_name (G_VALUE_TYPE (iter_value))); g_value_unset (&real_value); } converted = TRUE; } if (converted) { g_value_copy (&real_value, iter_value); g_value_unset (&real_value); } else g_value_copy (value, iter_value); /* Check if we need to sort */ if (!priv->ignore_sort) { if (model_priv->sort_column == column && model_priv->sort) { _model_sort (priv->model); } g_signal_emit (priv->model, model_signals[ROW_CHANGED], 0, iter); } } static gboolean _model_iter_is_first (ClutterModelIter *iter) { ClutterModel *model = NULL; ClutterModelPrivate *model_priv; ClutterModelIterPrivate *iter_priv; GSequenceIter *begin, *end; ClutterModelIter *temp_iter; g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), TRUE); iter_priv = iter->priv; model = iter_priv->model; g_return_val_if_fail (CLUTTER_IS_MODEL (model), TRUE); model_priv = model->priv; begin = g_sequence_get_begin_iter (model_priv->sequence); end = iter_priv->seq_iter; temp_iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, NULL); while (!g_sequence_iter_is_begin (begin)) { temp_iter->priv->seq_iter = begin; if (_model_filter (model, temp_iter)) { end = begin; break; } begin = g_sequence_iter_prev (begin); } /* This is because the 'begin_iter' is always *before* the last valid iter. * Otherwise we'd have endless loops */ end = g_sequence_iter_prev (end); g_object_unref (temp_iter); return iter_priv->seq_iter == end; } static gboolean _model_iter_is_last (ClutterModelIter *iter) { ClutterModel *model = NULL; ClutterModelPrivate *model_priv; ClutterModelIterPrivate *iter_priv; GSequenceIter *begin, *end; ClutterModelIter *temp_iter; g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), TRUE); iter_priv = iter->priv; model = iter_priv->model; g_return_val_if_fail (CLUTTER_IS_MODEL (model), TRUE); model_priv = model->priv; if (g_sequence_iter_is_end (iter_priv->seq_iter)) return TRUE; begin = g_sequence_get_end_iter (model_priv->sequence); begin = g_sequence_iter_prev (begin); end = iter_priv->seq_iter; temp_iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, "iter", begin, NULL); while (!g_sequence_iter_is_begin (begin)) { temp_iter->priv->seq_iter = begin; if (_model_filter (model, temp_iter)) { end = begin; break; } begin = g_sequence_iter_prev (begin); } /* This is because the 'end_iter' is always *after* the last valid iter. * Otherwise we'd have endless loops */ end = g_sequence_iter_next (end); g_object_unref (temp_iter); return iter_priv->seq_iter == end; } static ClutterModelIter * _model_iter_next (ClutterModelIter *iter) { ClutterModel *model = NULL; ClutterModelPrivate *model_priv; ClutterModelIterPrivate *iter_priv; GSequenceIter *filter_next; ClutterModelIter *temp_iter; g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), iter); iter_priv = iter->priv; model = iter_priv->model; g_return_val_if_fail (CLUTTER_IS_MODEL (model), iter); model_priv = model->priv; filter_next = g_sequence_iter_next (iter_priv->seq_iter); temp_iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, NULL); while (!g_sequence_iter_is_end (filter_next)) { g_object_set (temp_iter, "iter", filter_next, NULL); if (_model_filter (model, temp_iter)) break; filter_next = g_sequence_iter_next (filter_next); } g_object_unref (temp_iter); /* We do this because the 'end_iter' is always *after* the last valid iter. * Otherwise loops will go on forever */ if (filter_next == iter_priv->seq_iter) filter_next = g_sequence_iter_next (filter_next); g_object_set (iter, "iter", filter_next, NULL); return iter; } static ClutterModelIter * _model_iter_prev (ClutterModelIter *iter) { ClutterModel *model = NULL; ClutterModelPrivate *model_priv; ClutterModelIterPrivate *iter_priv; GSequenceIter *filter_prev; ClutterModelIter *temp_iter; g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), iter); iter_priv = iter->priv; model = iter_priv->model; g_return_val_if_fail (CLUTTER_IS_MODEL (model), iter); model_priv = model->priv; filter_prev = g_sequence_iter_prev (iter_priv->seq_iter); temp_iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, NULL); while (!g_sequence_iter_is_begin (filter_prev)) { g_object_set (temp_iter, "iter", filter_prev, NULL); if (_model_filter (model, temp_iter)) break; filter_prev = g_sequence_iter_prev (filter_prev); } /* We do this because the 'end_iter' is always *after* the last valid iter. * Otherwise loops will go on forever */ if (filter_prev == iter_priv->seq_iter) filter_prev = g_sequence_iter_prev (filter_prev); g_object_set (iter, "iter", filter_prev, NULL); g_object_unref (temp_iter); return iter; } static ClutterModel * _model_iter_get_model (ClutterModelIter *iter) { ClutterModelIterPrivate *priv; g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), NULL); priv = iter->priv; g_return_val_if_fail (CLUTTER_IS_MODEL (priv->model), NULL); return priv->model; } static guint _model_iter_get_row (ClutterModelIter *iter) { ClutterModel *model = NULL; ClutterModelPrivate *model_priv; ClutterModelIterPrivate *iter_priv; GSequenceIter *filter_next; ClutterModelIter *temp_iter; guint row = 0; g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), row); iter_priv = iter->priv; model = iter_priv->model; g_return_val_if_fail (CLUTTER_IS_MODEL (model), row); model_priv = model->priv; filter_next = g_sequence_iter_next (iter_priv->seq_iter); temp_iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, NULL); while (!g_sequence_iter_is_end (filter_next)) { g_object_set (temp_iter, "iter", filter_next, NULL); if (_model_filter (model, temp_iter)) { if (iter_priv->seq_iter == temp_iter->priv->seq_iter) break; row++; } filter_next = g_sequence_iter_next (filter_next); } g_object_unref (temp_iter); return row; } static void _model_iter_set_row (ClutterModelIter *iter, guint row) { ClutterModel *model = NULL; ClutterModelPrivate *model_priv; ClutterModelIterPrivate *iter_priv; GSequenceIter *filter_next; ClutterModelIter *temp_iter; guint i = 0; g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter)); iter_priv = iter->priv; model = iter_priv->model; g_return_if_fail (CLUTTER_IS_MODEL (model)); model_priv = model->priv; filter_next = g_sequence_get_begin_iter (model_priv->sequence); temp_iter = g_object_new (CLUTTER_TYPE_MODEL_ITER, "model", model, NULL); while (!g_sequence_iter_is_end (filter_next)) { g_object_set (temp_iter, "iter", filter_next, NULL); if (_model_filter (model, temp_iter)) { if (i == row) { iter_priv->seq_iter = filter_next; break; } i++; } filter_next = g_sequence_iter_next (filter_next); } g_object_unref (temp_iter); } /* * Public functions */ /** * 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) { ClutterModel *model; ClutterModelPrivate *model_priv; ClutterModelIterPrivate *priv; guint column = 0; gboolean sort = FALSE; g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter)); priv = iter->priv; model = priv->model; g_return_if_fail (CLUTTER_IS_MODEL (model)); model_priv = model->priv; 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 >= model_priv->n_columns) { 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, model_priv->column_types[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 == model_priv->sort_column) sort = TRUE; column = va_arg (args, gint); } priv->ignore_sort = FALSE; if (sort) _model_sort (model); g_signal_emit (model, model_signals[ROW_CHANGED], 0, iter); } /** * clutter_model_iter_get: * @iter: a #ClutterModelIter * @Varargs: va_list of column/return location pairs, terminiated 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, you would write: clutter_model_iter_get * (iter, 0, &place_string_here, -1);, 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: 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; g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter)); klass = CLUTTER_MODEL_ITER_GET_CLASS (iter); g_return_if_fail (klass->get_value != NULL); klass->get_value (iter, column, value); } /** * clutter_model_iter_get_valist: * @iter: a #ClutterModelIter * @args: va_list of column/return location pairs, terminiated 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) { ClutterModel *model; ClutterModelPrivate *model_priv; ClutterModelIterPrivate *priv; guint column = 0; g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter)); priv = iter->priv; model = priv->model; g_return_if_fail (CLUTTER_IS_MODEL (model)); model_priv = model->priv; column = va_arg (args, gint); while (column != -1) { GValue value = { 0, }; gchar *error = NULL; if (column < 0 || column >= model_priv->n_columns) { 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, model_priv->column_types[column]); 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: va_list of column/return location pairs, terminiated 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, you would write: * clutter_model_iter_set (iter, 0, "foo", -1);. * * 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); g_return_if_fail (klass->set_value != NULL); 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); g_return_val_if_fail (klass->is_first != NULL, FALSE); return klass->is_first (iter); } /** * 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); g_return_val_if_fail (klass->is_last != NULL, FALSE); return klass->is_last (iter); } /** * clutter_model_iter_next: * @iter: a #ClutterModelIter * * Sets the @iter to point at the next position in the model after @iter. * * Return value: @iter, pointing at the next position. * * 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); g_return_val_if_fail (klass->next != NULL, NULL); return klass->next (iter); } /** * clutter_model_iter_prev: * @iter: a #ClutterModelIter * * Sets the @iter to point at the previous position in the model after @iter. * * Return value: @iter, pointing at the previous position. * * 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); g_return_val_if_fail (klass->prev != NULL, NULL); return klass->prev (iter); } /** * clutter_model_iter_get_model: * @iter: a #ClutterModelIter * * Returns a pointer to the #ClutterModel that this iter is part of. * * Return value: 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); g_return_val_if_fail (klass->get_model != NULL, NULL); return klass->get_model (iter); } /** * clutter_model_iter_get_row: * @iter: a #ClutterModelIter * * Returns 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), -1); klass = CLUTTER_MODEL_ITER_GET_CLASS (iter); g_return_val_if_fail (klass->get_row != NULL, -1); return klass->get_row (iter); }