4 * An OpenGL based 'interactive canvas' library.
6 * Copyright (C) 2008 Intel Corporation.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 * Emmanuele Bassi <ebassi@linux.intel.com>
26 * SECTION:clutter-animation
27 * @short_description: Simple implicit animations
28 * @See_Also: #ClutterAnimatable, #ClutterInterval, #ClutterAlpha,
31 * #ClutterAnimation is an object providing simple, implicit animations
32 * for #GObject<!-- -->s.
34 * #ClutterAnimation instances will bind one or more #GObject properties
35 * belonging to a #GObject to a #ClutterInterval, and will then use a
36 * #ClutterAlpha to interpolate the property between the initial and final
37 * values of the interval.
39 * The duration of the animation is set using clutter_animation_set_duration().
40 * The easing mode of the animation is set using clutter_animation_set_mode().
42 * If you want to control the animation you should retrieve the
43 * #ClutterTimeline using clutter_animation_get_timeline() and then
44 * use #ClutterTimeline functions like clutter_timeline_start(),
45 * clutter_timeline_pause() or clutter_timeline_stop().
47 * A #ClutterAnimation will emit the #ClutterAnimation::completed signal
48 * when the #ClutterTimeline used by the animation is completed; unlike
49 * #ClutterTimeline, though, the #ClutterAnimation::completed will not be
50 * emitted if #ClutterAnimation:loop is set to %TRUE - that is, a looping
51 * animation never completes.
53 * If your animation depends on user control you can force its completion
54 * using clutter_animation_completed().
56 * If the #GObject instance bound to a #ClutterAnimation implements the
57 * #ClutterAnimatable interface it is possible for that instance to
58 * control the way the initial and final states are interpolated.
60 * #ClutterAnimation<!-- -->s are distinguished from #ClutterBehaviour<!-- -->s
61 * because the former can only control #GObject properties of a single
62 * #GObject instance, while the latter can control multiple properties
63 * using accessor functions inside the #ClutterBehaviour
64 * <function>alpha_notify</function> virtual function, and can control
65 * multiple #ClutterActor<!-- -->s as well.
67 * For convenience, it is possible to use the clutter_actor_animate()
68 * function call which will take care of setting up and tearing down
69 * a #ClutterAnimation instance and animate an actor between its current
70 * state and the specified final state.
72 * <refsect2 id="clutter-AnimationMode-Script">
73 * <title>Defining ClutterAnimationMode inside ClutterScript</title>
74 * <para>When defining a #ClutterAnimation inside a ClutterScript
75 * file or string the #ClutterAnimation:mode can be defined either
76 * using the #ClutterAnimationMode enumeration values through their
77 * "nick" (the short string used inside #GEnumValue), their numeric
78 * id, or using the following strings:</para>
81 * <term>easeInQuad, easeOutQuad, easeInOutQuad</term>
82 * <listitem><para>Corresponding to the quadratic easing
83 * modes</para></listitem>
86 * <term>easeInCubic, easeOutCubic, easeInOutCubic</term>
87 * <listitem><para>Corresponding to the cubic easing
88 * modes</para></listitem>
91 * <term>easeInQuart, easeOutQuart, easeInOutQuart</term>
92 * <listitem><para>Corresponding to the quartic easing
93 * modes</para></listitem>
96 * <term>easeInQuint, easeOutQuint, easeInOutQuint</term>
97 * <listitem><para>Corresponding to the quintic easing
98 * modes</para></listitem>
101 * <term>easeInSine, easeOutSine, easeInOutSine</term>
102 * <listitem><para>Corresponding to the sine easing
103 * modes</para></listitem>
106 * <term>easeInExpo, easeOutExpo, easeInOutExpo</term>
107 * <listitem><para>Corresponding to the exponential easing
108 * modes</para></listitem>
111 * <term>easeInCirc, easeOutCirc, easeInOutCirc</term>
112 * <listitem><para>Corresponding to the circular easing
113 * modes</para></listitem>
116 * <term>easeInElastic, easeOutElastic, easeInOutElastic</term>
117 * <listitem><para>Corresponding to the overshooting elastic
118 * easing modes</para></listitem>
121 * <term>easeInBack, easeOutBack, easeInOutBack</term>
122 * <listitem><para>Corresponding to the overshooting cubic
123 * easing modes</para></listitem>
126 * <term>easeInBounce, easeOutBounce, easeInOutBounce</term>
127 * <listitem><para>Corresponding to the bouncing easing
128 * modes</para></listitem>
133 * <example id="example-clutter-animation">
134 * <title>Tweening using clutter_actor_animate()</title>
136 * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../tests/interactive/test-easing.c">
137 * <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
142 * #ClutterAnimation is available since Clutter 1.0
149 #include <glib-object.h>
150 #include <gobject/gvaluecollector.h>
152 #include "clutter-alpha.h"
153 #include "clutter-animatable.h"
154 #include "clutter-animation.h"
155 #include "clutter-debug.h"
156 #include "clutter-enum-types.h"
157 #include "clutter-interval.h"
158 #include "clutter-marshal.h"
159 #include "clutter-private.h"
160 #include "clutter-scriptable.h"
161 #include "clutter-script-private.h"
163 #include "deprecated/clutter-animation.h"
179 static GParamSpec *obj_props[PROP_LAST];
189 #define CLUTTER_ANIMATION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_ANIMATION, ClutterAnimationPrivate))
191 struct _ClutterAnimationPrivate
195 GHashTable *properties;
198 ClutterTimeline *timeline;
200 guint timeline_started_id;
201 guint timeline_completed_id;
202 guint timeline_frame_id;
205 static guint animation_signals[LAST_SIGNAL] = { 0, };
207 static GQuark quark_object_animation = 0;
209 static void clutter_scriptable_init (ClutterScriptableIface *iface);
211 static void clutter_animation_set_alpha_internal (ClutterAnimation *animation,
212 ClutterAlpha *alpha);
213 static ClutterAlpha * clutter_animation_get_alpha_internal (ClutterAnimation *animation);
214 static ClutterTimeline * clutter_animation_get_timeline_internal (ClutterAnimation *animation);
216 G_DEFINE_TYPE_WITH_CODE (ClutterAnimation, clutter_animation, G_TYPE_OBJECT,
217 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
218 clutter_scriptable_init));
220 static ClutterAlpha *
221 clutter_animation_get_alpha_internal (ClutterAnimation *animation)
223 ClutterAnimationPrivate *priv = animation->priv;
225 if (priv->alpha == NULL)
229 alpha = clutter_alpha_new ();
230 clutter_alpha_set_mode (alpha, CLUTTER_LINEAR);
232 priv->alpha = g_object_ref_sink (alpha);
234 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_ALPHA]);
241 on_actor_destroy (ClutterActor *actor,
242 ClutterAnimation *animation)
244 ClutterAnimationPrivate *priv = animation->priv;
245 GObject *obj = G_OBJECT (actor);
247 if (obj == priv->object)
249 g_object_set_qdata (priv->object, quark_object_animation, NULL);
250 g_signal_handlers_disconnect_by_func (priv->object,
251 G_CALLBACK (on_actor_destroy),
253 g_object_unref (animation);
258 clutter_animation_real_completed (ClutterAnimation *self)
260 ClutterAnimationPrivate *priv = self->priv;
261 ClutterAnimatable *animatable = NULL;
262 ClutterAnimation *animation;
263 ClutterTimeline *timeline;
264 ClutterTimelineDirection direction;
268 timeline = clutter_animation_get_timeline (self);
269 direction = clutter_timeline_get_direction (timeline);
271 if (CLUTTER_IS_ANIMATABLE (priv->object))
272 animatable = CLUTTER_ANIMATABLE (priv->object);
274 /* explicitly set the final state of the animation */
275 CLUTTER_NOTE (ANIMATION, "Set final state on object [%p]", priv->object);
276 g_hash_table_iter_init (&iter, priv->properties);
277 while (g_hash_table_iter_next (&iter, &key, &value))
279 const gchar *p_name = key;
280 ClutterInterval *interval = value;
283 if (direction == CLUTTER_TIMELINE_FORWARD)
284 p_value = clutter_interval_peek_final_value (interval);
286 p_value = clutter_interval_peek_initial_value (interval);
288 if (animatable != NULL)
289 clutter_animatable_set_final_state (animatable, p_name, p_value);
291 g_object_set_property (priv->object, p_name, p_value);
294 /* at this point, if this animation was created by clutter_actor_animate()
295 * and friends, the animation will be attached to the object's data; since
296 * we want to allow developers to use g_signal_connect_after("completed")
297 * to concatenate a new animation, we need to remove the animation back
298 * pointer here, and unref() the animation. FIXME - we might want to
299 * provide a clutter_animation_attach()/clutter_animation_detach() pair
300 * to let the user reattach an animation
302 animation = g_object_get_qdata (priv->object, quark_object_animation);
303 if (animation == self)
305 CLUTTER_NOTE (ANIMATION, "Unsetting animation for actor [%p]",
308 g_object_set_qdata (priv->object, quark_object_animation, NULL);
309 g_signal_handlers_disconnect_by_func (priv->object,
310 G_CALLBACK (on_actor_destroy),
313 CLUTTER_NOTE (ANIMATION, "Releasing the reference Animation [%p]",
315 g_object_unref (animation);
320 clutter_animation_finalize (GObject *gobject)
322 ClutterAnimationPrivate *priv = CLUTTER_ANIMATION (gobject)->priv;
324 CLUTTER_NOTE (ANIMATION,
325 "Destroying properties table for Animation [%p]",
327 g_hash_table_destroy (priv->properties);
329 G_OBJECT_CLASS (clutter_animation_parent_class)->finalize (gobject);
333 clutter_animation_dispose (GObject *gobject)
335 ClutterAnimationPrivate *priv = CLUTTER_ANIMATION (gobject)->priv;
336 ClutterTimeline *timeline;
338 if (priv->alpha != NULL)
339 timeline = clutter_alpha_get_timeline (priv->alpha);
341 timeline = priv->timeline;
343 if (timeline != NULL && priv->timeline_started_id != 0)
344 g_signal_handler_disconnect (timeline, priv->timeline_started_id);
346 if (timeline != NULL && priv->timeline_completed_id != 0)
347 g_signal_handler_disconnect (timeline, priv->timeline_completed_id);
349 if (timeline != NULL && priv->timeline_frame_id != 0)
350 g_signal_handler_disconnect (timeline, priv->timeline_frame_id);
352 priv->timeline_started_id = 0;
353 priv->timeline_completed_id = 0;
354 priv->timeline_frame_id = 0;
356 if (priv->alpha != NULL)
358 g_object_unref (priv->alpha);
362 if (priv->object != NULL)
364 g_object_unref (priv->object);
368 G_OBJECT_CLASS (clutter_animation_parent_class)->dispose (gobject);
372 clutter_animation_set_property (GObject *gobject,
377 ClutterAnimation *animation = CLUTTER_ANIMATION (gobject);
382 clutter_animation_set_object (animation, g_value_get_object (value));
386 clutter_animation_set_mode (animation, g_value_get_ulong (value));
390 clutter_animation_set_duration (animation, g_value_get_uint (value));
394 clutter_animation_set_loop (animation, g_value_get_boolean (value));
398 clutter_animation_set_timeline (animation, g_value_get_object (value));
402 clutter_animation_set_alpha_internal (animation, g_value_get_object (value));
406 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
412 clutter_animation_get_property (GObject *gobject,
417 ClutterAnimation *animation = CLUTTER_ANIMATION (gobject);
418 ClutterAnimationPrivate *priv = animation->priv;
423 g_value_set_object (value, priv->object);
427 g_value_set_ulong (value, clutter_animation_get_mode (animation));
431 g_value_set_uint (value, clutter_animation_get_duration (animation));
435 g_value_set_boolean (value, clutter_animation_get_loop (animation));
439 g_value_set_object (value, clutter_animation_get_timeline (animation));
443 g_value_set_object (value, clutter_animation_get_alpha_internal (animation));
447 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
453 clutter_animation_parse_custom_node (ClutterScriptable *scriptable,
454 ClutterScript *script,
459 if (strncmp (name, "mode", 4) == 0)
463 mode = _clutter_script_resolve_animation_mode (node);
465 g_value_init (value, G_TYPE_ULONG);
466 g_value_set_ulong (value, mode);
475 clutter_scriptable_init (ClutterScriptableIface *iface)
477 iface->parse_custom_node = clutter_animation_parse_custom_node;
481 clutter_animation_class_init (ClutterAnimationClass *klass)
483 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
485 quark_object_animation =
486 g_quark_from_static_string ("clutter-actor-animation");
488 g_type_class_add_private (klass, sizeof (ClutterAnimationPrivate));
490 klass->completed = clutter_animation_real_completed;
492 gobject_class->set_property = clutter_animation_set_property;
493 gobject_class->get_property = clutter_animation_get_property;
494 gobject_class->dispose = clutter_animation_dispose;
495 gobject_class->finalize = clutter_animation_finalize;
498 * ClutterAnimation:object:
500 * The #GObject to which the animation applies.
504 obj_props[PROP_OBJECT] =
505 g_param_spec_object ("object",
507 P_("Object to which the animation applies"),
509 CLUTTER_PARAM_READWRITE);
512 * ClutterAnimation:mode:
514 * The animation mode, either a value from #ClutterAnimationMode
515 * or a value returned by clutter_alpha_register_func(). The
516 * default value is %CLUTTER_LINEAR.
520 obj_props[PROP_MODE] =
521 g_param_spec_ulong ("mode",
523 P_("The mode of the animation"),
526 CLUTTER_PARAM_READWRITE);
529 * ClutterAnimation:duration:
531 * The duration of the animation, expressed in milliseconds.
535 obj_props[PROP_DURATION] =
536 g_param_spec_uint ("duration",
538 P_("Duration of the animation, in milliseconds"),
541 CLUTTER_PARAM_READWRITE);
544 * ClutterAnimation:loop:
546 * Whether the animation should loop.
550 obj_props[PROP_LOOP] =
551 g_param_spec_boolean ("loop",
553 P_("Whether the animation should loop"),
555 CLUTTER_PARAM_READWRITE);
558 * ClutterAnimation:timeline:
560 * The #ClutterTimeline used by the animation.
564 obj_props[PROP_TIMELINE] =
565 g_param_spec_object ("timeline",
567 P_("The timeline used by the animation"),
568 CLUTTER_TYPE_TIMELINE,
569 CLUTTER_PARAM_READWRITE);
572 * ClutterAnimation:alpha:
574 * The #ClutterAlpha used by the animation.
578 * Deprecated: 1.10: Use the #ClutterAnimation:timeline property and
579 * the #ClutterTimeline:progress-mode property instead.
581 obj_props[PROP_ALPHA] =
582 g_param_spec_object ("alpha",
584 P_("The alpha used by the animation"),
586 CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
588 g_object_class_install_properties (gobject_class,
593 * ClutterAnimation::started:
594 * @animation: the animation that emitted the signal
596 * The ::started signal is emitted once the animation has been
601 animation_signals[STARTED] =
602 g_signal_new (I_("started"),
603 G_TYPE_FROM_CLASS (klass),
605 G_STRUCT_OFFSET (ClutterAnimationClass, started),
607 _clutter_marshal_VOID__VOID,
611 * ClutterAnimation::completed:
612 * @animation: the animation that emitted the signal
614 * The ::completed signal is emitted once the animation has
617 * The @animation instance is guaranteed to be valid for the entire
618 * duration of the signal emission chain.
622 animation_signals[COMPLETED] =
623 g_signal_new (I_("completed"),
624 G_TYPE_FROM_CLASS (klass),
626 G_STRUCT_OFFSET (ClutterAnimationClass, completed),
628 _clutter_marshal_VOID__VOID,
633 clutter_animation_init (ClutterAnimation *self)
635 self->priv = CLUTTER_ANIMATION_GET_PRIVATE (self);
637 self->priv->properties =
638 g_hash_table_new_full (g_str_hash, g_str_equal,
639 (GDestroyNotify) g_free,
640 (GDestroyNotify) g_object_unref);
644 clutter_animation_bind_property_internal (ClutterAnimation *animation,
645 const gchar *property_name,
647 ClutterInterval *interval)
649 ClutterAnimationPrivate *priv = animation->priv;
651 if (!clutter_interval_validate (interval, pspec))
653 g_warning ("Cannot bind property '%s': the interval is out "
659 g_hash_table_insert (priv->properties,
660 g_strdup (property_name),
661 g_object_ref_sink (interval));
665 clutter_animation_update_property_internal (ClutterAnimation *animation,
666 const gchar *property_name,
668 ClutterInterval *interval)
670 ClutterAnimationPrivate *priv = animation->priv;
672 if (!clutter_interval_validate (interval, pspec))
674 g_warning ("Cannot bind property '%s': the interval is out "
680 g_hash_table_replace (priv->properties,
681 g_strdup (property_name),
682 g_object_ref_sink (interval));
686 clutter_animation_validate_bind (ClutterAnimation *animation,
687 const char *property_name,
690 ClutterAnimationPrivate *priv;
694 priv = animation->priv;
696 if (G_UNLIKELY (!priv->object))
698 g_warning ("Cannot bind property '%s': the animation has no "
699 "object set. You need to call clutter_animation_set_object() "
700 "first to be able to bind a property",
705 if (G_UNLIKELY (clutter_animation_has_property (animation, property_name)))
707 g_warning ("Cannot bind property '%s': the animation already has "
708 "a bound property with the same name",
713 if (CLUTTER_IS_ANIMATABLE (priv->object))
715 ClutterAnimatable *animatable = CLUTTER_ANIMATABLE (priv->object);
717 pspec = clutter_animatable_find_property (animatable, property_name);
721 GObjectClass *klass = G_OBJECT_GET_CLASS (priv->object);
723 pspec = g_object_class_find_property (klass, property_name);
728 g_warning ("Cannot bind property '%s': objects of type '%s' have "
731 g_type_name (G_OBJECT_TYPE (priv->object)));
735 if (!(pspec->flags & G_PARAM_WRITABLE))
737 g_warning ("Cannot bind property '%s': the property is not writable",
742 pspec_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
744 if (g_value_type_transformable (argtype, pspec_type))
748 g_warning ("Cannot bind property '%s': the interval value of "
749 "type '%s' is not compatible with the property value "
752 g_type_name (argtype),
753 g_type_name (pspec_type));
759 * clutter_animation_bind_interval:
760 * @animation: a #ClutterAnimation
761 * @property_name: the property to control
762 * @interval: (transfer full): a #ClutterInterval
764 * Binds @interval to the @property_name of the #GObject
765 * attached to @animation. The #ClutterAnimation will take
766 * ownership of the passed #ClutterInterval. For more information
767 * about animations, see clutter_actor_animate().
769 * If you need to update the interval instance use
770 * clutter_animation_update_interval() instead.
772 * Return value: (transfer none): The animation itself.
776 clutter_animation_bind_interval (ClutterAnimation *animation,
777 const gchar *property_name,
778 ClutterInterval *interval)
782 g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
783 g_return_val_if_fail (property_name != NULL, NULL);
784 g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), NULL);
786 pspec = clutter_animation_validate_bind (animation, property_name,
787 clutter_interval_get_value_type (interval));
791 clutter_animation_bind_property_internal (animation, property_name,
800 * clutter_animation_bind:
801 * @animation: a #ClutterAnimation
802 * @property_name: the property to control
803 * @final: The final value of the property
805 * Adds a single property with name @property_name to the
806 * animation @animation. For more information about animations,
807 * see clutter_actor_animate().
809 * This method returns the animation primarily to make chained
810 * calls convenient in language bindings.
812 * Return value: (transfer none): The animation itself.
817 clutter_animation_bind (ClutterAnimation *animation,
818 const gchar *property_name,
821 ClutterAnimationPrivate *priv;
823 ClutterInterval *interval;
825 GValue initial = G_VALUE_INIT;
826 GValue real_final = G_VALUE_INIT;
828 g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
829 g_return_val_if_fail (property_name != NULL, NULL);
831 priv = animation->priv;
833 type = G_VALUE_TYPE (final);
834 pspec = clutter_animation_validate_bind (animation, property_name, type);
838 g_value_init (&real_final, G_PARAM_SPEC_VALUE_TYPE (pspec));
839 if (!g_value_transform (final, &real_final))
841 g_value_unset (&real_final);
842 g_warning ("Unable to transform the value of type '%s' to a value "
843 "of '%s' compatible with the property '%s'of the object "
846 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
848 G_OBJECT_TYPE_NAME (priv->object));
852 g_value_init (&initial, G_PARAM_SPEC_VALUE_TYPE (pspec));
854 if (CLUTTER_IS_ANIMATABLE (priv->object))
855 clutter_animatable_get_initial_state (CLUTTER_ANIMATABLE (priv->object),
859 g_object_get_property (priv->object, property_name, &initial);
861 interval = clutter_interval_new_with_values (G_PARAM_SPEC_VALUE_TYPE (pspec),
865 g_value_unset (&initial);
866 g_value_unset (&real_final);
868 clutter_animation_bind_property_internal (animation, property_name,
877 * clutter_animation_unbind_property:
878 * @animation: a #ClutterAnimation
879 * @property_name: name of the property
881 * Removes @property_name from the list of animated properties.
886 clutter_animation_unbind_property (ClutterAnimation *animation,
887 const gchar *property_name)
889 ClutterAnimationPrivate *priv;
891 g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
892 g_return_if_fail (property_name != NULL);
894 priv = animation->priv;
896 if (!clutter_animation_has_property (animation, property_name))
898 g_warning ("Cannot unbind property '%s': the animation has "
899 "no bound property with that name",
904 g_hash_table_remove (priv->properties, property_name);
908 * clutter_animation_has_property:
909 * @animation: a #ClutterAnimation
910 * @property_name: name of the property
912 * Checks whether @animation is controlling @property_name.
914 * Return value: %TRUE if the property is animated by the
915 * #ClutterAnimation, %FALSE otherwise
920 clutter_animation_has_property (ClutterAnimation *animation,
921 const gchar *property_name)
923 ClutterAnimationPrivate *priv;
925 g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), FALSE);
926 g_return_val_if_fail (property_name != NULL, FALSE);
928 priv = animation->priv;
930 return g_hash_table_lookup (priv->properties, property_name) != NULL;
934 * clutter_animation_update_interval:
935 * @animation: a #ClutterAnimation
936 * @property_name: name of the property
937 * @interval: a #ClutterInterval
939 * Changes the @interval for @property_name. The #ClutterAnimation
940 * will take ownership of the passed #ClutterInterval.
945 clutter_animation_update_interval (ClutterAnimation *animation,
946 const gchar *property_name,
947 ClutterInterval *interval)
949 ClutterAnimationPrivate *priv;
951 GType pspec_type, int_type;
953 g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
954 g_return_if_fail (property_name != NULL);
955 g_return_if_fail (CLUTTER_IS_INTERVAL (interval));
957 priv = animation->priv;
959 if (!clutter_animation_has_property (animation, property_name))
961 g_warning ("Cannot update property '%s': the animation has "
962 "no bound property with that name",
967 if (CLUTTER_IS_ANIMATABLE (priv->object))
969 ClutterAnimatable *animatable = CLUTTER_ANIMATABLE (priv->object);
971 pspec = clutter_animatable_find_property (animatable, property_name);
975 GObjectClass *klass = G_OBJECT_GET_CLASS (priv->object);
977 pspec = g_object_class_find_property (klass, property_name);
982 g_warning ("Cannot update property '%s': objects of type '%s' have "
985 g_type_name (G_OBJECT_TYPE (priv->object)));
989 pspec_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
990 int_type = clutter_interval_get_value_type (interval);
992 if (!g_value_type_compatible (int_type, pspec_type) ||
993 !g_value_type_transformable (int_type, pspec_type))
995 g_warning ("Cannot update property '%s': the interval value of "
996 "type '%s' is not compatible with the property value "
999 g_type_name (int_type),
1000 g_type_name (pspec_type));
1004 clutter_animation_update_property_internal (animation, property_name,
1010 * clutter_animation_update:
1011 * @animation: a #ClutterAnimation
1012 * @property_name: name of the property
1013 * @final: The final value of the property
1015 * Updates the @final value of the interval for @property_name
1017 * Return value: (transfer none): The animation itself.
1022 clutter_animation_update (ClutterAnimation *animation,
1023 const gchar *property_name,
1024 const GValue *final)
1026 ClutterInterval *interval;
1029 g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
1030 g_return_val_if_fail (property_name != NULL, NULL);
1031 g_return_val_if_fail (final != NULL, NULL);
1032 g_return_val_if_fail (G_VALUE_TYPE (final) != G_TYPE_INVALID, NULL);
1034 interval = clutter_animation_get_interval (animation, property_name);
1035 if (interval == NULL)
1037 g_warning ("Cannot update property '%s': the animation has "
1038 "no bound property with that name",
1043 int_type = clutter_interval_get_value_type (interval);
1045 if (!g_value_type_compatible (G_VALUE_TYPE (final), int_type) ||
1046 !g_value_type_transformable (G_VALUE_TYPE (final), int_type))
1048 g_warning ("Cannot update property '%s': the interval value of "
1049 "type '%s' is not compatible with the property value "
1052 g_type_name (int_type),
1053 g_type_name (G_VALUE_TYPE (final)));
1057 clutter_interval_set_final_value (interval, final);
1063 * clutter_animation_get_interval:
1064 * @animation: a #ClutterAnimation
1065 * @property_name: name of the property
1067 * Retrieves the #ClutterInterval associated to @property_name
1068 * inside @animation.
1070 * Return value: (transfer none): a #ClutterInterval or %NULL if no
1071 * property with the same name was found. The returned interval is
1072 * owned by the #ClutterAnimation and should not be unreferenced
1077 clutter_animation_get_interval (ClutterAnimation *animation,
1078 const gchar *property_name)
1080 ClutterAnimationPrivate *priv;
1082 g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
1083 g_return_val_if_fail (property_name != NULL, NULL);
1085 priv = animation->priv;
1087 return g_hash_table_lookup (priv->properties, property_name);
1091 on_timeline_started (ClutterTimeline *timeline,
1092 ClutterAnimation *animation)
1094 g_signal_emit (animation, animation_signals[STARTED], 0);
1098 on_timeline_completed (ClutterTimeline *timeline,
1099 ClutterAnimation *animation)
1101 CLUTTER_NOTE (ANIMATION, "Timeline [%p] complete", timeline);
1103 if (!clutter_animation_get_loop (animation))
1104 g_signal_emit (animation, animation_signals[COMPLETED], 0);
1108 on_timeline_frame (ClutterTimeline *timeline,
1110 ClutterAnimation *animation)
1112 ClutterAnimationPrivate *priv;
1113 GList *properties, *p;
1114 gdouble alpha_value;
1115 gboolean is_animatable = FALSE;
1116 ClutterAnimatable *animatable = NULL;
1118 /* make sure the animation survives the notification */
1119 g_object_ref (animation);
1121 priv = animation->priv;
1123 if (priv->alpha != NULL)
1124 alpha_value = clutter_alpha_get_alpha (priv->alpha);
1126 alpha_value = clutter_timeline_get_progress (priv->timeline);
1128 if (CLUTTER_IS_ANIMATABLE (priv->object))
1130 animatable = CLUTTER_ANIMATABLE (priv->object);
1131 is_animatable = TRUE;
1134 g_object_freeze_notify (priv->object);
1136 properties = g_hash_table_get_keys (priv->properties);
1137 for (p = properties; p != NULL; p = p->next)
1139 const gchar *p_name = p->data;
1140 ClutterInterval *interval;
1141 GValue value = G_VALUE_INIT;
1144 interval = g_hash_table_lookup (priv->properties, p_name);
1145 g_assert (CLUTTER_IS_INTERVAL (interval));
1147 g_value_init (&value, clutter_interval_get_value_type (interval));
1151 apply = clutter_animatable_interpolate_value (animatable, p_name,
1158 apply = clutter_interval_compute_value (interval,
1166 clutter_animatable_set_final_state (animatable, p_name, &value);
1168 g_object_set_property (priv->object, p_name, &value);
1171 g_value_unset (&value);
1174 g_list_free (properties);
1176 g_object_thaw_notify (priv->object);
1178 g_object_unref (animation);
1181 static ClutterTimeline *
1182 clutter_animation_get_timeline_internal (ClutterAnimation *animation)
1184 ClutterAnimationPrivate *priv = animation->priv;
1185 ClutterTimeline *timeline;
1187 if (priv->timeline != NULL)
1188 return priv->timeline;
1190 if (priv->alpha != NULL)
1192 timeline = clutter_alpha_get_timeline (priv->alpha);
1193 if (timeline != NULL)
1197 timeline = g_object_new (CLUTTER_TYPE_TIMELINE, NULL);
1199 priv->timeline_started_id =
1200 g_signal_connect (timeline, "started",
1201 G_CALLBACK (on_timeline_started),
1204 priv->timeline_completed_id =
1205 g_signal_connect (timeline, "completed",
1206 G_CALLBACK (on_timeline_completed),
1209 priv->timeline_frame_id =
1210 g_signal_connect (timeline, "new-frame",
1211 G_CALLBACK (on_timeline_frame),
1214 if (priv->alpha != NULL)
1216 clutter_alpha_set_timeline (priv->alpha, timeline);
1218 /* the alpha owns the timeline now */
1219 g_object_unref (timeline);
1222 priv->timeline = timeline;
1224 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_TIMELINE]);
1226 return priv->timeline;
1230 clutter_animation_set_alpha_internal (ClutterAnimation *animation,
1231 ClutterAlpha *alpha)
1233 ClutterAnimationPrivate *priv;
1234 ClutterTimeline *timeline;
1236 priv = animation->priv;
1238 if (priv->alpha == alpha)
1241 g_object_freeze_notify (G_OBJECT (animation));
1243 if (priv->alpha != NULL)
1244 timeline = clutter_alpha_get_timeline (priv->alpha);
1248 /* disconnect the old timeline first */
1249 if (timeline != NULL && priv->timeline_started_id != 0)
1251 g_signal_handler_disconnect (timeline, priv->timeline_started_id);
1252 priv->timeline_started_id = 0;
1255 if (timeline != NULL && priv->timeline_completed_id != 0)
1257 g_signal_handler_disconnect (timeline, priv->timeline_completed_id);
1258 priv->timeline_completed_id = 0;
1261 /* then we need to disconnect the signal handler from the old alpha */
1262 if (timeline != NULL && priv->timeline_frame_id != 0)
1264 g_signal_handler_disconnect (timeline, priv->timeline_frame_id);
1265 priv->timeline_frame_id = 0;
1268 if (priv->alpha != NULL)
1270 /* this will take care of any reference we hold on the timeline */
1271 g_object_unref (priv->alpha);
1278 priv->alpha = g_object_ref_sink (alpha);
1280 /* if the alpha has a timeline then we use it, otherwise we create one */
1281 timeline = clutter_alpha_get_timeline (priv->alpha);
1282 if (timeline != NULL)
1284 priv->timeline_started_id =
1285 g_signal_connect (timeline, "started",
1286 G_CALLBACK (on_timeline_started),
1288 priv->timeline_completed_id =
1289 g_signal_connect (timeline, "completed",
1290 G_CALLBACK (on_timeline_completed),
1292 priv->timeline_frame_id =
1293 g_signal_connect (timeline, "new-frame",
1294 G_CALLBACK (on_timeline_frame),
1299 /* FIXME - add a create_timeline_internal() because this does
1300 * not look very good
1302 (void) clutter_animation_get_timeline_internal (animation);
1306 /* emit all relevant notifications */
1307 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_MODE]);
1308 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_DURATION]);
1309 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_LOOP]);
1310 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_ALPHA]);
1311 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_TIMELINE]);
1313 g_object_thaw_notify (G_OBJECT (animation));
1317 * clutter_animation_new:
1319 * Creates a new #ClutterAnimation instance. You should set the
1320 * #GObject to be animated using clutter_animation_set_object(),
1321 * set the duration with clutter_animation_set_duration() and the
1322 * easing mode using clutter_animation_set_mode().
1324 * Use clutter_animation_bind() or clutter_animation_bind_interval()
1325 * to define the properties to be animated. The interval and the
1326 * animated properties can be updated at runtime.
1328 * The clutter_actor_animate() and relative family of functions provide
1329 * an easy way to animate a #ClutterActor and automatically manage the
1330 * lifetime of a #ClutterAnimation instance, so you should consider using
1331 * those functions instead of manually creating an animation.
1333 * Return value: the newly created #ClutterAnimation. Use g_object_unref()
1334 * to release the associated resources
1339 clutter_animation_new (void)
1341 return g_object_new (CLUTTER_TYPE_ANIMATION, NULL);
1345 * clutter_animation_set_object:
1346 * @animation: a #ClutterAnimation
1347 * @object: a #GObject
1349 * Attaches @animation to @object. The #ClutterAnimation will take a
1350 * reference on @object.
1355 clutter_animation_set_object (ClutterAnimation *animation,
1358 ClutterAnimationPrivate *priv;
1360 g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
1361 g_return_if_fail (object == NULL || G_IS_OBJECT (object));
1363 priv = animation->priv;
1365 if (priv->object != NULL)
1367 g_object_set_qdata (priv->object, quark_object_animation, NULL);
1369 g_object_unref (priv->object);
1370 priv->object = NULL;
1374 priv->object = g_object_ref (object);
1376 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_OBJECT]);
1380 * clutter_animation_get_object:
1381 * @animation: a #ClutterAnimation
1383 * Retrieves the #GObject attached to @animation.
1385 * Return value: (transfer none): a #GObject
1390 clutter_animation_get_object (ClutterAnimation *animation)
1392 g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
1394 return animation->priv->object;
1398 * clutter_animation_set_mode:
1399 * @animation: a #ClutterAnimation
1400 * @mode: an animation mode logical id
1402 * Sets the animation @mode of @animation. The animation @mode is
1403 * a logical id, either coming from the #ClutterAnimationMode enumeration
1404 * or the return value of clutter_alpha_register_func().
1406 * This function will also set #ClutterAnimation:alpha if needed.
1411 clutter_animation_set_mode (ClutterAnimation *animation,
1414 g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
1416 g_object_freeze_notify (G_OBJECT (animation));
1418 if (animation->priv->alpha != NULL || mode > CLUTTER_ANIMATION_LAST)
1420 ClutterAlpha *alpha;
1422 if (animation->priv->alpha == NULL)
1423 alpha = clutter_animation_get_alpha_internal (animation);
1425 alpha = animation->priv->alpha;
1427 clutter_alpha_set_mode (alpha, mode);
1431 ClutterTimeline *timeline;
1433 timeline = clutter_animation_get_timeline_internal (animation);
1435 clutter_timeline_set_progress_mode (timeline, mode);
1438 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_MODE]);
1440 g_object_thaw_notify (G_OBJECT (animation));
1444 * clutter_animation_get_mode:
1445 * @animation: a #ClutterAnimation
1447 * Retrieves the animation mode of @animation, as set by
1448 * clutter_animation_set_mode().
1450 * Return value: the mode for the animation
1455 clutter_animation_get_mode (ClutterAnimation *animation)
1457 ClutterTimeline *timeline;
1459 g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), CLUTTER_LINEAR);
1461 if (animation->priv->alpha != NULL)
1462 return clutter_alpha_get_mode (animation->priv->alpha);
1464 timeline = clutter_animation_get_timeline_internal (animation);
1466 return clutter_timeline_get_progress_mode (timeline);
1470 * clutter_animation_set_duration:
1471 * @animation: a #ClutterAnimation
1472 * @msecs: the duration in milliseconds
1474 * Sets the duration of @animation in milliseconds.
1476 * This function will set #ClutterAnimation:alpha and
1477 * #ClutterAnimation:timeline if needed.
1482 clutter_animation_set_duration (ClutterAnimation *animation,
1485 ClutterTimeline *timeline;
1487 g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
1489 g_object_freeze_notify (G_OBJECT (animation));
1491 timeline = clutter_animation_get_timeline_internal (animation);
1492 clutter_timeline_set_duration (timeline, msecs);
1493 clutter_timeline_rewind (timeline);
1495 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_DURATION]);
1497 g_object_thaw_notify (G_OBJECT (animation));
1501 * clutter_animation_set_loop:
1502 * @animation: a #ClutterAnimation
1503 * @loop: %TRUE if the animation should loop
1505 * Sets whether @animation should loop over itself once finished.
1507 * A looping #ClutterAnimation will not emit the #ClutterAnimation::completed
1508 * signal when finished.
1510 * This function will set #ClutterAnimation:alpha and
1511 * #ClutterAnimation:timeline if needed.
1516 clutter_animation_set_loop (ClutterAnimation *animation,
1519 ClutterTimeline *timeline;
1521 g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
1523 g_object_freeze_notify (G_OBJECT (animation));
1525 timeline = clutter_animation_get_timeline_internal (animation);
1526 clutter_timeline_set_repeat_count (timeline, loop ? -1 : 0);
1528 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_LOOP]);
1530 g_object_thaw_notify (G_OBJECT (animation));
1534 * clutter_animation_get_loop:
1535 * @animation: a #ClutterAnimation
1537 * Retrieves whether @animation is looping.
1539 * Return value: %TRUE if the animation is looping
1544 clutter_animation_get_loop (ClutterAnimation *animation)
1546 ClutterTimeline *timeline;
1548 g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), FALSE);
1550 timeline = clutter_animation_get_timeline_internal (animation);
1552 return clutter_timeline_get_repeat_count (timeline) != 0;
1556 * clutter_animation_get_duration:
1557 * @animation: a #ClutterAnimation
1559 * Retrieves the duration of @animation, in milliseconds.
1561 * Return value: the duration of the animation
1566 clutter_animation_get_duration (ClutterAnimation *animation)
1568 ClutterTimeline *timeline;
1570 g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), 0);
1572 timeline = clutter_animation_get_timeline_internal (animation);
1574 return clutter_timeline_get_duration (timeline);
1578 * clutter_animation_set_timeline:
1579 * @animation: a #ClutterAnimation
1580 * @timeline: (allow-none): a #ClutterTimeline, or %NULL to unset the
1581 * current #ClutterTimeline
1583 * Sets the #ClutterTimeline used by @animation.
1585 * This function will take a reference on the passed @timeline.
1590 clutter_animation_set_timeline (ClutterAnimation *animation,
1591 ClutterTimeline *timeline)
1593 ClutterAnimationPrivate *priv;
1594 ClutterTimeline *cur_timeline;
1596 g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
1597 g_return_if_fail (timeline == NULL || CLUTTER_IS_TIMELINE (timeline));
1599 priv = animation->priv;
1601 if (priv->alpha != NULL)
1602 cur_timeline = clutter_alpha_get_timeline (priv->alpha);
1604 cur_timeline = NULL;
1606 if (cur_timeline == timeline)
1609 g_object_freeze_notify (G_OBJECT (animation));
1611 if (cur_timeline != NULL && priv->timeline_started_id != 0)
1612 g_signal_handler_disconnect (cur_timeline, priv->timeline_started_id);
1614 if (cur_timeline != NULL && priv->timeline_completed_id != 0)
1615 g_signal_handler_disconnect (cur_timeline, priv->timeline_completed_id);
1617 if (cur_timeline != NULL && priv->timeline_frame_id != 0)
1618 g_signal_handler_disconnect (cur_timeline, priv->timeline_frame_id);
1620 priv->timeline_started_id = 0;
1621 priv->timeline_completed_id = 0;
1622 priv->timeline_frame_id = 0;
1624 if (priv->alpha != NULL)
1625 clutter_alpha_set_timeline (priv->alpha, timeline);
1627 priv->timeline = timeline;
1629 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_TIMELINE]);
1630 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_DURATION]);
1631 g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_LOOP]);
1633 if (timeline != NULL)
1635 g_object_ref (timeline);
1637 priv->timeline_started_id =
1638 g_signal_connect (timeline, "started",
1639 G_CALLBACK (on_timeline_started),
1641 priv->timeline_completed_id =
1642 g_signal_connect (timeline, "completed",
1643 G_CALLBACK (on_timeline_completed),
1645 priv->timeline_frame_id =
1646 g_signal_connect (timeline, "new-frame",
1647 G_CALLBACK (on_timeline_frame),
1651 g_object_thaw_notify (G_OBJECT (animation));
1655 * clutter_animation_get_timeline:
1656 * @animation: a #ClutterAnimation
1658 * Retrieves the #ClutterTimeline used by @animation
1660 * Return value: (transfer none): the timeline used by the animation
1665 clutter_animation_get_timeline (ClutterAnimation *animation)
1667 g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
1669 return clutter_animation_get_timeline_internal (animation);
1673 * clutter_animation_set_alpha:
1674 * @animation: a #ClutterAnimation
1675 * @alpha: a #ClutterAlpha, or %NULL to unset the current #ClutterAlpha
1677 * Sets @alpha as the #ClutterAlpha used by @animation.
1679 * If @alpha is not %NULL, the #ClutterAnimation will take ownership
1680 * of the #ClutterAlpha instance.
1684 * Deprecated: 1.10: Use clutter_animation_get_timeline() and
1685 * clutter_timeline_set_progress_mode() instead.
1688 clutter_animation_set_alpha (ClutterAnimation *animation,
1689 ClutterAlpha *alpha)
1691 g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
1692 g_return_if_fail (alpha == NULL || CLUTTER_IS_ALPHA (alpha));
1694 clutter_animation_set_alpha_internal (animation, alpha);
1698 * clutter_animation_get_alpha:
1699 * @animation: a #ClutterAnimation
1701 * Retrieves the #ClutterAlpha used by @animation.
1703 * Return value: (transfer none): the alpha object used by the animation
1707 * Deprecated: 1.10: Use clutter_animation_get_timeline() and
1708 * clutter_timeline_get_progress_mode() instead.
1711 clutter_animation_get_alpha (ClutterAnimation *animation)
1713 g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
1715 return clutter_animation_get_alpha_internal (animation);
1719 * clutter_animation_completed:
1720 * @animation: a #ClutterAnimation
1722 * Emits the ::completed signal on @animation
1724 * When using this function with a #ClutterAnimation created
1725 * by the clutter_actor_animate() family of functions, @animation
1726 * will be unreferenced and it will not be valid anymore,
1727 * unless g_object_ref() was called before calling this function
1728 * or unless a reference was taken inside a handler for the
1729 * #ClutterAnimation::completed signal
1734 clutter_animation_completed (ClutterAnimation *animation)
1736 g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
1738 g_signal_emit (animation, animation_signals[COMPLETED], 0);
1742 * starts the timeline
1745 clutter_animation_start (ClutterAnimation *animation)
1747 ClutterTimeline *timeline;
1749 timeline = clutter_animation_get_timeline_internal (animation);
1751 if (G_LIKELY (timeline != NULL))
1752 clutter_timeline_start (timeline);
1756 g_warning (G_STRLOC ": no timeline found, unable to start the animation");
1761 clutter_animation_setup_property (ClutterAnimation *animation,
1762 const gchar *property_name,
1763 const GValue *value,
1767 ClutterAnimationPrivate *priv = animation->priv;
1768 GValue real_value = G_VALUE_INIT;
1770 if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
1772 g_warning ("Cannot bind property '%s': the property is "
1778 if (!(pspec->flags & G_PARAM_WRITABLE))
1780 g_warning ("Cannot bind property '%s': the property is "
1786 /* initialize the real value that will be used to store the
1787 * final state of the animation
1789 g_value_init (&real_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1791 /* if it's not the same type of the GParamSpec value, try to
1792 * convert it using the GValue transformation API, otherwise
1795 if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (&real_value)))
1797 /* are these two types compatible (can be directly copied)? */
1798 if (g_value_type_compatible (G_VALUE_TYPE (value),
1799 G_VALUE_TYPE (&real_value)))
1801 g_value_copy (value, &real_value);
1805 /* are these two type transformable? */
1806 if (g_value_type_transformable (G_VALUE_TYPE (value),
1807 G_VALUE_TYPE (&real_value)))
1809 if (g_value_transform (value, &real_value))
1813 /* if not compatible and not transformable then we can't do much */
1814 g_warning ("%s: Unable to convert from %s to %s for "
1815 "the property '%s' of object %s",
1817 g_type_name (G_VALUE_TYPE (value)),
1818 g_type_name (G_VALUE_TYPE (&real_value)),
1820 G_OBJECT_TYPE_NAME (priv->object));
1821 g_value_unset (&real_value);
1825 g_value_copy (value, &real_value);
1828 /* create an interval and bind it to the property, in case
1829 * it's not a fixed property, otherwise just set it
1831 if (G_LIKELY (!is_fixed))
1833 ClutterInterval *interval;
1834 GValue cur_value = G_VALUE_INIT;
1836 g_value_init (&cur_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1838 if (CLUTTER_IS_ANIMATABLE (priv->object))
1839 clutter_animatable_get_initial_state (CLUTTER_ANIMATABLE (priv->object),
1843 g_object_get_property (priv->object, property_name, &cur_value);
1846 clutter_interval_new_with_values (G_PARAM_SPEC_VALUE_TYPE (pspec),
1850 if (!clutter_animation_has_property (animation, property_name))
1851 clutter_animation_bind_property_internal (animation, property_name,
1855 clutter_animation_update_property_internal (animation, property_name,
1859 g_value_unset (&cur_value);
1863 if (CLUTTER_IS_ANIMATABLE (priv->object))
1864 clutter_animatable_set_final_state (CLUTTER_ANIMATABLE (priv->object),
1868 g_object_set_property (priv->object, property_name, &real_value);
1871 g_value_unset (&real_value);
1875 clutter_animation_setupv (ClutterAnimation *animation,
1877 const gchar * const properties[],
1878 const GValue *values)
1880 ClutterAnimationPrivate *priv = animation->priv;
1881 ClutterAnimatable *animatable = NULL;
1882 GObjectClass *klass = NULL;
1885 if (CLUTTER_IS_ANIMATABLE (priv->object))
1886 animatable = CLUTTER_ANIMATABLE (priv->object);
1888 klass = G_OBJECT_GET_CLASS (priv->object);
1890 for (i = 0; i < n_properties; i++)
1892 const gchar *property_name = properties[i];
1894 gboolean is_fixed = FALSE;
1896 if (g_str_has_prefix (property_name, "fixed::"))
1898 property_name += 7; /* strlen("fixed::") */
1902 if (animatable != NULL)
1903 pspec = clutter_animatable_find_property (animatable, property_name);
1905 pspec = g_object_class_find_property (klass, property_name);
1909 g_warning ("Cannot bind property '%s': objects of type '%s' do "
1910 "not have this property",
1912 g_type_name (G_OBJECT_TYPE (priv->object)));
1916 clutter_animation_setup_property (animation, property_name,
1926 GConnectFlags flags;
1927 } signal_prefixes[] =
1930 { "-swapped::", G_CONNECT_SWAPPED },
1931 { "-after::", G_CONNECT_AFTER },
1932 { "-swapped-after::", G_CONNECT_SWAPPED | G_CONNECT_AFTER }
1936 clutter_animation_has_signal_prefix (const gchar *property_name,
1937 GConnectFlags *flags,
1942 if (!g_str_has_prefix (property_name, "signal"))
1945 for (i = 0; i < G_N_ELEMENTS (signal_prefixes); i++)
1946 if (g_str_has_prefix (property_name + 6, signal_prefixes[i].name))
1948 *offset = strlen (signal_prefixes[i].name) + 6;
1949 *flags = signal_prefixes[i].flags;
1957 clutter_animation_setup_valist (ClutterAnimation *animation,
1958 const gchar *first_property_name,
1961 ClutterAnimationPrivate *priv = animation->priv;
1962 ClutterAnimatable *animatable = NULL;
1963 GObjectClass *klass = NULL;
1964 const gchar *property_name;
1966 if (CLUTTER_IS_ANIMATABLE (priv->object))
1967 animatable = CLUTTER_ANIMATABLE (priv->object);
1969 klass = G_OBJECT_GET_CLASS (priv->object);
1971 property_name = first_property_name;
1972 while (property_name != NULL)
1975 GValue final = G_VALUE_INIT;
1976 gchar *error = NULL;
1977 gboolean is_fixed = FALSE;
1978 GConnectFlags flags;
1981 if (clutter_animation_has_signal_prefix (property_name,
1985 const gchar *signal_name = property_name + offset;
1986 GCallback callback = va_arg (var_args, GCallback);
1987 gpointer userdata = va_arg (var_args, gpointer);
1989 g_signal_connect_data (animation, signal_name,
1995 if (g_str_has_prefix (property_name, "fixed::"))
1997 property_name += 7; /* strlen("fixed::") */
2001 if (animatable != NULL)
2002 pspec = clutter_animatable_find_property (animatable,
2005 pspec = g_object_class_find_property (klass, property_name);
2009 g_warning ("Cannot bind property '%s': objects of type '%s' do "
2010 "not have this property",
2012 g_type_name (G_OBJECT_TYPE (priv->object)));
2016 G_VALUE_COLLECT_INIT (&final, G_PARAM_SPEC_VALUE_TYPE (pspec),
2022 g_warning ("%s: %s", G_STRLOC, error);
2027 clutter_animation_setup_property (animation, property_name,
2033 g_value_unset (&final);
2036 property_name = va_arg (var_args, gchar*);
2040 static ClutterAnimation *
2041 animation_create_for_actor (ClutterActor *actor)
2043 ClutterAnimation *animation;
2044 GObject *object = G_OBJECT (actor);
2046 animation = g_object_get_qdata (object, quark_object_animation);
2047 if (animation == NULL)
2049 animation = clutter_animation_new ();
2050 clutter_animation_set_object (animation, object);
2051 g_object_set_qdata (object, quark_object_animation, animation);
2053 /* use the ::destroy signal to get a notification
2054 * that the actor went away mid-animation
2056 g_signal_connect (object, "destroy",
2057 G_CALLBACK (on_actor_destroy),
2060 CLUTTER_NOTE (ANIMATION,
2061 "Created new Animation [%p] for actor [%p]",
2067 CLUTTER_NOTE (ANIMATION,
2068 "Reusing Animation [%p] for actor [%p]",
2077 * clutter_actor_animate_with_alpha:
2078 * @actor: a #ClutterActor
2079 * @alpha: a #ClutterAlpha
2080 * @first_property_name: the name of a property
2081 * @...: a %NULL terminated list of property names and
2084 * Animates the given list of properties of @actor between the current
2085 * value for each property and a new final value. The animation has a
2086 * definite behaviour given by the passed @alpha.
2088 * See clutter_actor_animate() for further details.
2090 * This function is useful if you want to use an existing #ClutterAlpha
2091 * to animate @actor.
2093 * Return value: (transfer none): a #ClutterAnimation object. The object is owned by the
2094 * #ClutterActor and should not be unreferenced with g_object_unref()
2098 * Deprecated: 1.10: Use clutter_actor_animate_with_timeline() instead
2101 clutter_actor_animate_with_alpha (ClutterActor *actor,
2102 ClutterAlpha *alpha,
2103 const gchar *first_property_name,
2106 ClutterAnimation *animation;
2107 ClutterTimeline *timeline;
2110 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
2111 g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), NULL);
2112 g_return_val_if_fail (first_property_name != NULL, NULL);
2114 timeline = clutter_alpha_get_timeline (alpha);
2115 if (timeline == NULL)
2117 g_warning ("The passed ClutterAlpha does not have an "
2118 "associated ClutterTimeline.");
2122 animation = animation_create_for_actor (actor);
2123 clutter_animation_set_alpha_internal (animation, alpha);
2125 va_start (args, first_property_name);
2126 clutter_animation_setup_valist (animation, first_property_name, args);
2129 clutter_animation_start (animation);
2135 * clutter_actor_animate_with_timeline:
2136 * @actor: a #ClutterActor
2137 * @mode: an animation mode logical id
2138 * @timeline: a #ClutterTimeline
2139 * @first_property_name: the name of a property
2140 * @...: a %NULL terminated list of property names and
2143 * Animates the given list of properties of @actor between the current
2144 * value for each property and a new final value. The animation has a
2145 * definite duration given by @timeline and a speed given by the @mode.
2147 * See clutter_actor_animate() for further details.
2149 * This function is useful if you want to use an existing timeline
2150 * to animate @actor.
2152 * Return value: (transfer none): a #ClutterAnimation object. The object is
2153 * owned by the #ClutterActor and should not be unreferenced with
2159 clutter_actor_animate_with_timeline (ClutterActor *actor,
2161 ClutterTimeline *timeline,
2162 const gchar *first_property_name,
2165 ClutterAnimation *animation;
2168 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
2169 g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
2170 g_return_val_if_fail (first_property_name != NULL, NULL);
2172 animation = animation_create_for_actor (actor);
2173 clutter_animation_set_mode (animation, mode);
2174 clutter_animation_set_timeline (animation, timeline);
2176 va_start (args, first_property_name);
2177 clutter_animation_setup_valist (animation, first_property_name, args);
2180 clutter_animation_start (animation);
2186 * clutter_actor_animate:
2187 * @actor: a #ClutterActor
2188 * @mode: an animation mode logical id
2189 * @duration: duration of the animation, in milliseconds
2190 * @first_property_name: the name of a property
2191 * @...: a %NULL terminated list of property names and
2194 * Animates the given list of properties of @actor between the current
2195 * value for each property and a new final value. The animation has a
2196 * definite duration and a speed given by the @mode.
2198 * For example, this:
2201 * clutter_actor_animate (rectangle, CLUTTER_LINEAR, 250,
2207 * will make width and height properties of the #ClutterActor "rectangle"
2208 * grow linearly between the current value and 100 pixels, in 250 milliseconds.
2210 * The animation @mode is a logical id, either from the #ClutterAnimationMode
2211 * enumeration of from clutter_alpha_register_func().
2213 * All the properties specified will be animated between the current value
2214 * and the final value. If a property should be set at the beginning of
2215 * the animation but not updated during the animation, it should be prefixed
2216 * by the "fixed::" string, for instance:
2219 * clutter_actor_animate (actor, CLUTTER_EASE_IN_SINE, 100,
2220 * "rotation-angle-z", 360.0,
2221 * "fixed::rotation-center-z", &center,
2225 * Will animate the "rotation-angle-z" property between the current value
2226 * and 360 degrees, and set the "rotation-center-z" property to the fixed
2227 * value of the #ClutterVertex "center".
2229 * This function will implicitly create a #ClutterAnimation object which
2230 * will be assigned to the @actor and will be returned to the developer
2231 * to control the animation or to know when the animation has been
2234 * If a name argument starts with "signal::", "signal-after::",
2235 * "signal-swapped::" or "signal-swapped-after::" the two following arguments
2236 * are used as callback function and data for a signal handler installed on
2237 * the #ClutterAnimation object for the specified signal name, for instance:
2242 * on_animation_completed (ClutterAnimation *animation,
2243 * ClutterActor *actor)
2245 * clutter_actor_hide (actor);
2248 * clutter_actor_animate (actor, CLUTTER_EASE_IN_CUBIC, 100,
2250 * "signal::completed", on_animation_completed, actor,
2254 * or, to automatically destroy an actor at the end of the animation:
2257 * clutter_actor_animate (actor, CLUTTER_EASE_IN_CUBIC, 100,
2259 * "signal-swapped-after::completed",
2260 * clutter_actor_destroy,
2265 * The "signal::" modifier is the equivalent of using g_signal_connect();
2266 * the "signal-after::" modifier is the equivalent of using
2267 * g_signal_connect_after() or g_signal_connect_data() with the
2268 * %G_CONNECT_AFTER; the "signal-swapped::" modifier is the equivalent
2269 * of using g_signal_connect_swapped() or g_signal_connect_data() with the
2270 * %G_CONNECT_SWAPPED flah; finally, the "signal-swapped-after::" modifier
2271 * is the equivalent of using g_signal_connect_data() with both the
2272 * %G_CONNECT_AFTER and %G_CONNECT_SWAPPED flags. The clutter_actor_animate()
2273 * function will not keep track of multiple connections to the same signal,
2274 * so it is your responsability to avoid them when calling
2275 * clutter_actor_animate() multiple times on the same actor.
2277 * Calling this function on an actor that is already being animated
2278 * will cause the current animation to change with the new final values,
2279 * the new easing mode and the new duration - that is, this code:
2282 * clutter_actor_animate (actor, CLUTTER_LINEAR, 250,
2286 * clutter_actor_animate (actor, CLUTTER_EASE_IN_CUBIC, 500,
2293 * is the equivalent of:
2296 * clutter_actor_animate (actor, CLUTTER_EASE_IN_CUBIC, 500,
2304 * <note>Unless the animation is looping, the #ClutterAnimation created by
2305 * clutter_actor_animate() will become invalid as soon as it is
2308 * Since the created #ClutterAnimation instance attached to @actor
2309 * is guaranteed to be valid throughout the #ClutterAnimation::completed
2310 * signal emission chain, you will not be able to create a new animation
2311 * using clutter_actor_animate() on the same @actor from within the
2312 * #ClutterAnimation::completed signal handler unless you use
2313 * g_signal_connect_after() to connect the callback function, for instance:
2317 * on_animation_completed (ClutterAnimation *animation,
2318 * ClutterActor *actor)
2320 * clutter_actor_animate (actor, CLUTTER_EASE_OUT_CUBIC, 250,
2327 * animation = clutter_actor_animate (actor, CLUTTER_EASE_IN_CUBIC, 250,
2331 * g_signal_connect (animation, "completed",
2332 * G_CALLBACK (on_animation_completed),
2337 * Return value: (transfer none): a #ClutterAnimation object. The object is
2338 * owned by the #ClutterActor and should not be unreferenced with
2344 clutter_actor_animate (ClutterActor *actor,
2347 const gchar *first_property_name,
2350 ClutterAnimation *animation;
2353 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
2354 g_return_val_if_fail (mode != CLUTTER_CUSTOM_MODE, NULL);
2355 g_return_val_if_fail (duration > 0, NULL);
2356 g_return_val_if_fail (first_property_name != NULL, NULL);
2358 animation = animation_create_for_actor (actor);
2359 clutter_animation_set_mode (animation, mode);
2360 clutter_animation_set_duration (animation, duration);
2362 va_start (args, first_property_name);
2363 clutter_animation_setup_valist (animation, first_property_name, args);
2366 clutter_animation_start (animation);
2372 * clutter_actor_animatev:
2373 * @actor: a #ClutterActor
2374 * @mode: an animation mode logical id
2375 * @duration: duration of the animation, in milliseconds
2376 * @n_properties: number of property names and values
2377 * @properties: (array length=n_properties) (element-type utf8): a vector
2378 * containing the property names to set
2379 * @values: (array length=n_properties): a vector containing the
2380 * property values to set
2382 * Animates the given list of properties of @actor between the current
2383 * value for each property and a new final value. The animation has a
2384 * definite duration and a speed given by the @mode.
2386 * This is the vector-based variant of clutter_actor_animate(), useful
2387 * for language bindings.
2389 * <warning>Unlike clutter_actor_animate(), this function will not
2390 * allow you to specify "signal::" names and callbacks.</warning>
2392 * Return value: (transfer none): a #ClutterAnimation object. The object is
2393 * owned by the #ClutterActor and should not be unreferenced with
2399 clutter_actor_animatev (ClutterActor *actor,
2403 const gchar * const properties[],
2404 const GValue *values)
2406 ClutterAnimation *animation;
2408 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
2409 g_return_val_if_fail (mode != CLUTTER_CUSTOM_MODE, NULL);
2410 g_return_val_if_fail (duration > 0, NULL);
2411 g_return_val_if_fail (properties != NULL, NULL);
2412 g_return_val_if_fail (values != NULL, NULL);
2414 animation = animation_create_for_actor (actor);
2415 clutter_animation_set_mode (animation, mode);
2416 clutter_animation_set_duration (animation, duration);
2417 clutter_animation_setupv (animation, n_properties, properties, values);
2418 clutter_animation_start (animation);
2424 * clutter_actor_animate_with_timelinev:
2425 * @actor: a #ClutterActor
2426 * @mode: an animation mode logical id
2427 * @timeline: a #ClutterTimeline
2428 * @n_properties: number of property names and values
2429 * @properties: (array length=n_properties) (element-type utf8): a vector
2430 * containing the property names to set
2431 * @values: (array length=n_properties): a vector containing the
2432 * property values to set
2434 * Animates the given list of properties of @actor between the current
2435 * value for each property and a new final value. The animation has a
2436 * definite duration given by @timeline and a speed given by the @mode.
2438 * See clutter_actor_animate() for further details.
2440 * This function is useful if you want to use an existing timeline
2441 * to animate @actor.
2443 * This is the vector-based variant of clutter_actor_animate_with_timeline(),
2444 * useful for language bindings.
2446 * <warning>Unlike clutter_actor_animate_with_timeline(), this function
2447 * will not allow you to specify "signal::" names and callbacks.</warning>
2449 * Return value: (transfer none): a #ClutterAnimation object. The object is
2450 * owned by the #ClutterActor and should not be unreferenced with
2456 clutter_actor_animate_with_timelinev (ClutterActor *actor,
2458 ClutterTimeline *timeline,
2460 const gchar * const properties[],
2461 const GValue *values)
2463 ClutterAnimation *animation;
2465 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
2466 g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
2467 g_return_val_if_fail (properties != NULL, NULL);
2468 g_return_val_if_fail (values != NULL, NULL);
2470 animation = animation_create_for_actor (actor);
2471 clutter_animation_set_mode (animation, mode);
2472 clutter_animation_set_timeline (animation, timeline);
2473 clutter_animation_setupv (animation, n_properties, properties, values);
2474 clutter_animation_start (animation);
2480 * clutter_actor_animate_with_alphav:
2481 * @actor: a #ClutterActor
2482 * @alpha: a #ClutterAlpha
2483 * @n_properties: number of property names and values
2484 * @properties: (array length=n_properties) (element-type utf8): a vector
2485 * containing the property names to set
2486 * @values: (array length=n_properties): a vector containing the
2487 * property values to set
2489 * Animates the given list of properties of @actor between the current
2490 * value for each property and a new final value. The animation has a
2491 * definite behaviour given by the passed @alpha.
2493 * See clutter_actor_animate() for further details.
2495 * This function is useful if you want to use an existing #ClutterAlpha
2496 * to animate @actor.
2498 * This is the vector-based variant of clutter_actor_animate_with_alpha(),
2499 * useful for language bindings.
2501 * <warning>Unlike clutter_actor_animate_with_alpha(), this function will
2502 * not allow you to specify "signal::" names and callbacks.</warning>
2504 * Return value: (transfer none): a #ClutterAnimation object. The object is owned by the
2505 * #ClutterActor and should not be unreferenced with g_object_unref()
2509 * Deprecated: 1.10: Use clutter_actor_animate_with_timelinev() instead
2512 clutter_actor_animate_with_alphav (ClutterActor *actor,
2513 ClutterAlpha *alpha,
2515 const gchar * const properties[],
2516 const GValue *values)
2518 ClutterAnimation *animation;
2519 ClutterTimeline *timeline;
2521 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
2522 g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), NULL);
2523 g_return_val_if_fail (properties != NULL, NULL);
2524 g_return_val_if_fail (values != NULL, NULL);
2526 timeline = clutter_alpha_get_timeline (alpha);
2527 if (timeline == NULL)
2529 g_warning ("The passed ClutterAlpha does not have an "
2530 "associated ClutterTimeline.");
2534 animation = animation_create_for_actor (actor);
2535 clutter_animation_set_alpha_internal (animation, alpha);
2536 clutter_animation_setupv (animation, n_properties, properties, values);
2537 clutter_animation_start (animation);
2543 * clutter_actor_get_animation:
2544 * @actor: a #ClutterActor
2546 * Retrieves the #ClutterAnimation used by @actor, if clutter_actor_animate()
2547 * has been called on @actor.
2549 * Return value: (transfer none): a #ClutterAnimation, or %NULL
2554 clutter_actor_get_animation (ClutterActor *actor)
2556 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
2558 return g_object_get_qdata (G_OBJECT (actor), quark_object_animation);
2562 * clutter_actor_detach_animation:
2563 * @actor: a #ClutterActor
2565 * Detaches the #ClutterAnimation used by @actor, if clutter_actor_animate()
2566 * has been called on @actor.
2568 * Once the animation has been detached, it loses a reference. If it was
2569 * the only reference then the #ClutterAnimation becomes invalid.
2571 * The #ClutterAnimation::completed signal will not be emitted.
2576 clutter_actor_detach_animation (ClutterActor *actor)
2578 ClutterAnimation *animation;
2579 ClutterAnimationPrivate *priv;
2581 g_return_if_fail (CLUTTER_IS_ACTOR (actor));
2583 animation = g_object_get_qdata (G_OBJECT (actor), quark_object_animation);
2584 if (animation == NULL)
2587 priv = animation->priv;
2589 g_assert (priv->object == G_OBJECT (actor));
2591 /* we can't call get_timeline_internal() here because it would be
2592 * pointless to create a timeline on an animation we want to detach
2594 if (priv->alpha != NULL)
2596 ClutterTimeline *timeline;
2598 timeline = clutter_alpha_get_timeline (priv->alpha);
2599 if (timeline != NULL)
2600 clutter_timeline_stop (timeline);
2603 /* disconnect the ::destroy handler added by animation_create_for_actor() */
2604 g_signal_handlers_disconnect_by_func (actor,
2605 G_CALLBACK (on_actor_destroy),
2608 clutter_animation_set_object (animation, NULL);
2610 /* drop the reference on the animation */
2611 g_object_unref (animation);