2008-03-18 Emmanuele Bassi <ebassi@openedhand.com>

* clutter.symbols: Add new symbols

	* clutter/clutter-score.[ch]: Use the newly added marker API
	on the timelines to implement attaching timelines at specific
	points, using either milliseconds or frames.

	* tests/test-score.c (main): Test the new API.
This commit is contained in:
Emmanuele Bassi 2008-03-18 17:54:40 +00:00
parent 43d9d579b4
commit b6b9dd7c65
5 changed files with 478 additions and 248 deletions

View File

@ -1,3 +1,13 @@
2008-03-18 Emmanuele Bassi <ebassi@openedhand.com>
* clutter.symbols: Add new symbols
* clutter/clutter-score.[ch]: Use the newly added marker API
on the timelines to implement attaching timelines at specific
points, using either milliseconds or frames.
* tests/test-score.c (main): Test the new API.
2008-03-18 Emmanuele Bassi <ebassi@openedhand.com> 2008-03-18 Emmanuele Bassi <ebassi@openedhand.com>
* clutter/clutter-marshal.list: Add signature for the * clutter/clutter-marshal.list: Add signature for the

View File

@ -442,6 +442,8 @@ clutter_redraw
clutter_rotate_axis_get_type clutter_rotate_axis_get_type
clutter_rotate_direction_get_type clutter_rotate_direction_get_type
clutter_score_append clutter_score_append
clutter_score_append_at_frame
clutter_score_append_at_time
clutter_score_get_loop clutter_score_get_loop
clutter_score_get_timeline clutter_score_get_timeline
clutter_score_get_type clutter_score_get_type

View File

