2007-12-04 Emmanuele Bassi <ebassi@openedhand.com>

* 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.
This commit is contained in:
Emmanuele Bassi 2007-12-04 16:26:19 +00:00
parent 094c5f77b8
commit 51a0d5a80f
9 changed files with 646 additions and 295 deletions

View File

@ -1,3 +1,13 @@
2007-12-04 Emmanuele Bassi <ebassi@openedhand.com>
* 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 <pippin@o-hand.com>
* clutter/clutter-shader.c: (bind_glsl_shader): use gchar instead of

View File

@ -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

View File

@ -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 #ClutterTimeline<!-- -->s 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;
}

View File

@ -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 <glib-object.h>
#include <clutter/clutter-timeline.h>
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__ */

View File

@ -1,3 +1,9 @@
2007-12-04 Emmanuele Bassi <ebassi@openedhand.com>
* clutter.types:
* clutter-docs.sgml:
* clutter-sections.txt: Fix ClutterScore symbols.
2007-11-30 Emmanuele Bassi <ebassi@openedhand.com>
* clutter-sections.txt: Update with the newly added API.

View File

@ -106,6 +106,7 @@
<title>Base classes</title>
<xi:include href="xml/clutter-timeline.xml"/>
<xi:include href="xml/clutter-score.xml"/>
<xi:include href="xml/clutter-alpha.xml"/>
<xi:include href="xml/clutter-behaviour.xml"/>
</chapter>

View File

@ -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
<SUBSECTION>
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
<SUBSECTION>
clutter_score_start

View File

@ -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

View File

@ -2,6 +2,38 @@
#include <stdlib.h>
#include <clutter/clutter.h>
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;
}