From 51a0d5a80f519fb0cdb04db9cca6f0e9b091cd51 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 4 Dec 2007 16:26:19 +0000 Subject: [PATCH] 2007-12-04 Emmanuele Bassi * clutter/clutter-score.[ch]: Reimplement ClutterScore using a N-ary tree to store the timelines. Remove clutter_score_add(): the same functionality can be achieved by passing a NULL parent to clutter_score_append(). * tests/test-score.c: Update ClutterScore test unit, and add debug printouts. --- ChangeLog | 10 + clutter.symbols | 4 +- clutter/clutter-score.c | 737 ++++++++++++++++++++--------- clutter/clutter-score.h | 112 ++--- doc/reference/ChangeLog | 6 + doc/reference/clutter-docs.sgml | 1 + doc/reference/clutter-sections.txt | 9 +- doc/reference/clutter.types | 1 + tests/test-score.c | 61 ++- 9 files changed, 646 insertions(+), 295 deletions(-) diff --git a/ChangeLog b/ChangeLog index 92c32d59b..659ff17d7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2007-12-04 Emmanuele Bassi + + * clutter/clutter-score.[ch]: Reimplement ClutterScore using + a N-ary tree to store the timelines. Remove clutter_score_add(): + the same functionality can be achieved by passing a NULL + parent to clutter_score_append(). + + * tests/test-score.c: Update ClutterScore test unit, and add + debug printouts. + 2007-12-04 Øyvind Kolås * clutter/clutter-shader.c: (bind_glsl_shader): use gchar instead of diff --git a/clutter.symbols b/clutter.symbols index 8527c9128..88008a39d 100644 --- a/clutter.symbols +++ b/clutter.symbols @@ -427,12 +427,14 @@ clutter_rectangle_set_color clutter_redraw clutter_rotate_axis_get_type clutter_rotate_direction_get_type -clutter_score_add clutter_score_append clutter_score_get_loop +clutter_score_get_timeline clutter_score_get_type clutter_score_is_playing +clutter_score_list_timelines clutter_score_new +clutter_score_remove clutter_score_remove_all clutter_score_rewind clutter_score_start diff --git a/clutter/clutter-score.c b/clutter/clutter-score.c index 69d38b3da..88fded462 100644 --- a/clutter/clutter-score.c +++ b/clutter/clutter-score.c @@ -23,26 +23,6 @@ * Boston, MA 02111-1307, USA. */ -/* - * IDEAS: - * API; - * - add() - * + an new timeline to beginning of score - * - append (timeline_existing, timeline_new, delay) - * + appends a new timeline to an existing one - * - * ScoreEntry - * { - * Timeline *base; - * GList *next_timelines; - to start on completion of base, - * (points to score entries) - * Callback id; - * delay - * } - * - * start()/stop(),remove(),remove_all() ? - */ - /** * SECTION:clutter-score * @short_description: Sequencing multiple #ClutterTimelines in order @@ -62,34 +42,48 @@ #include "clutter-private.h" #include "clutter-debug.h" -G_DEFINE_TYPE (ClutterScore, clutter_score, G_TYPE_OBJECT); +typedef struct _ClutterScoreEntry ClutterScoreEntry; -typedef struct ClutterScoreEntry +struct _ClutterScoreEntry { ClutterTimeline *timeline; - gulong handler_id; - GSList *child_entries; - ClutterScore *score; -} -ClutterScoreEntry; + guint id; + + /* signal handler id */ + gulong completed_id; + + ClutterScore *score; + + /* pointer back to the tree structure */ + GNode *node; +}; + +#define CLUTTER_SCORE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_SCORE, ClutterScorePrivate)) struct _ClutterScorePrivate { - GSList *entries; - GHashTable *running_timelines; - guint paused :1; - guint loop : 1; + GNode *root; + + GHashTable *running_timelines; + + guint last_id; + + guint is_paused : 1; + guint loop : 1; }; enum { PROP_0, + PROP_LOOP }; enum { - NEW_TIMELINE, + TIMELINE_STARTED, + TIMELINE_COMPLETED, + STARTED, PAUSED, COMPLETED, @@ -97,23 +91,19 @@ enum LAST_SIGNAL }; -static int score_signals[LAST_SIGNAL] = { 0 }; +G_DEFINE_TYPE (ClutterScore, clutter_score, G_TYPE_OBJECT); -static void start_entry (ClutterScoreEntry *entry); +static int score_signals[LAST_SIGNAL] = { 0 }; /* Object */ static void -clutter_score_set_property (GObject *object, +clutter_score_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { - ClutterScore *score; - ClutterScorePrivate *priv; - - score = CLUTTER_SCORE(object); - priv = score->priv; + ClutterScorePrivate *priv = CLUTTER_SCORE_GET_PRIVATE (gobject); switch (prop_id) { @@ -121,22 +111,18 @@ clutter_score_set_property (GObject *object, priv->loop = g_value_get_boolean (value); break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void -clutter_score_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) +clutter_score_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) { - ClutterScore *score; - ClutterScorePrivate *priv; - - score = CLUTTER_SCORE(object); - priv = score->priv; + ClutterScorePrivate *priv = CLUTTER_SCORE_GET_PRIVATE (gobject); switch (prop_id) { @@ -144,7 +130,7 @@ clutter_score_get_property (GObject *object, g_value_set_boolean (value, priv->loop); break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } @@ -154,7 +140,9 @@ clutter_score_finalize (GObject *object) { ClutterScorePrivate *priv = CLUTTER_SCORE (object)->priv; - g_hash_table_destroy (priv->running_timelines); + if (priv->running_timelines) + g_hash_table_destroy (priv->running_timelines); + G_OBJECT_CLASS (clutter_score_parent_class)->finalize (object); } @@ -173,70 +161,152 @@ clutter_score_dispose (GObject *object) static void clutter_score_class_init (ClutterScoreClass *klass) { - GObjectClass *object_class; + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - object_class = (GObjectClass*) klass; - - object_class->set_property = clutter_score_set_property; - object_class->get_property = clutter_score_get_property; - object_class->finalize = clutter_score_finalize; - object_class->dispose = clutter_score_dispose; + gobject_class->set_property = clutter_score_set_property; + gobject_class->get_property = clutter_score_get_property; + gobject_class->finalize = clutter_score_finalize; + gobject_class->dispose = clutter_score_dispose; g_type_class_add_private (klass, sizeof (ClutterScorePrivate)); + /** + * ClutterScore:loop: + * + * Whether the #ClutterScore should restart once finished. + * + * Since: 0.6 + */ + g_object_class_install_property (gobject_class, + PROP_LOOP, + g_param_spec_boolean ("loop", + "Loop", + "Whether the score should restart once finished", + FALSE, + CLUTTER_PARAM_READWRITE)); /** - * ClutterScore::new-timeline: + * ClutterScore::timeline-started: * @score: the score which received the signal * @timeline: the current timeline * - * The ::new-timeline signal is emitted each time a new timeline in the - * score is reached. + * The ::timeline-started signal is emitted each time a new timeline + * inside a #ClutterScore starts playing. + * + * Since: 0.6 */ - score_signals[NEW_TIMELINE] = - g_signal_new ("new-timeline", - G_TYPE_FROM_CLASS (object_class), + score_signals[TIMELINE_STARTED] = + g_signal_new ("timeline-started", + G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterScoreClass, new_timeline), + G_STRUCT_OFFSET (ClutterScoreClass, timeline_started), NULL, NULL, clutter_marshal_VOID__OBJECT, G_TYPE_NONE, 1, CLUTTER_TYPE_TIMELINE); + /** + * ClutterScore::timeline-completed: + * @score: the score which received the signal + * @timeline: the completed timeline + * + * The ::timeline-completed signal is emitted each time a timeline + * inside a #ClutterScore terminates. + * + * Since: 0.6 + */ + score_signals[TIMELINE_COMPLETED] = + g_signal_new ("timeline-completed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterScoreClass, timeline_completed), + NULL, NULL, + clutter_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + CLUTTER_TYPE_TIMELINE); + /** + * ClutterScore::completed: + * @score: the score which received the signal + * + * The ::completed signal is emitted each time a #ClutterScore terminates. + * + * Since: 0.6 + */ score_signals[COMPLETED] = g_signal_new ("completed", - G_TYPE_FROM_CLASS (object_class), + G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterScoreClass, completed), NULL, NULL, clutter_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * ClutterScore::started: + * @score: the score which received the signal + * + * The ::started signal is emitted each time a #ClutterScore starts playing. + * + * Since: 0.6 + */ score_signals[STARTED] = g_signal_new ("started", - G_TYPE_FROM_CLASS (object_class), + G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterScoreClass, started), NULL, NULL, clutter_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * ClutterScore::paused: + * @score: the score which received the signal + * + * The ::paused signal is emitted each time a #ClutterScore + * is paused. + * + * Since: 0.6 + */ score_signals[PAUSED] = g_signal_new ("paused", - G_TYPE_FROM_CLASS (object_class), + G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterScoreClass, paused), NULL, NULL, clutter_marshal_VOID__VOID, G_TYPE_NONE, 0); - } static void clutter_score_init (ClutterScore *self) { - self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, - CLUTTER_TYPE_SCORE, - ClutterScorePrivate); + ClutterScorePrivate *priv; - self->priv->running_timelines = g_hash_table_new(NULL, NULL); + self->priv = priv = CLUTTER_SCORE_GET_PRIVATE (self); + + /* sentinel */ + priv->root = g_node_new (NULL); + + priv->running_timelines = NULL; + + priv->is_paused = FALSE; + priv->loop = FALSE; + + priv->last_id = 1; +} + +/** + * clutter_score_new: + * + * Creates a new #ClutterScore. A #ClutterScore is an object that can + * hold multiple #ClutterTimelines in a sequential order. + * + * Return value: the newly created #ClutterScore. Use g_object_unref() + * when done. + * + * Since: 0.6 + */ +ClutterScore * +clutter_score_new (void) +{ + return g_object_new (CLUTTER_TYPE_SCORE, NULL); } /** @@ -282,76 +352,108 @@ clutter_score_get_loop (ClutterScore *score) * * Query state of a #ClutterScore instance. * - * Return Value: TRUE if score is currently playing, FALSE if not. + * Return Value: %TRUE if score is currently playing */ gboolean clutter_score_is_playing (ClutterScore *score) { g_return_val_if_fail (CLUTTER_IS_SCORE (score), FALSE); - /* FIXME: paused state currently counts as playing */ + if (score->priv->is_paused) + return FALSE; - return !!g_hash_table_size (score->priv->running_timelines); + return (g_hash_table_size (score->priv->running_timelines) != 0); +} + +/* forward declaration */ +static void start_entry (ClutterScoreEntry *entry); + +static void +start_children_entries (GNode *node, + gpointer data) +{ + ClutterScoreEntry *entry = node->data; + + start_entry (entry); } static void on_timeline_finish (ClutterTimeline *timeline, ClutterScoreEntry *entry) { - GSList *item; + ClutterScorePrivate *priv = entry->score->priv; - g_hash_table_remove (entry->score->priv->running_timelines, - GINT_TO_POINTER(entry->handler_id)); + g_hash_table_remove (priv->running_timelines, + GINT_TO_POINTER (entry->id)); - g_signal_handler_disconnect (timeline, entry->handler_id); + g_signal_handler_disconnect (timeline, entry->completed_id); + entry->completed_id = 0; - CLUTTER_NOTE (SCHEDULER, - "completed %p %li\n", - entry->timeline, entry->handler_id); + CLUTTER_NOTE (SCHEDULER, "timeline [%p] (%d) completed", + entry->timeline, + entry->id); - for (item = entry->child_entries; item != NULL; item = item->next) + g_signal_emit (entry->score, score_signals[TIMELINE_COMPLETED], 0, + entry->timeline); + + /* start every child */ + if (entry->node->children) { - ClutterScoreEntry *child_entry = item->data; - start_entry (child_entry); + g_node_children_foreach (entry->node, + G_TRAVERSE_ALL, + start_children_entries, + NULL); } - if (clutter_score_is_playing (entry->score) == FALSE) + /* score has finished - fire 'completed' signal */ + if (g_hash_table_size (priv->running_timelines) == 0) { - /* Score has finished - fire 'completed' signal */ - /* Also check if looped etc */ - CLUTTER_NOTE (SCHEDULER, "looks like we finished\n"); + CLUTTER_NOTE (SCHEDULER, "looks like we finished"); g_signal_emit (entry->score, score_signals[COMPLETED], 0); + + clutter_score_stop (entry->score); + + if (priv->loop) + clutter_score_start (entry->score); } } static void start_entry (ClutterScoreEntry *entry) { - entry->handler_id = g_signal_connect (entry->timeline, - "completed", - G_CALLBACK (on_timeline_finish), - entry); + ClutterScorePrivate *priv = entry->score->priv; - CLUTTER_NOTE (SCHEDULER, - "started %p %li\n", entry->timeline, entry->handler_id); + entry->completed_id = + g_signal_connect (entry->timeline, + "completed", G_CALLBACK (on_timeline_finish), + entry); - g_hash_table_insert (entry->score->priv->running_timelines, - GINT_TO_POINTER(entry->handler_id), + CLUTTER_NOTE (SCHEDULER, "timeline [%p] (%d) started", + entry->timeline, + entry->id); + + if (G_UNLIKELY (priv->running_timelines == NULL)) + priv->running_timelines = g_hash_table_new (NULL, NULL); + + g_hash_table_insert (priv->running_timelines, + GINT_TO_POINTER (entry->id), entry); clutter_timeline_start (entry->timeline); - g_signal_emit (entry->score, score_signals[NEW_TIMELINE], - 0, entry->timeline); + g_signal_emit (entry->score, score_signals[TIMELINE_STARTED], 0, + entry->timeline); } -void -on_foreach_running_timeline_start (gpointer key, - gpointer value, - gpointer user_data) +static void +foreach_running_timeline_start (gpointer key, + gpointer value, + gpointer user_data) { - clutter_timeline_start (CLUTTER_TIMELINE(value)); + ClutterScoreEntry *entry = value; + + clutter_timeline_start (entry->timeline); } /** @@ -365,36 +467,37 @@ on_foreach_running_timeline_start (gpointer key, void clutter_score_start (ClutterScore *score) { - GSList *item; ClutterScorePrivate *priv; g_return_if_fail (CLUTTER_IS_SCORE (score)); priv = score->priv; - if (priv->paused) + if (priv->is_paused) { g_hash_table_foreach (priv->running_timelines, - (GHFunc)on_foreach_running_timeline_start, + foreach_running_timeline_start, NULL); - priv->paused = 0; + priv->is_paused = FALSE; } else { - for (item = priv->entries; item != NULL; item = item->next) - { - ClutterScoreEntry *entry = item->data; - start_entry (entry); - } + g_node_children_foreach (priv->root, + G_TRAVERSE_ALL, + start_children_entries, + NULL); } } -gboolean -on_foreach_running_timeline_stop (gpointer key, - gpointer value, - gpointer user_data) +static gboolean +foreach_running_timeline_stop (gpointer key, + gpointer value, + gpointer user_data) { - clutter_timeline_stop (((ClutterScoreEntry*)value)->timeline); + ClutterScoreEntry *entry = value; + + clutter_timeline_stop (entry->timeline); + return TRUE; } @@ -415,8 +518,10 @@ clutter_score_stop (ClutterScore *score) priv = score->priv; g_hash_table_foreach_remove (priv->running_timelines, - (GHRFunc)on_foreach_running_timeline_stop, + foreach_running_timeline_stop, NULL); + g_hash_table_destroy (priv->running_timelines); + priv->running_timelines = NULL; } /** @@ -440,12 +545,14 @@ clutter_score_rewind (ClutterScore *score) clutter_score_start (score); } -void -on_foreach_running_timeline_pause (gpointer key, - gpointer value, - gpointer user_data) +static void +foreach_running_timeline_pause (gpointer key, + gpointer value, + gpointer user_data) { - clutter_timeline_pause (((ClutterScoreEntry*)value)->timeline); + ClutterScoreEntry *entry = value; + + clutter_timeline_pause (entry->timeline); } void @@ -457,131 +564,278 @@ clutter_score_pause (ClutterScore *score) priv = score->priv; - if (priv->paused || !clutter_score_is_playing (score)) + if (!clutter_score_is_playing (score)) return; g_hash_table_foreach (priv->running_timelines, - (GHFunc)on_foreach_running_timeline_pause, + foreach_running_timeline_pause, NULL); - priv->paused = 1; + priv->is_paused = TRUE; g_signal_emit (score, score_signals[PAUSED], 0); } -static ClutterScoreEntry* -find_entry (GSList *list, ClutterTimeline *timeline) +typedef enum { + FIND_BY_TIMELINE, + FIND_BY_ID, + REMOVE_BY_ID, + LIST_TIMELINES +} TraverseAction; + +typedef struct { + TraverseAction action; + + ClutterScore *score; + + union { + ClutterTimeline *timeline; + guint id; + ClutterScoreEntry *entry; + } d; + + gpointer result; +} TraverseClosure; + +static gboolean +destroy_entry (GNode *node, + G_GNUC_UNUSED gpointer data) { - GSList *item; - ClutterScoreEntry *res = NULL; + ClutterScoreEntry *entry = node->data; - if (list == NULL) - return NULL; - - for (item = list; item != NULL && res == NULL; item = item->next) + if (G_LIKELY (entry != NULL)) { - ClutterScoreEntry *entry = item->data; + if (entry->completed_id) + g_signal_handler_disconnect (entry->timeline, entry->completed_id); - g_assert (entry != NULL); + g_object_unref (entry->timeline); + g_slice_free (ClutterScoreEntry, entry); - if (entry->timeline == timeline) - return entry; - - if (entry->child_entries) - res = find_entry (entry->child_entries, timeline); + node->data = NULL; } - return res; + /* continue */ + return FALSE; +} + +static gboolean +traverse_children (GNode *node, + gpointer data) +{ + TraverseClosure *closure = data; + ClutterScoreEntry *entry = node->data; + gboolean retval = FALSE; + + /* root */ + if (!entry) + return TRUE; + + switch (closure->action) + { + case FIND_BY_TIMELINE: + if (closure->d.timeline == entry->timeline) + { + closure->result = node; + retval = TRUE; + } + break; + + case FIND_BY_ID: + if (closure->d.id == entry->id) + { + closure->result = node; + retval = TRUE; + } + break; + + case REMOVE_BY_ID: + if (closure->d.id == entry->id) + { + if (entry->completed_id) + g_signal_handler_disconnect (entry->timeline, entry->completed_id); + + g_object_unref (entry->timeline); + + g_node_traverse (node, + G_POST_ORDER, + G_TRAVERSE_ALL, + -1, + destroy_entry, NULL); + + g_slice_free (ClutterScoreEntry, entry); + + closure->result = node; + retval = TRUE; + } + break; + + case LIST_TIMELINES: + retval = TRUE; + break; + } + + return retval; +} + +static GNode * +find_entry_by_timeline (ClutterScore *score, + ClutterTimeline *timeline) +{ + ClutterScorePrivate *priv = score->priv; + TraverseClosure closure; + + closure.action = FIND_BY_TIMELINE; + closure.score = score; + closure.d.timeline = timeline; + closure.result = NULL; + + g_node_traverse (priv->root, + G_POST_ORDER, + G_TRAVERSE_ALL, + -1, + traverse_children, &closure); + + if (closure.result) + return closure.result; + + return NULL; +} + +static GNode * +find_entry_by_id (ClutterScore *score, + guint id) +{ + ClutterScorePrivate *priv = score->priv; + TraverseClosure closure; + + closure.action = FIND_BY_ID; + closure.score = score; + closure.d.id = id; + closure.result = NULL; + + g_node_traverse (priv->root, + G_POST_ORDER, + G_TRAVERSE_ALL, + -1, + traverse_children, &closure); + + if (closure.result) + return closure.result; + + return NULL; } /** * clutter_score_append: - * @score: A #ClutterScore - * @timeline_existing: A #ClutterTimeline in the score - * @timeline_new: A new #ClutterTimeline to start when #timeline_existing has - * completed, + * @score: a #ClutterScore + * @parent: a #ClutterTimeline in the score or %NULL + * @timeline: a #ClutterTimeline * - * Appends a new timeline to an one existing in the score. + * Appends a timeline to another one existing in the score; the newly + * appended timeline will be started when @parent is complete. * + * If @parent is %NULL, the new #ClutterTimeline will be started when + * clutter_score_start() is called. + * + * Return value: the id of the newly added timeline, to be used with + * clutter_score_get_timeline() and clutter_score_remove(). + * + * Since: 0.6 */ -void +guint clutter_score_append (ClutterScore *score, - ClutterTimeline *timeline_existing, - ClutterTimeline *timeline_new) + ClutterTimeline *parent, + ClutterTimeline *timeline) { ClutterScorePrivate *priv; - ClutterScoreEntry *entry, *entry_new; + + g_return_val_if_fail (CLUTTER_IS_SCORE (score), 0); + g_return_val_if_fail (parent == NULL || CLUTTER_IS_TIMELINE (parent), 0); + g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0); priv = score->priv; - /* Appends a timeline to the end of another */ - if ((entry = find_entry (priv->entries, timeline_existing)) != NULL) + if (!parent) { - entry_new = g_new0(ClutterScoreEntry, 1); - entry_new->timeline = g_object_ref (timeline_new); - entry_new->score = score; + ClutterScoreEntry *entry; - entry->child_entries = g_slist_append (entry->child_entries, entry_new); + entry = g_slice_new (ClutterScoreEntry); - clutter_timeline_stop (timeline_new); /* stop it */ + entry->timeline = g_object_ref (timeline); + entry->id = priv->last_id; + entry->completed_id = 0; + entry->score = score; + + entry->node = g_node_append_data (priv->root, entry); + + priv->last_id += 1; + + return entry->id; } + else + { + GNode *node; + ClutterScoreEntry *entry; + + node = find_entry_by_timeline (score, parent); + if (G_UNLIKELY (!node)) + { + g_warning ("Unable to find the parent timeline inside the score."); + return 0; + } + + entry = g_slice_new (ClutterScoreEntry); + entry->timeline = g_object_ref (timeline); + entry->id = priv->last_id; + entry->completed_id = 0; + entry->score = score; + + entry->node = g_node_append_data (node, entry); + + priv->last_id += 1; + + return entry->id; + } + + return 0; } /** - * clutter_score_add: - * @score: A #ClutterScore - * @timeline: A #ClutterTimeline + * clutter_score_remove: + * @score: a #ClutterScore + * @id: the id of the timeline to remove * - * Adds a new initial timeline to start when the score is started. + * Removes the #ClutterTimeline with the given id inside @score. If + * the timeline has other timelines attached to it, those are removed + * as well. + * + * Since: 0.6 */ void -clutter_score_add (ClutterScore *score, - ClutterTimeline *timeline) +clutter_score_remove (ClutterScore *score, + guint id) { - ClutterScorePrivate *priv; - ClutterScoreEntry *entry; + ClutterScorePrivate *priv; + TraverseClosure closure; + + g_return_if_fail (CLUTTER_IS_SCORE (score)); + g_return_if_fail (id > 0); priv = score->priv; - /* Added timelines are always started first */ - entry = g_new0(ClutterScoreEntry, 1); - entry->timeline = g_object_ref (timeline); - entry->score = score; - score->priv->entries = g_slist_append (score->priv->entries, entry); + closure.action = REMOVE_BY_ID; + closure.score = score; + closure.d.id = id; + closure.result = NULL; - clutter_timeline_stop (timeline); /* stop it */ - - CLUTTER_NOTE (SCHEDULER, "added timeline %p\n", entry->timeline); -} - -static void -remove_entries (GSList *list) -{ - GSList *item; - - if (!list) - return; - - for (item = list; item != NULL; item = item->next) - { - ClutterScoreEntry *entry = item->data; - - g_object_unref (entry->timeline); - - if (entry->child_entries) - remove_entries (entry->child_entries); - - g_slist_free (entry->child_entries); - g_free(entry); - } -} - -void -clutter_score_remove (ClutterScore *score, - ClutterTimeline *timeline_parent, - ClutterTimeline *timeline) -{ + g_node_traverse (priv->root, + G_POST_ORDER, + G_TRAVERSE_ALL, + -1, + traverse_children, &closure); + if (closure.result) + g_node_destroy (closure.result); } /** @@ -595,21 +849,66 @@ clutter_score_remove (ClutterScore *score, void clutter_score_remove_all (ClutterScore *score) { + ClutterScorePrivate *priv; + + g_return_if_fail (CLUTTER_IS_SCORE (score)); + + /* this will take care of the running timelines */ clutter_score_stop (score); - remove_entries (score->priv->entries); + + priv = score->priv; + + g_node_traverse (priv->root, + G_POST_ORDER, + G_TRAVERSE_ALL, + -1, + destroy_entry, NULL); + g_node_destroy (priv->root); + + /* sentinel */ + priv->root = g_node_new (NULL); } -/** - * clutter_score_new: - * - * Creates a new #ClutterScore. - * - * Return value: the newly created #ClutterScore - * - * Since: 0.6 - */ -ClutterScore * -clutter_score_new (void) +ClutterTimeline * +clutter_score_get_timeline (ClutterScore *score, + guint id) { - return g_object_new (CLUTTER_TYPE_SCORE, NULL); + GNode *node; + ClutterScoreEntry *entry; + + g_return_val_if_fail (CLUTTER_IS_SCORE (score), NULL); + g_return_val_if_fail (id > 0, NULL); + + node = find_entry_by_id (score, id); + if (G_UNLIKELY (!node)) + return NULL; + + entry = node->data; + + return entry->timeline; +} + +GSList * +clutter_score_list_timelines (ClutterScore *score) +{ + ClutterScorePrivate *priv; + TraverseClosure closure; + GSList *retval; + + g_return_val_if_fail (CLUTTER_IS_SCORE (score), NULL); + + priv = score->priv; + + closure.action = LIST_TIMELINES; + closure.result = NULL; + + g_node_traverse (priv->root, + G_POST_ORDER, + G_TRAVERSE_ALL, + -1, + traverse_children, &closure); + + retval = closure.result; + + return retval; } diff --git a/clutter/clutter-score.h b/clutter/clutter-score.h index f28c5c853..fde0e43fc 100644 --- a/clutter/clutter-score.h +++ b/clutter/clutter-score.h @@ -23,41 +23,24 @@ * Boston, MA 02111-1307, USA. */ -#ifndef _HAVE_CLUTTER_SCORE_H -#define _HAVE_CLUTTER_SCORE_H +#ifndef __CLUTTER_SCORE_H__ +#define __CLUTTER_SCORE_H__ -/* clutter-score.h */ - -#include #include G_BEGIN_DECLS -#define CLUTTER_TYPE_SCORE clutter_score_get_type() +#define CLUTTER_TYPE_SCORE (clutter_score_get_type ()) -#define CLUTTER_SCORE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_TYPE_SCORE, ClutterScore)) - -#define CLUTTER_SCORE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_TYPE_SCORE, ClutterScoreClass)) - -#define CLUTTER_IS_SCORE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_TYPE_SCORE)) - -#define CLUTTER_IS_SCORE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_TYPE_SCORE)) - -#define CLUTTER_SCORE_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_TYPE_SCORE, ClutterScoreClass)) +#define CLUTTER_SCORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SCORE, ClutterScore)) +#define CLUTTER_SCORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_SCORE, ClutterScoreClass)) +#define CLUTTER_IS_SCORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SCORE)) +#define CLUTTER_IS_SCORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_SCORE)) +#define CLUTTER_SCORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_SCORE, ClutterScoreClass)) typedef struct _ClutterScore ClutterScore; -typedef struct _ClutterScoreClass ClutterScoreClass; typedef struct _ClutterScorePrivate ClutterScorePrivate; +typedef struct _ClutterScoreClass ClutterScoreClass; struct _ClutterScore { @@ -68,13 +51,21 @@ struct _ClutterScore struct _ClutterScoreClass { + /*< private >*/ GObjectClass parent_class; - - void (*new_timeline) (ClutterScore *score, ClutterTimeline *timeline); - void (*started) (ClutterScore *score); - void (*completed) (ClutterScore *score); - void (*paused) (ClutterScore *score); + /*< public >*/ + void (* timeline_started) (ClutterScore *score, + ClutterTimeline *timeline); + void (* timeline_completed) (ClutterScore *score, + ClutterTimeline *timeline); + + void (* started) (ClutterScore *score); + void (* completed) (ClutterScore *score); + void (* paused) (ClutterScore *score); + + /*< private >*/ + /* padding for future expansion */ void (*_clutter_score_1) (void); void (*_clutter_score_2) (void); void (*_clutter_score_3) (void); @@ -84,48 +75,27 @@ struct _ClutterScoreClass GType clutter_score_get_type (void) G_GNUC_CONST; -ClutterScore *clutter_score_new (void); +ClutterScore * clutter_score_new (void); +void clutter_score_set_loop (ClutterScore *score, + gboolean loop); +gboolean clutter_score_get_loop (ClutterScore *score); +gboolean clutter_score_is_playing (ClutterScore *score); -void -clutter_score_set_loop (ClutterScore *score, - gboolean loop); - -gboolean -clutter_score_get_loop (ClutterScore *score); - -void -clutter_score_rewind (ClutterScore *score); - -gboolean -clutter_score_is_playing (ClutterScore *score); - -void -clutter_score_start (ClutterScore *score); - -void -clutter_score_stop (ClutterScore *score); - -void -clutter_score_pause (ClutterScore *score); - -void -clutter_score_append (ClutterScore *score, - ClutterTimeline *timeline_existing, - ClutterTimeline *timeline_new); - -void -clutter_score_add (ClutterScore *score, - ClutterTimeline *timeline); - -void -clutter_score_remove (ClutterScore *score, - ClutterTimeline *timeline_parent, - ClutterTimeline *timeline); - -void -clutter_score_remove_all (ClutterScore *score); +guint clutter_score_append (ClutterScore *score, + ClutterTimeline *parent, + ClutterTimeline *timeline); +void clutter_score_remove (ClutterScore *score, + guint id); +void clutter_score_remove_all (ClutterScore *score); +ClutterTimeline *clutter_score_get_timeline (ClutterScore *score, + guint id); +GSList * clutter_score_list_timelines (ClutterScore *score); +void clutter_score_start (ClutterScore *score); +void clutter_score_stop (ClutterScore *score); +void clutter_score_pause (ClutterScore *score); +void clutter_score_rewind (ClutterScore *score); G_END_DECLS -#endif +#endif /* __CLUTTER_SCORE_H__ */ diff --git a/doc/reference/ChangeLog b/doc/reference/ChangeLog index 3b86fd440..288431833 100644 --- a/doc/reference/ChangeLog +++ b/doc/reference/ChangeLog @@ -1,3 +1,9 @@ +2007-12-04 Emmanuele Bassi + + * clutter.types: + * clutter-docs.sgml: + * clutter-sections.txt: Fix ClutterScore symbols. + 2007-11-30 Emmanuele Bassi * clutter-sections.txt: Update with the newly added API. diff --git a/doc/reference/clutter-docs.sgml b/doc/reference/clutter-docs.sgml index d5b3d41de..51de9a552 100644 --- a/doc/reference/clutter-docs.sgml +++ b/doc/reference/clutter-docs.sgml @@ -106,6 +106,7 @@ Base classes + diff --git a/doc/reference/clutter-sections.txt b/doc/reference/clutter-sections.txt index e00fb71b5..4b7ce5448 100644 --- a/doc/reference/clutter-sections.txt +++ b/doc/reference/clutter-sections.txt @@ -1212,12 +1212,15 @@ clutter_model_iter_get_type ClutterScore ClutterScoreClass clutter_score_new -clutter_score_add +clutter_score_set_loop +clutter_score_get_loop + + clutter_score_append clutter_score_remove clutter_score_remove_all -clutter_score_set_loop -clutter_score_get_loop +clutter_score_get_timeline +clutter_score_list_timelines clutter_score_start diff --git a/doc/reference/clutter.types b/doc/reference/clutter.types index c1fda8153..bad9636f8 100644 --- a/doc/reference/clutter.types +++ b/doc/reference/clutter.types @@ -24,3 +24,4 @@ clutter_script_get_type clutter_scriptable_get_type clutter_model_get_type clutter_model_iter_get_type +clutter_score_get_type diff --git a/tests/test-score.c b/tests/test-score.c index eee76d739..e00b9ca89 100644 --- a/tests/test-score.c +++ b/tests/test-score.c @@ -2,6 +2,38 @@ #include #include +static gint level = 1; + +static void +on_timeline_started (ClutterScore *score, + ClutterTimeline *timeline) +{ + gint i; + + for (i = 0; i < level; i++) + g_print (" "); + + g_print ("Started timeline: `%s'\n", + g_object_get_data (G_OBJECT (timeline), "timeline-name")); + + level += 1; +} + +static void +on_timeline_completed (ClutterScore *score, + ClutterTimeline *timeline) +{ + gint i; + + level -= 1; + + for (i = 0; i < level; i++) + g_print (" "); + + g_print ("Completed timeline: `%s'\n", + g_object_get_data (G_OBJECT (timeline), "timeline-name")); +} + int main (int argc, char **argv) { @@ -14,12 +46,38 @@ main (int argc, char **argv) clutter_init (&argc, &argv); timeline_1 = clutter_timeline_new (10, 120); + g_object_set_data_full (G_OBJECT (timeline_1), + "timeline-name", g_strdup ("Timeline 1"), + g_free); + timeline_2 = clutter_timeline_clone (timeline_1); + g_object_set_data_full (G_OBJECT (timeline_2), + "timeline-name", g_strdup ("Timeline 2"), + g_free); + timeline_3 = clutter_timeline_clone (timeline_1); + g_object_set_data_full (G_OBJECT (timeline_3), + "timeline-name", g_strdup ("Timeline 3"), + g_free); + timeline_4 = clutter_timeline_clone (timeline_1); + g_object_set_data_full (G_OBJECT (timeline_4), + "timeline-name", g_strdup ("Timeline 4"), + g_free); + score = clutter_score_new(); - clutter_score_add (score, timeline_1); + g_signal_connect (score, "timeline-started", + G_CALLBACK (on_timeline_started), + NULL); + g_signal_connect (score, "timeline-completed", + G_CALLBACK (on_timeline_completed), + NULL); + g_signal_connect (score, "completed", + G_CALLBACK (clutter_main_quit), + NULL); + + clutter_score_append (score, NULL, timeline_1); clutter_score_append (score, timeline_1, timeline_2); clutter_score_append (score, timeline_1, timeline_3); clutter_score_append (score, timeline_3, timeline_4); @@ -32,6 +90,7 @@ main (int argc, char **argv) g_object_unref (timeline_1); g_object_unref (timeline_2); g_object_unref (timeline_3); + g_object_unref (timeline_4); return EXIT_SUCCESS; }