0
+ * Copyright (C) 2006 OpenedHand
0
+ * This library is free software; you can redistribute it and/or
0
+ * modify it under the terms of the GNU Lesser General Public
0
+ * License as published by the Free Software Foundation; either
0
+ * version 2 of the License, or (at your option) any later version.
0
+ * This library is distributed in the hope that it will be useful,
0
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
0
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0
+ * Lesser General Public License for more details.
0
+ * You should have received a copy of the GNU Lesser General Public
0
+ * License along with this library; if not, write to the
0
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
0
+ * Boston, MA 02111-1307, USA.
0
+ * Taken from clutter-list-model.c:
0
+ * Neil Jagdish Patel <njp@o-hand.com>
0
+ * Emmanuele Bassi <ebassi@openedhand.com>
0
+#include <glib-object.h>
0
+#include <clutter/clutter.h>
0
+#include "tweet-status-model.h"
0
+#define TWEET_TYPE_STATUS_MODEL_ITER \
0
+ (tweet_status_model_iter_get_type())
0
+#define TWEET_STATUS_MODEL_ITER(obj) \
0
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), \
0
+ TWEET_TYPE_STATUS_MODEL_ITER, \
0
+ TweetStatusModelIter))
0
+#define TWEET_IS_STATUS_MODEL_ITER(obj) \
0
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
0
+ TWEET_TYPE_STATUS_MODEL_ITER))
0
+#define TWEET_STATUS_MODEL_ITER_CLASS(klass) \
0
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
0
+ TWEET_TYPE_STATUS_MODEL_ITER, \
0
+ TweetStatusModelIterClass))
0
+#define TWEET_IS_STATUS_MODEL_ITER_CLASS(klass) \
0
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
0
+ TWEET_TYPE_STATUS_MODEL_ITER))
0
+#define TWEET_STATUS_MODEL_ITER_GET_CLASS(obj) \
0
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
0
+ TWEET_TYPE_STATUS_MODEL_ITER, \
0
+ TweetStatusModelIterClass))
0
+typedef struct _TweetStatusModelIter TweetStatusModelIter;
0
+typedef struct _ClutterModelIterClass TweetStatusModelIterClass;
0
+struct _TweetStatusModelIter
0
+ ClutterModelIter parent_instance;
0
+ GSequenceIter *seq_iter;
0
+#define TWEET_STATUS_MODEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TWEET_TYPE_STATUS_MODEL, TweetStatusModelPrivate))
0
+struct _TweetStatusModelPrivate
0
+static const gchar *model_names[] = {
0
+static const gint model_columns = G_N_ELEMENTS (model_names);
0
+G_DEFINE_TYPE (TweetStatusModelIter,
0
+ tweet_status_model_iter,
0
+ CLUTTER_TYPE_MODEL_ITER);
0
+tweet_status_model_iter_get_value (ClutterModelIter *iter,
0
+ TweetStatusModelIter *iter_default;
0
+ GValueArray *value_array;
0
+ GValue real_value = { 0, };
0
+ gboolean converted = FALSE;
0
+ iter_default = TWEET_STATUS_MODEL_ITER (iter);
0
+ g_assert (iter_default->seq_iter != NULL);
0
+ value_array = g_sequence_get (iter_default->seq_iter);
0
+ iter_value = g_value_array_get_nth (value_array, column);
0
+ g_assert (iter_value != NULL);
0
+ if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (iter_value)))
0
+ if (!g_value_type_compatible (G_VALUE_TYPE (value),
0
+ G_VALUE_TYPE (iter_value)) &&
0
+ !g_value_type_compatible (G_VALUE_TYPE (iter_value),
0
+ G_VALUE_TYPE (value)))
0
+ g_warning ("%s: Unable to convert from %s to %s",
0
+ g_type_name (G_VALUE_TYPE (value)),
0
+ g_type_name (G_VALUE_TYPE (iter_value)));
0
+ if (!g_value_transform (iter_value, &real_value))
0
+ g_warning ("%s: Unable to make conversion from %s to %s",
0
+ g_type_name (G_VALUE_TYPE (value)),
0
+ g_type_name (G_VALUE_TYPE (iter_value)));
0
+ g_value_unset (&real_value);
0
+ g_value_copy (&real_value, value);
0
+ g_value_unset (&real_value);
0
+ g_value_copy (iter_value, value);
0
+tweet_status_model_iter_set_value (ClutterModelIter *iter,
0
+ TweetStatusModelIter *iter_default;
0
+ GValueArray *value_array;
0
+ GValue real_value = { 0, };
0
+ gboolean converted = FALSE;
0
+ iter_default = TWEET_STATUS_MODEL_ITER (iter);
0
+ g_assert (iter_default->seq_iter != NULL);
0
+ value_array = g_sequence_get (iter_default->seq_iter);
0
+ iter_value = g_value_array_get_nth (value_array, column);
0
+ g_assert (iter_value != NULL);
0
+ if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (iter_value)))
0
+ if (!g_value_type_compatible (G_VALUE_TYPE (value),
0
+ G_VALUE_TYPE (iter_value)) &&
0
+ !g_value_type_compatible (G_VALUE_TYPE (iter_value),
0
+ G_VALUE_TYPE (value)))
0
+ g_warning ("%s: Unable to convert from %s to %s\n",
0
+ g_type_name (G_VALUE_TYPE (value)),
0
+ g_type_name (G_VALUE_TYPE (iter_value)));
0
+ if (!g_value_transform (value, &real_value))
0
+ g_warning ("%s: Unable to make conversion from %s to %s\n",
0
+ g_type_name (G_VALUE_TYPE (value)),
0
+ g_type_name (G_VALUE_TYPE (iter_value)));
0
+ g_value_unset (&real_value);
0
+ g_value_copy (&real_value, iter_value);
0
+ g_value_unset (&real_value);
0
+ g_value_copy (value, iter_value);
0
+tweet_status_model_iter_is_first (ClutterModelIter *iter)
0
+ TweetStatusModelIter *iter_default;
0
+ ClutterModelIter *temp_iter;
0
+ GSequenceIter *begin, *end;
0
+ iter_default = TWEET_STATUS_MODEL_ITER (iter);
0
+ g_assert (iter_default->seq_iter != NULL);
0
+ model = clutter_model_iter_get_model (iter);
0
+ row = clutter_model_iter_get_row (iter);
0
+ begin = g_sequence_get_begin_iter (TWEET_STATUS_MODEL (model)->priv->sequence);
0
+ end = iter_default->seq_iter;
0
+ temp_iter = g_object_new (TWEET_TYPE_STATUS_MODEL_ITER,
0
+ while (!g_sequence_iter_is_begin (begin))
0
+ TWEET_STATUS_MODEL_ITER (temp_iter)->seq_iter = begin;
0
+ g_object_set (G_OBJECT (temp_iter), "row", row, NULL);
0
+ if (clutter_model_filter_iter (model, temp_iter))
0
+ begin = g_sequence_iter_next (begin);
0
+ g_object_unref (temp_iter);
0
+ /* This is because the 'begin_iter' is always *before* the last valid
0
+ * iter, otherwise we'd have endless loops
0
+ end = g_sequence_iter_prev (end);
0
+ return iter_default->seq_iter == end;
0
+tweet_status_model_iter_is_last (ClutterModelIter *iter)
0
+ TweetStatusModelIter *iter_default;
0
+ ClutterModelIter *temp_iter;
0
+ GSequenceIter *begin, *end;
0
+ iter_default = TWEET_STATUS_MODEL_ITER (iter);
0
+ g_assert (iter_default->seq_iter != NULL);
0
+ if (g_sequence_iter_is_end (iter_default->seq_iter))
0
+ model = clutter_model_iter_get_model (iter);
0
+ row = clutter_model_iter_get_row (iter);
0
+ begin = g_sequence_get_end_iter (TWEET_STATUS_MODEL (model)->priv->sequence);
0
+ begin = g_sequence_iter_prev (begin);
0
+ end = iter_default->seq_iter;
0
+ temp_iter = g_object_new (TWEET_TYPE_STATUS_MODEL_ITER,
0
+ while (!g_sequence_iter_is_begin (begin))
0
+ TWEET_STATUS_MODEL_ITER (temp_iter)->seq_iter = begin;
0
+ g_object_set (G_OBJECT (temp_iter), "row", row, NULL);
0
+ if (clutter_model_filter_iter (model, temp_iter))
0
+ begin = g_sequence_iter_prev (begin);
0
+ g_object_unref (temp_iter);
0
+ /* This is because the 'end_iter' is always *after* the last valid iter.
0
+ * Otherwise we'd have endless loops
0
+ end = g_sequence_iter_next (end);
0
+ return iter_default->seq_iter == end;
0
+static ClutterModelIter *
0
+tweet_status_model_iter_next (ClutterModelIter *iter)
0
+ TweetStatusModelIter *iter_default;
0
+ ClutterModelIter *temp_iter;
0
+ ClutterModel *model = NULL;
0
+ GSequenceIter *filter_next;
0
+ iter_default = TWEET_STATUS_MODEL_ITER (iter);
0
+ g_assert (iter_default->seq_iter != NULL);
0
+ model = clutter_model_iter_get_model (iter);
0
+ row = clutter_model_iter_get_row (iter) + 1;
0
+ filter_next = g_sequence_iter_next (iter_default->seq_iter);
0
+ g_assert (filter_next != NULL);
0
+ temp_iter = g_object_new (TWEET_TYPE_STATUS_MODEL_ITER,
0
+ while (!g_sequence_iter_is_end (filter_next))
0
+ TWEET_STATUS_MODEL_ITER (temp_iter)->seq_iter = filter_next;
0
+ g_object_set (G_OBJECT (temp_iter), "row", row, NULL);
0
+ if (clutter_model_filter_iter (model, temp_iter))
0
+ filter_next = g_sequence_iter_next (filter_next);
0
+ g_object_unref (temp_iter);
0
+ /* We do this because the 'end_iter' is always *after* the
0
+ * last valid iter. Otherwise loops will go on forever
0
+ if (filter_next == iter_default->seq_iter)
0
+ filter_next = g_sequence_iter_next (filter_next);
0
+ /* update the iterator and return it */
0
+ g_object_set (G_OBJECT (iter_default), "model", model, "row", row, NULL);
0
+ iter_default->seq_iter = filter_next;
0
+ return CLUTTER_MODEL_ITER (iter_default);
0
+static ClutterModelIter *
0
+tweet_status_model_iter_prev (ClutterModelIter *iter)
0
+ TweetStatusModelIter *iter_default;
0
+ ClutterModelIter *temp_iter;
0
+ GSequenceIter *filter_prev;
0
+ iter_default = TWEET_STATUS_MODEL_ITER (iter);
0
+ g_assert (iter_default->seq_iter != NULL);
0
+ model = clutter_model_iter_get_model (iter);
0
+ row = clutter_model_iter_get_row (iter) - 1;
0
+ filter_prev = g_sequence_iter_prev (iter_default->seq_iter);
0
+ g_assert (filter_prev != NULL);
0
+ temp_iter = g_object_new (TWEET_TYPE_STATUS_MODEL_ITER,
0
+ while (!g_sequence_iter_is_begin (filter_prev))
0
+ TWEET_STATUS_MODEL_ITER (temp_iter)->seq_iter = filter_prev;
0
+ g_object_set (G_OBJECT (temp_iter), "row", row, NULL);
0
+ if (clutter_model_filter_iter (model, temp_iter))
0
+ filter_prev = g_sequence_iter_prev (filter_prev);
0
+ g_object_unref (temp_iter);
0
+ /* We do this because the 'end_iter' is always *after* the last
0
+ * valid iter. Otherwise loops will go on forever
0
+ if (filter_prev == iter_default->seq_iter)
0
+ filter_prev = g_sequence_iter_prev (filter_prev);
0
+ /* update the iterator and return it */
0
+ g_object_set (G_OBJECT (iter_default), "model", model, "row", row, NULL);
0
+ iter_default->seq_iter = filter_prev;
0
+ return CLUTTER_MODEL_ITER (iter_default);
0
+#if CLUTTER_CHECK_VERSION(0, 7, 0)
0
+static ClutterModelIter *
0
+tweet_status_model_iter_copy (ClutterModelIter *iter)
0
+ TweetStatusModelIter *iter_default;
0
+ TweetStatusModelIter *iter_copy;
0
+ iter_default = TWEET_STATUS_MODEL_ITER (iter);
0
+ model = clutter_model_iter_get_model (iter);
0
+ row = clutter_model_iter_get_row (iter) - 1;
0
+ iter_copy = g_object_new (TWEET_TYPE_STATUS_MODEL_ITER,
0
+ /* this is safe, because the seq_iter pointer on the passed
0
+ * iterator will be always be overwritten in ::next or ::prev
0
+ iter_copy->seq_iter = iter_default->seq_iter;
0
+ return CLUTTER_MODEL_ITER (iter_copy);
0
+#endif /* CLUTTER_CHECK_VERSION(0, 7, 0) */
0
+tweet_status_model_iter_class_init (TweetStatusModelIterClass *klass)
0
+ ClutterModelIterClass *iter_class = CLUTTER_MODEL_ITER_CLASS (klass);
0
+ iter_class->get_value = tweet_status_model_iter_get_value;
0
+ iter_class->set_value = tweet_status_model_iter_set_value;
0
+ iter_class->is_first = tweet_status_model_iter_is_first;
0
+ iter_class->is_last = tweet_status_model_iter_is_last;
0
+ iter_class->next = tweet_status_model_iter_next;
0
+ iter_class->prev = tweet_status_model_iter_prev;
0
+#if CLUTTER_CHECK_VERSION(0, 7, 0)
0
+ iter_class->copy = tweet_status_model_iter_copy;
0
+tweet_status_model_iter_init (TweetStatusModelIter *iter)
0
+ iter->seq_iter = NULL;
0
+G_DEFINE_TYPE (TweetStatusModel, tweet_status_model, CLUTTER_TYPE_MODEL);
0
+static ClutterModelIter *
0
+tweet_status_model_get_iter_at_row (ClutterModel *model,
0
+ TweetStatusModelPrivate *priv = TWEET_STATUS_MODEL (model)->priv;
0
+ TweetStatusModelIter *retval;
0
+ if (row >= g_sequence_get_length (priv->sequence))
0
+ retval = g_object_new (TWEET_TYPE_STATUS_MODEL_ITER,
0
+ retval->seq_iter = g_sequence_get_iter_at_pos (priv->sequence, row);
0
+ return CLUTTER_MODEL_ITER (retval);
0
+static ClutterModelIter *
0
+tweet_status_model_insert_row (ClutterModel *model,
0
+ TweetStatusModelPrivate *priv = TWEET_STATUS_MODEL (model)->priv;
0
+ TweetStatusModelIter *retval;
0
+ guint n_columns, i, pos;
0
+ GSequenceIter *seq_iter;
0
+ n_columns = clutter_model_get_n_columns (model);
0
+ array = g_value_array_new (n_columns);
0
+ for (i = 0; i < n_columns; i++)
0
+ g_value_array_append (array, NULL);
0
+ value = g_value_array_get_nth (array, i);
0
+ g_value_init (value, clutter_model_get_column_type (model, i));
0
+ seq_iter = g_sequence_append (priv->sequence, array);
0
+ pos = g_sequence_get_length (priv->sequence);
0
+ seq_iter = g_sequence_prepend (priv->sequence, array);
0
+ seq_iter = g_sequence_get_iter_at_pos (priv->sequence, index_);
0
+ seq_iter = g_sequence_insert_before (seq_iter, array);
0
+ retval = g_object_new (TWEET_TYPE_STATUS_MODEL_ITER,
0
+ retval->seq_iter = seq_iter;
0
+ return CLUTTER_MODEL_ITER (retval);
0
+tweet_status_model_remove_row (ClutterModel *model,
0
+ TweetStatusModelPrivate *priv = TWEET_STATUS_MODEL (model)->priv;
0
+ GSequenceIter *seq_iter;
0
+ seq_iter = g_sequence_get_begin_iter (priv->sequence);
0
+ while (!g_sequence_iter_is_end (seq_iter))
0
+ if (clutter_model_filter_row (model, pos))
0
+ ClutterModelIter *iter;
0
+ iter = g_object_new (TWEET_TYPE_STATUS_MODEL_ITER,
0
+ TWEET_STATUS_MODEL_ITER (iter)->seq_iter = seq_iter;
0
+ /* the actual row is removed from the sequence inside
0
+ * the ::row-removed signal class handler, so that every
0
+ * handler connected to ::row-removed will still get
0
+ * a valid iterator, and every signal connected to
0
+ * ::row-removed with the AFTER flag will get an updated
0
+ g_signal_emit_by_name (model, "row-removed", iter);
0
+ g_object_unref (iter);
0
+ seq_iter = g_sequence_iter_next (seq_iter);
0
+tweet_status_model_get_n_rows (ClutterModel *model)
0
+ TweetStatusModelPrivate *priv = TWEET_STATUS_MODEL (model)->priv;
0
+ return g_sequence_get_length (priv->sequence);
0
+ ClutterModelSortFunc func;
0
+sort_model_default (gconstpointer a,
0
+ GValueArray *row_a = (GValueArray *) a;
0
+ GValueArray *row_b = (GValueArray *) b;
0
+ SortClosure *clos = data;
0
+ return clos->func (clos->model,
0
+ g_value_array_get_nth (row_a, clos->column),
0
+ g_value_array_get_nth (row_b, clos->column),
0
+tweet_status_model_resort (ClutterModel *model,
0
+ ClutterModelSortFunc func,
0
+ SortClosure sort_closure = { NULL, 0, NULL, NULL };
0
+ sort_closure.model = model;
0
+ sort_closure.column = clutter_model_get_sorting_column (model);
0
+ sort_closure.func = func;
0
+ sort_closure.data = data;
0
+ g_sequence_sort (TWEET_STATUS_MODEL (model)->priv->sequence,
0
+tweet_status_model_row_removed (ClutterModel *model,
0
+ ClutterModelIter *iter)
0
+ TweetStatusModelIter *iter_default;
0
+ iter_default = TWEET_STATUS_MODEL_ITER (iter);
0
+ array = g_sequence_get (iter_default->seq_iter);
0
+ g_value_array_free (array);
0
+ g_sequence_remove (iter_default->seq_iter);
0
+ iter_default->seq_iter = NULL;
0
+tweet_status_model_finalize (GObject *gobject)
0
+ TweetStatusModelPrivate *priv = TWEET_STATUS_MODEL (gobject)->priv;
0
+ iter = g_sequence_get_begin_iter (priv->sequence);
0
+ while (!g_sequence_iter_is_end (iter))
0
+ GValueArray *value_array = g_sequence_get (iter);
0
+ g_value_array_free (value_array);
0
+ iter = g_sequence_iter_next (iter);
0
+ g_sequence_free (priv->sequence);
0
+ G_OBJECT_CLASS (tweet_status_model_parent_class)->finalize (gobject);
0
+tweet_status_model_class_init (TweetStatusModelClass *klass)
0
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
0
+ ClutterModelClass *model_class = CLUTTER_MODEL_CLASS (klass);
0
+ g_type_class_add_private (klass, sizeof (TweetStatusModelPrivate));
0
+ gobject_class->finalize = tweet_status_model_finalize;
0
+ model_class->get_n_rows = tweet_status_model_get_n_rows;
0
+ model_class->get_iter_at_row = tweet_status_model_get_iter_at_row;
0
+ model_class->insert_row = tweet_status_model_insert_row;
0
+ model_class->remove_row = tweet_status_model_remove_row;
0
+ model_class->resort = tweet_status_model_resort;
0
+ model_class->row_removed = tweet_status_model_row_removed;
0
+tweet_status_model_init (TweetStatusModel *model)
0
+ ClutterModel *base_model = CLUTTER_MODEL (model);
0
+ TweetStatusModelPrivate *priv;
0
+ GType model_types[] = {
0
+ model->priv = priv = TWEET_STATUS_MODEL_GET_PRIVATE (model);
0
+ priv->sequence = g_sequence_new (NULL);
0
+ clutter_model_set_types (base_model, model_columns, model_types);
0
+ clutter_model_set_names (base_model, model_columns, model_names);
0
+tweet_status_model_new (void)
0
+ return g_object_new (TWEET_TYPE_STATUS_MODEL, NULL);
0
+tweet_status_model_append_status (TweetStatusModel *model,
0
+ TwitterStatus *status)
0
+ g_return_if_fail (TWEET_IS_STATUS_MODEL (model));
0
+ g_return_if_fail (TWITTER_IS_STATUS (status));
0
+ clutter_model_append (CLUTTER_MODEL (model),
0
+tweet_status_model_set_max_size (TweetStatusModel *model,
0
+ TweetStatusModelPrivate *priv;
0
+ g_return_if_fail (TWEET_IS_STATUS_MODEL (model));
0
+ if (priv->max_size != max_size)
0
+ priv->max_size = max_size;