@ -38,7 +38,7 @@
* For example, this code will start two #ClutterTimeline<!-- -->s after * For example, this code will start two #ClutterTimeline<!-- -->s after
* a third timeline terminates: * a third timeline terminates:
* *
* <informalexample><programlisting> * |[
* ClutterTimeline *timeline_1, *timeline_2, *timeline_3; * ClutterTimeline *timeline_1, *timeline_2, *timeline_3;
* ClutterScore *score; * ClutterScore *score;
* *
@ -52,12 +52,17 @@
* clutter_score_append (score, timeline_1, timeline_3); * clutter_score_append (score, timeline_1, timeline_3);
* *
* clutter_score_start (); * clutter_score_start ();
* </programlisting></informalexample> * ]|
* *
* A #ClutterScore takes a reference on the timelines it manages. * A #ClutterScore takes a reference on the timelines it manages.
* *
* New timelines can be added to the #ClutterScore using * New timelines can be appended to the #ClutterScore using
* clutter_score_append() and removed using clutter_score_remove(). * clutter_score_append() and removed using clutter_score_remove().
* Timelines can either be appended at the end of other timelines
* or at the beginning of the score using clutter_score_append().
* Timelines can also be appeneded at a specific position, expressed
* in either millisecond or frame number, of the parent timeline,
* using clutter_score_append_at_time() or clutter_score_append_at_frame().
* *
* The score can be cleared using clutter_score_remove_all(). * The score can be cleared using clutter_score_remove_all().
* *
@ -81,15 +86,29 @@
#include "clutter-private.h" #include "clutter-private.h"
#include "clutter-debug.h" #include "clutter-debug.h"
typedef enum {
SCORE_ENTRY_TIME,
SCORE_ENTRY_FRAME,
SCORE_ENTRY_APPEND
} ClutterScoreEntryType;
typedef struct _ClutterScoreEntry ClutterScoreEntry; typedef struct _ClutterScoreEntry ClutterScoreEntry;
struct _ClutterScoreEntry struct _ClutterScoreEntry
{ {
ClutterTimeline *timeline; ClutterScoreEntryType type;
guint id;
/* signal handler id */ gchar *id;
gulong completed_id;
ClutterTimeline *timeline;
ClutterTimeline *parent;
guint msecs;
guint frame;
/* signal handlers id */
guint complete_id;
guint marker_id;
ClutterScore *score; ClutterScore *score;
@ -130,6 +149,8 @@ enum
LAST_SIGNAL LAST_SIGNAL
}; };
static inline void clutter_score_clear (ClutterScore *score);
G_DEFINE_TYPE (ClutterScore, clutter_score, G_TYPE_OBJECT); G_DEFINE_TYPE (ClutterScore, clutter_score, G_TYPE_OBJECT);
static int score_signals[LAST_SIGNAL] = { 0 }; static int score_signals[LAST_SIGNAL] = { 0 };
@ -177,26 +198,14 @@ clutter_score_get_property (GObject *gobject,
static void static void
clutter_score_finalize (GObject *object) clutter_score_finalize (GObject *object)
{ {
ClutterScorePrivate *priv = CLUTTER_SCORE (object)->priv; ClutterScore *score = CLUTTER_SCORE (object);
if (priv->running_timelines) clutter_score_stop (score);
g_hash_table_destroy (priv->running_timelines); clutter_score_clear (score);
G_OBJECT_CLASS (clutter_score_parent_class)->finalize (object); G_OBJECT_CLASS (clutter_score_parent_class)->finalize (object);
} }
static void
clutter_score_dispose (GObject *object)
{
ClutterScore *self = CLUTTER_SCORE(object);
ClutterScorePrivate *priv;
priv = self->priv;
G_OBJECT_CLASS (clutter_score_parent_class)->dispose (object);
}
static void static void
clutter_score_class_init (ClutterScoreClass *klass) clutter_score_class_init (ClutterScoreClass *klass)
{ {
@ -205,7 +214,6 @@ clutter_score_class_init (ClutterScoreClass *klass)
gobject_class->set_property = clutter_score_set_property; gobject_class->set_property = clutter_score_set_property;
gobject_class->get_property = clutter_score_get_property; gobject_class->get_property = clutter_score_get_property;
gobject_class->finalize = clutter_score_finalize; gobject_class->finalize = clutter_score_finalize;
gobject_class->dispose = clutter_score_dispose;
g_type_class_add_private (klass, sizeof (ClutterScorePrivate)); g_type_class_add_private (klass, sizeof (ClutterScorePrivate));
@ -411,6 +419,178 @@ clutter_score_is_playing (ClutterScore *score)
return (g_hash_table_size (score->priv->running_timelines) != 0); return (g_hash_table_size (score->priv->running_timelines) != 0);
} }
typedef enum {
FIND_BY_TIMELINE,
FIND_BY_ID,
REMOVE_BY_ID,
LIST_TIMELINES
} TraverseAction;
typedef struct {
TraverseAction action;
ClutterScore *score;
/* parameters */
union {
ClutterTimeline *timeline;
const gchar *id;
ClutterScoreEntry *entry;
} d;
gpointer result;
} TraverseClosure;
static gboolean
destroy_entry (GNode *node,
G_GNUC_UNUSED gpointer data)
{
ClutterScoreEntry *entry = node->data;
if (G_LIKELY (entry != NULL))
{
if (entry->marker_id)
{
g_signal_handler_disconnect (entry->parent, entry->marker_id);
entry->marker_id = 0;
}
if (entry->complete_id)
{
g_signal_handler_disconnect (entry->timeline, entry->complete_id);
entry->complete_id = 0;
}
g_object_unref (entry->timeline);
g_free (entry->id);
g_slice_free (ClutterScoreEntry, entry);
node->data = NULL;
}
/* continue */
return FALSE;
}
/* multi-purpose traversal function for the N-ary tree used by the score */
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 (strcmp (closure->d.id, entry->id) == 0)
{
closure->result = node;
retval = TRUE;
}
break;
case REMOVE_BY_ID:
if (strcmp (closure->d.id, entry->id) == 0)
{
if (entry->complete_id)
{
g_signal_handler_disconnect (entry->timeline, entry->complete_id);
entry->complete_id = 0;
}
if (entry->marker_id)
{
g_signal_handler_disconnect (entry->timeline, entry->marker_id);
entry->marker_id = 0;
}
g_object_unref (entry->timeline);
g_free (entry->id);
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:
closure->result = g_slist_prepend (closure->result, entry->timeline);
retval = FALSE;
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,
const gchar *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;
}
/* forward declaration */ /* forward declaration */
static void start_entry (ClutterScoreEntry *entry); static void start_entry (ClutterScoreEntry *entry);
@ -424,18 +604,42 @@ start_children_entries (GNode *node,
} }
static void static void
on_timeline_finish (ClutterTimeline *timeline, on_timeline_marker (ClutterTimeline *timeline,
ClutterScoreEntry *entry) const gchar *marker_name,
gint frame_num,
ClutterScoreEntry *entry)
{
GNode *parent;
CLUTTER_NOTE (SCHEDULER, "timeline [%p] marker ('%s') reached",
entry->timeline,
entry->id);
parent = find_entry_by_timeline (entry->score, timeline);
if (!parent)
return;
/* start every child */
if (parent->children)
{
g_node_children_foreach (parent,
G_TRAVERSE_ALL,
start_children_entries,
NULL);
}
}
static void
on_timeline_completed (ClutterTimeline *timeline,
ClutterScoreEntry *entry)
{ {
ClutterScorePrivate *priv = entry->score->priv; ClutterScorePrivate *priv = entry->score->priv;
g_hash_table_remove (priv->running_timelines, g_hash_table_remove (priv->running_timelines, entry->id);
GINT_TO_POINTER (entry->id));
g_signal_handler_disconnect (timeline, entry->completed_id); g_signal_handler_disconnect (timeline, entry->complete_id);
entry->completed_id = 0; entry->complete_id = 0;
CLUTTER_NOTE (SCHEDULER, "timeline [%p] (%d) completed", CLUTTER_NOTE (SCHEDULER, "timeline [%p] ('%s') completed",
entry->timeline, entry->timeline,
entry->id); entry->id);
@ -470,21 +674,22 @@ start_entry (ClutterScoreEntry *entry)
{ {
ClutterScorePrivate *priv = entry->score->priv; ClutterScorePrivate *priv = entry->score->priv;
entry->completed_id = if (clutter_timeline_is_playing (entry->timeline))
g_signal_connect (entry->timeline, return;
"completed", G_CALLBACK (on_timeline_finish),
entry);
CLUTTER_NOTE (SCHEDULER, "timeline [%p] (%d) started", entry->complete_id = g_signal_connect (entry->timeline,
"completed",
G_CALLBACK (on_timeline_completed),
entry);
CLUTTER_NOTE (SCHEDULER, "timeline [%p] ('%s') started",
entry->timeline, entry->timeline,
entry->id); entry->id);
if (G_UNLIKELY (priv->running_timelines == NULL)) if (G_UNLIKELY (priv->running_timelines == NULL))
priv->running_timelines = g_hash_table_new (NULL, NULL); priv->running_timelines = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (priv->running_timelines, g_hash_table_insert (priv->running_timelines, entry->id, entry);
GINT_TO_POINTER (entry->id),
entry);
clutter_timeline_start (entry->timeline); clutter_timeline_start (entry->timeline);
@ -499,7 +704,8 @@ foreach_running_timeline_start (gpointer key,
{ {
ClutterScoreEntry *entry = value; ClutterScoreEntry *entry = value;
clutter_timeline_start (entry->timeline); if (!clutter_timeline_is_playing (entry->timeline))
clutter_timeline_start (entry->timeline);
} }
/** /**
@ -564,11 +770,14 @@ clutter_score_stop (ClutterScore *score)
priv = score->priv; priv = score->priv;
g_hash_table_foreach_remove (priv->running_timelines, if (priv->running_timelines)
foreach_running_timeline_stop, {
NULL); g_hash_table_foreach_remove (priv->running_timelines,
g_hash_table_destroy (priv->running_timelines); foreach_running_timeline_stop,
priv->running_timelines = NULL; NULL);
g_hash_table_destroy (priv->running_timelines);
priv->running_timelines = NULL;
}
} }
/** /**
@ -633,161 +842,23 @@ clutter_score_pause (ClutterScore *score)
g_signal_emit (score, score_signals[PAUSED], 0); g_signal_emit (score, score_signals[PAUSED], 0);
} }
typedef enum { static inline void
FIND_BY_TIMELINE, clutter_score_clear (ClutterScore *score)
FIND_BY_ID,
REMOVE_BY_ID,
LIST_TIMELINES
} TraverseAction;
typedef struct {
TraverseAction action;
ClutterScore *score;
/* parameters */
union {
ClutterTimeline *timeline;
guint id;
ClutterScoreEntry *entry;
} d;
gpointer result;
} TraverseClosure;
static gboolean
destroy_entry (GNode *node,
G_GNUC_UNUSED gpointer data)
{
ClutterScoreEntry *entry = node->data;
if (G_LIKELY (entry != NULL))
{
if (entry->completed_id)
g_signal_handler_disconnect (entry->timeline, entry->completed_id);
g_object_unref (entry->timeline);
g_slice_free (ClutterScoreEntry, entry);
node->data = NULL;
}
/* continue */
return FALSE;
}
/* multi-purpose traversal function for the N-ary tree used by the score */
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:
closure->result = g_slist_prepend (closure->result, entry->timeline);
retval = FALSE;
break;
}
return retval;
}
static GNode *
find_entry_by_timeline (ClutterScore *score,
ClutterTimeline *timeline)
{ {
ClutterScorePrivate *priv = score->priv; 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_node_traverse (priv->root,
G_POST_ORDER, G_POST_ORDER,
G_TRAVERSE_ALL, G_TRAVERSE_ALL,
-1, -1,
traverse_children, &closure); destroy_entry, NULL);
g_node_destroy (priv->root);
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: * clutter_score_append:
* @score: a #ClutterScore * @score: a #ClutterScore
* @id: a unique string identifying the timeline
* @parent: a #ClutterTimeline in the score or %NULL * @parent: a #ClutterTimeline in the score or %NULL
* @timeline: a #ClutterTimeline * @timeline: a #ClutterTimeline
* *
@ -799,21 +870,20 @@ find_entry_by_id (ClutterScore *score,
* *
* #ClutterScore will take a reference on @timeline. * #ClutterScore will take a reference on @timeline.
* *
* Return value: the id of the newly added timeline, to be used with * Since: 0.8
* clutter_score_get_timeline() and clutter_score_remove().
*
* Since: 0.6
*/ */
guint void
clutter_score_append (ClutterScore *score, clutter_score_append (ClutterScore *score,
const gchar *id,
ClutterTimeline *parent, ClutterTimeline *parent,
ClutterTimeline *timeline) ClutterTimeline *timeline)
{ {
ClutterScorePrivate *priv; ClutterScorePrivate *priv;
g_return_val_if_fail (CLUTTER_IS_SCORE (score), 0); g_return_if_fail (CLUTTER_IS_SCORE (score));
g_return_val_if_fail (parent == NULL || CLUTTER_IS_TIMELINE (parent), 0); g_return_if_fail (id != NULL);
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0); g_return_if_fail (parent == NULL || CLUTTER_IS_TIMELINE (parent));
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
priv = score->priv; priv = score->priv;
@ -822,17 +892,14 @@ clutter_score_append (ClutterScore *score,
ClutterScoreEntry *entry; ClutterScoreEntry *entry;
entry = g_slice_new (ClutterScoreEntry); entry = g_slice_new (ClutterScoreEntry);
entry->type = SCORE_ENTRY_APPEND;
entry->timeline = g_object_ref (timeline); entry->timeline = g_object_ref (timeline);
entry->id = priv->last_id; entry->parent = NULL;
entry->completed_id = 0; entry->id = g_strdup (id);
entry->marker_id = 0;
entry->score = score; entry->score = score;
entry->node = g_node_append_data (priv->root, entry); entry->node = g_node_append_data (priv->root, entry);
priv->last_id += 1;
return entry->id;
} }
else else
{ {
@ -843,23 +910,159 @@ clutter_score_append (ClutterScore *score,
if (G_UNLIKELY (!node)) if (G_UNLIKELY (!node))
{ {
g_warning ("Unable to find the parent timeline inside the score."); g_warning ("Unable to find the parent timeline inside the score.");
return 0; return;
} }
entry = g_slice_new (ClutterScoreEntry); entry = g_slice_new (ClutterScoreEntry);
entry->type = SCORE_ENTRY_APPEND;
entry->timeline = g_object_ref (timeline); entry->timeline = g_object_ref (timeline);
entry->id = priv->last_id; entry->parent = parent;
entry->completed_id = 0; entry->id = g_strdup (id);
entry->marker_id = 0;
entry->score = score; entry->score = score;
entry->node = g_node_append_data (node, entry); entry->node = g_node_append_data (node, entry);
}
}
priv->last_id += 1; /**
* clutter_score_append_at_time:
* @score: a #ClutterScore
* @id: a unique string identifying the timeline
* @parent: the parent #ClutterTimeline
* @msecs: the time on @parent where the new timeline should
* be appended, in milliseconds
* @timeline: the #ClutterTimeline to append
*
* Appends @timeline at the given position on the @parent timeline,
* expressed in milliseconds.
*
* This function implicitly creates a timeline marker (see
* clutter_timeline_add_marker_at_time()) with the given @id on
* @parent.
*
* If you want to append @timeline at the end of @parent, use
* clutter_score_append().
*
* Since: 0.8
*/
void
clutter_score_append_at_time (ClutterScore *score,
const gchar *id,
ClutterTimeline *parent,
guint msecs,
ClutterTimeline *timeline)
{
ClutterScorePrivate *priv;
GNode *node;
ClutterScoreEntry *entry;
gchar *marker_reached_signal;
return entry->id; g_return_if_fail (CLUTTER_IS_SCORE (score));
g_return_if_fail (id != NULL);
g_return_if_fail (CLUTTER_IS_TIMELINE (parent));
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
priv = score->priv;
node = find_entry_by_timeline (score, parent);
if (G_UNLIKELY (!node))
{
g_warning ("Unable to find the parent timeline inside the score.");
return;
} }
return 0; entry = g_slice_new (ClutterScoreEntry);
entry->type = SCORE_ENTRY_TIME;
entry->timeline = g_object_ref (timeline);
entry->parent = parent;
entry->msecs = msecs;
entry->id = g_strdup (id);
entry->score = score;
clutter_timeline_add_marker_at_time (entry->parent,
entry->id,
entry->msecs);
marker_reached_signal = g_strdup_printf ("marker-reached::%s", entry->id);
entry->marker_id = g_signal_connect (entry->parent,
marker_reached_signal,
G_CALLBACK (on_timeline_marker),
entry);
entry->node = g_node_append_data (node, entry);
g_free (marker_reached_signal);
}
/**
* clutter_score_append_at_frame:
* @score: a #ClutterScore
* @id: a unique string identifying the timeline
* @parent: the parent #ClutterTimeline
* @msecs: the frame of @parent where the new timeline should
* be appended to
* @timeline: the #ClutterTimeline to append
*
* Appends @timeline at the given position on the @parent timeline,
* expressed as a frame number.
*
* This function implicitly creates a timeline marker (see
* clutter_timeline_add_marker_at_frame()) with the given @id
* on @parent.
*
* If you want to append @timeline at the end of @parent, use
* clutter_score_append().
*
* Since: 0.8
*/
void
clutter_score_append_at_frame (ClutterScore *score,
const gchar *id,
ClutterTimeline *parent,
guint frame,
ClutterTimeline *timeline)
{
ClutterScorePrivate *priv;
GNode *node;
ClutterScoreEntry *entry;
gchar *marker_reached_signal;
g_return_if_fail (CLUTTER_IS_SCORE (score));
g_return_if_fail (id != NULL);
g_return_if_fail (CLUTTER_IS_TIMELINE (parent));
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
priv = score->priv;
node = find_entry_by_timeline (score, parent);
if (G_UNLIKELY (!node))
{
g_warning ("Unable to find the parent timeline inside the score.");
return;
}
entry = g_slice_new (ClutterScoreEntry);
entry->type = SCORE_ENTRY_TIME;
entry->timeline = g_object_ref (timeline);
entry->parent = parent;
entry->frame = frame;
entry->id = g_strdup (id);
entry->score = score;
clutter_timeline_add_marker_at_frame (entry->parent,
entry->id,
entry->frame);
marker_reached_signal = g_strdup_printf ("marker-reached::%s", entry->id);
entry->marker_id = g_signal_connect (entry->parent,
marker_reached_signal,
G_CALLBACK (on_timeline_marker),
entry);
entry->node = g_node_append_data (node, entry);
g_free (marker_reached_signal);
} }
/** /**
@ -871,11 +1074,11 @@ clutter_score_append (ClutterScore *score,
* the timeline has other timelines attached to it, those are removed * the timeline has other timelines attached to it, those are removed
* as well. * as well.
* *
* Since: 0.6 * Since: 0.8
*/ */
void void
clutter_score_remove (ClutterScore *score, clutter_score_remove (ClutterScore *score,
guint id) const gchar *id)
{ {
ClutterScorePrivate *priv; ClutterScorePrivate *priv;
TraverseClosure closure; TraverseClosure closure;
@ -915,17 +1118,13 @@ clutter_score_remove_all (ClutterScore *score)
g_return_if_fail (CLUTTER_IS_SCORE (score)); g_return_if_fail (CLUTTER_IS_SCORE (score));
priv = score->priv;
/* this will take care of the running timelines */ /* this will take care of the running timelines */
clutter_score_stop (score); clutter_score_stop (score);
priv = score->priv; /* destroy all the contents of the tree */
clutter_score_clear (score);
g_node_traverse (priv->root,
G_POST_ORDER,
G_TRAVERSE_ALL,
-1,
destroy_entry, NULL);
g_node_destroy (priv->root);
/* recreate the sentinel */ /* recreate the sentinel */
priv->root = g_node_new (NULL); priv->root = g_node_new (NULL);
@ -941,11 +1140,11 @@ clutter_score_remove_all (ClutterScore *score)
* Return value: the requested timeline, or %NULL. This function does * Return value: the requested timeline, or %NULL. This function does
* not increase the reference count on the returned #ClutterTimeline * not increase the reference count on the returned #ClutterTimeline
* *
* Since: 0.6 * Since: 0.8
*/ */
ClutterTimeline * ClutterTimeline *
clutter_score_get_timeline (ClutterScore *score, clutter_score_get_timeline (ClutterScore *score,
guint id) const gchar *id)
{ {
GNode *node; GNode *node;
ClutterScoreEntry *entry; ClutterScoreEntry *entry;

View File

@ -76,25 +76,37 @@ struct _ClutterScoreClass
GType clutter_score_get_type (void) G_GNUC_CONST; 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);
guint clutter_score_append (ClutterScore *score, void clutter_score_set_loop (ClutterScore *score,
ClutterTimeline *parent, gboolean loop);
ClutterTimeline *timeline); gboolean clutter_score_get_loop (ClutterScore *score);
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_append_at_time (ClutterScore *score,
void clutter_score_stop (ClutterScore *score); const gchar *id,
void clutter_score_pause (ClutterScore *score); ClutterTimeline *parent,
void clutter_score_rewind (ClutterScore *score); guint msecs,
gboolean clutter_score_is_playing (ClutterScore *score); ClutterTimeline *timeline);
void clutter_score_append_at_frame (ClutterScore *score,
const gchar *id,
ClutterTimeline *parent,
guint frame,
ClutterTimeline *timeline);
void clutter_score_append (ClutterScore *score,
const gchar *id,
ClutterTimeline *parent,
ClutterTimeline *timeline);
void clutter_score_remove (ClutterScore *score,
const gchar *id);
void clutter_score_remove_all (ClutterScore *score);
ClutterTimeline *clutter_score_get_timeline (ClutterScore *score,
const gchar *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);
gboolean clutter_score_is_playing (ClutterScore *score);
G_END_DECLS G_END_DECLS

View File

@ -42,31 +42,35 @@ main (int argc, char **argv)
ClutterTimeline *timeline_2; ClutterTimeline *timeline_2;
ClutterTimeline *timeline_3; ClutterTimeline *timeline_3;
ClutterTimeline *timeline_4; ClutterTimeline *timeline_4;
ClutterTimeline *timeline_5;
GSList *timelines; GSList *timelines;
guint t1, t2, t3, t4;
clutter_init (&argc, &argv); clutter_init (&argc, &argv);
timeline_1 = clutter_timeline_new (10, 120); timeline_1 = clutter_timeline_new_for_duration (1000);
g_object_set_data_full (G_OBJECT (timeline_1), g_object_set_data_full (G_OBJECT (timeline_1),
"timeline-name", g_strdup ("Timeline 1"), "timeline-name", g_strdup ("Timeline 1"),
g_free); g_free);
timeline_2 = clutter_timeline_clone (timeline_1); timeline_2 = clutter_timeline_new_for_duration (1000);
g_object_set_data_full (G_OBJECT (timeline_2), g_object_set_data_full (G_OBJECT (timeline_2),
"timeline-name", g_strdup ("Timeline 2"), "timeline-name", g_strdup ("Timeline 2"),
g_free); g_free);
timeline_3 = clutter_timeline_clone (timeline_1); timeline_3 = clutter_timeline_new_for_duration (1000);
g_object_set_data_full (G_OBJECT (timeline_3), g_object_set_data_full (G_OBJECT (timeline_3),
"timeline-name", g_strdup ("Timeline 3"), "timeline-name", g_strdup ("Timeline 3"),
g_free); g_free);
timeline_4 = clutter_timeline_clone (timeline_1); timeline_4 = clutter_timeline_new_for_duration (1000);
g_object_set_data_full (G_OBJECT (timeline_4), g_object_set_data_full (G_OBJECT (timeline_4),
"timeline-name", g_strdup ("Timeline 4"), "timeline-name", g_strdup ("Timeline 4"),
g_free); g_free);
timeline_5 = clutter_timeline_new_for_duration (1000);
g_object_set_data_full (G_OBJECT (timeline_5),
"timeline-name", g_strdup ("Timeline 5"),
g_free);
score = clutter_score_new(); score = clutter_score_new();
g_signal_connect (score, "timeline-started", g_signal_connect (score, "timeline-started",
@ -79,13 +83,15 @@ main (int argc, char **argv)
G_CALLBACK (clutter_main_quit), G_CALLBACK (clutter_main_quit),
NULL); NULL);
t1 = clutter_score_append (score, NULL, timeline_1); clutter_score_append (score, "line-0", NULL, timeline_1);
t2 = clutter_score_append (score, timeline_1, timeline_2); clutter_score_append (score, "line-1", timeline_1, timeline_2);
t3 = clutter_score_append (score, timeline_1, timeline_3); clutter_score_append (score, "line-2", timeline_1, timeline_3);
t4 = clutter_score_append (score, timeline_3, timeline_4); clutter_score_append (score, "line-3", timeline_3, timeline_4);
clutter_score_append_at_time (score, "line-4", timeline_2, 500, timeline_5);
timelines = clutter_score_list_timelines (score); timelines = clutter_score_list_timelines (score);
g_assert (4 == g_slist_length (timelines)); g_assert (5 == g_slist_length (timelines));
g_slist_free (timelines); g_slist_free (timelines);
clutter_score_start (score); clutter_score_start (score);
@ -96,6 +102,7 @@ main (int argc, char **argv)
g_object_unref (timeline_2); g_object_unref (timeline_2);
g_object_unref (timeline_3); g_object_unref (timeline_3);
g_object_unref (timeline_4); g_object_unref (timeline_4);
g_object_unref (timeline_5);
g_object_unref (score); g_object_unref (score);
return EXIT_SUCCESS; return EXIT_SUCCESS;