Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-transition.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2012 Intel Corporation
7  *
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.
12  *
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.
17  *
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/>.
20  *
21  * Author: Emmanuele Bassi <ebassi@linux.intel.com>
22  */
23
24 /**
25  * SECTION:clutter-transition
26  * @Title: ClutterTransition
27  * @Short_Description: Transition between two values
28  *
29  * #ClutterTransition is a subclass of #ClutterTimeline that computes
30  * the interpolation between two values, stored by a #ClutterInterval.
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include "clutter-transition.h"
38
39 #include "clutter-animatable.h"
40 #include "clutter-debug.h"
41 #include "clutter-interval.h"
42 #include "clutter-private.h"
43 #include "clutter-timeline.h"
44
45 #include <gobject/gvaluecollector.h>
46
47 struct _ClutterTransitionPrivate
48 {
49   ClutterInterval *interval;
50   ClutterAnimatable *animatable;
51
52   guint remove_on_complete : 1;
53 };
54
55 enum
56 {
57   PROP_0,
58
59   PROP_INTERVAL,
60   PROP_ANIMATABLE,
61   PROP_REMOVE_ON_COMPLETE,
62
63   PROP_LAST
64 };
65
66 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
67
68 static GQuark quark_animatable_set = 0;
69
70 G_DEFINE_ABSTRACT_TYPE (ClutterTransition, clutter_transition, CLUTTER_TYPE_TIMELINE)
71
72 static void
73 clutter_transition_attach (ClutterTransition *transition,
74                            ClutterAnimatable *animatable)
75 {
76   CLUTTER_TRANSITION_GET_CLASS (transition)->attached (transition, animatable);
77 }
78
79 static void
80 clutter_transition_detach (ClutterTransition *transition,
81                            ClutterAnimatable *animatable)
82 {
83   CLUTTER_TRANSITION_GET_CLASS (transition)->detached (transition, animatable);
84 }
85
86 static void
87 clutter_transition_real_compute_value (ClutterTransition *transition,
88                                        ClutterAnimatable *animatable,
89                                        ClutterInterval   *interval,
90                                        gdouble            progress)
91 {
92 }
93
94 static void
95 clutter_transition_real_attached (ClutterTransition *transition,
96                                   ClutterAnimatable *animatable)
97 {
98 }
99
100 static void
101 clutter_transition_real_detached (ClutterTransition *transition,
102                                   ClutterAnimatable *animatable)
103 {
104 }
105
106 static void
107 clutter_transition_new_frame (ClutterTimeline *timeline,
108                               gint             elapsed G_GNUC_UNUSED)
109 {
110   ClutterTransition *transition = CLUTTER_TRANSITION (timeline);
111   ClutterTransitionPrivate *priv = transition->priv;
112   gdouble progress;
113
114   if (priv->interval == NULL ||
115       priv->animatable == NULL)
116     return;
117
118   progress = clutter_timeline_get_progress (timeline);
119
120   CLUTTER_TRANSITION_GET_CLASS (timeline)->compute_value (transition,
121                                                           priv->animatable,
122                                                           priv->interval,
123                                                           progress);
124 }
125
126 static void
127 clutter_transition_stopped (ClutterTimeline *timeline,
128                             gboolean         is_finished)
129 {
130   ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (timeline)->priv;
131
132   if (is_finished &&
133       priv->animatable != NULL &&
134       priv->remove_on_complete)
135     {
136       clutter_transition_detach (CLUTTER_TRANSITION (timeline),
137                                  priv->animatable);
138       g_clear_object (&priv->animatable);
139       g_object_unref (timeline);
140     }
141 }
142
143 static void
144 clutter_transition_set_property (GObject      *gobject,
145                                  guint         prop_id,
146                                  const GValue *value,
147                                  GParamSpec   *pspec)
148 {
149   ClutterTransition *transition = CLUTTER_TRANSITION (gobject);
150
151   switch (prop_id)
152     {
153     case PROP_INTERVAL:
154       clutter_transition_set_interval (transition, g_value_get_object (value));
155       break;
156
157     case PROP_ANIMATABLE:
158       clutter_transition_set_animatable (transition, g_value_get_object (value));
159       break;
160
161     case PROP_REMOVE_ON_COMPLETE:
162       clutter_transition_set_remove_on_complete (transition, g_value_get_boolean (value));
163       break;
164
165     default:
166       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
167       break;
168     }
169 }
170
171 static void
172 clutter_transition_get_property (GObject    *gobject,
173                                  guint       prop_id,
174                                  GValue     *value,
175                                  GParamSpec *pspec)
176 {
177   ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (gobject)->priv;
178
179   switch (prop_id)
180     {
181     case PROP_INTERVAL:
182       g_value_set_object (value, priv->interval);
183       break;
184
185     case PROP_ANIMATABLE:
186       g_value_set_object (value, priv->animatable);
187       break;
188
189     case PROP_REMOVE_ON_COMPLETE:
190       g_value_set_boolean (value, priv->remove_on_complete);
191       break;
192
193     default:
194       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
195       break;
196     }
197 }
198
199 static void
200 clutter_transition_dispose (GObject *gobject)
201 {
202   ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (gobject)->priv;
203
204   if (priv->animatable != NULL)
205     clutter_transition_detach (CLUTTER_TRANSITION (gobject),
206                                priv->animatable);
207
208   g_clear_object (&priv->interval);
209   g_clear_object (&priv->animatable);
210
211   G_OBJECT_CLASS (clutter_transition_parent_class)->dispose (gobject);
212 }
213
214 static void
215 clutter_transition_class_init (ClutterTransitionClass *klass)
216 {
217   ClutterTimelineClass *timeline_class = CLUTTER_TIMELINE_CLASS (klass);
218   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
219
220   quark_animatable_set =
221     g_quark_from_static_string ("-clutter-transition-animatable-set");
222
223   g_type_class_add_private (klass, sizeof (ClutterTransitionPrivate));
224
225   klass->compute_value = clutter_transition_real_compute_value;
226   klass->attached = clutter_transition_real_attached;
227   klass->detached = clutter_transition_real_detached;
228
229   timeline_class->new_frame = clutter_transition_new_frame;
230   timeline_class->stopped = clutter_transition_stopped;
231
232   gobject_class->set_property = clutter_transition_set_property;
233   gobject_class->get_property = clutter_transition_get_property;
234   gobject_class->dispose = clutter_transition_dispose;
235
236   /**
237    * ClutterTransition:interval:
238    *
239    * The #ClutterInterval used to describe the initial and final states
240    * of the transition.
241    *
242    * Since: 1.10
243    */
244   obj_props[PROP_INTERVAL] =
245     g_param_spec_object ("interval",
246                          P_("Interval"),
247                          P_("The interval of values to transition"),
248                          CLUTTER_TYPE_INTERVAL,
249                          G_PARAM_READWRITE |
250                          G_PARAM_STATIC_STRINGS);
251
252   /**
253    * ClutterTransition:animatable:
254    *
255    * The #ClutterAnimatable instance currently being animated.
256    *
257    * Since: 1.10
258    */
259   obj_props[PROP_ANIMATABLE] =
260     g_param_spec_object ("animatable",
261                          P_("Animatable"),
262                          P_("The animatable object"),
263                          CLUTTER_TYPE_ANIMATABLE,
264                          G_PARAM_READWRITE |
265                          G_PARAM_STATIC_STRINGS);
266
267   /**
268    * ClutterTransition:remove-on-complete:
269    *
270    * Whether the #ClutterTransition should be automatically detached
271    * from the #ClutterTransition:animatable instance whenever the
272    * #ClutterTimeline::completed signal is emitted.
273    *
274    * The #ClutterTransition:remove-on-complete property takes into
275    * account the value of the #ClutterTimeline:repeat-count property,
276    * and it only detaches the transition if the transition is not
277    * repeating.
278    *
279    * Since: 1.10
280    */
281   obj_props[PROP_REMOVE_ON_COMPLETE] =
282     g_param_spec_boolean ("remove-on-complete",
283                           P_("Remove on Complete"),
284                           P_("Detach the transition when completed"),
285                           FALSE,
286                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
287
288   g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
289 }
290
291 static void
292 clutter_transition_init (ClutterTransition *self)
293 {
294   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_TRANSITION,
295                                             ClutterTransitionPrivate);
296 }
297
298 /**
299  * clutter_transition_set_interval:
300  * @transition: a #ClutterTransition
301  * @interval: (allow-none): a #ClutterInterval, or %NULL
302  *
303  * Sets the #ClutterTransition:interval property using @interval.
304  *
305  * The @transition will acquire a reference on the @interval, sinking
306  * the floating flag on it if necessary.
307  *
308  * Since: 1.10
309  */
310 void
311 clutter_transition_set_interval (ClutterTransition *transition,
312                                  ClutterInterval   *interval)
313 {
314   ClutterTransitionPrivate *priv;
315
316   g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
317   g_return_if_fail (interval == NULL || CLUTTER_IS_INTERVAL (interval));
318
319   priv = transition->priv;
320
321   g_clear_object (&priv->interval);
322
323   if (interval != NULL)
324     priv->interval = g_object_ref_sink (interval);
325
326   g_object_notify_by_pspec (G_OBJECT (transition), obj_props[PROP_INTERVAL]);
327 }
328
329 /**
330  * clutter_transition_get_interval:
331  * @transition: a #ClutterTransition
332  *
333  * Retrieves the interval set using clutter_transition_set_interval()
334  *
335  * Return value: (transfer none): a #ClutterInterval, or %NULL; the returned
336  *   interval is owned by the #ClutterTransition and it should not be freed
337  *   directly
338  *
339  * Since: 1.10
340  */
341 ClutterInterval *
342 clutter_transition_get_interval (ClutterTransition *transition)
343 {
344   g_return_val_if_fail (CLUTTER_IS_TRANSITION (transition), NULL);
345
346   return transition->priv->interval;
347 }
348
349 /**
350  * clutter_transition_set_animatable:
351  * @transition: a #ClutterTransition
352  * @animatable: (allow-none): a #ClutterAnimatable, or %NULL
353  *
354  * Sets the #ClutterTransition:animatable property.
355  *
356  * The @transition will acquire a reference to the @animatable instance,
357  * and will call the #ClutterTransitionClass.attached() virtual function.
358  *
359  * If an existing #ClutterAnimatable is attached to @transition, the
360  * reference will be released, and the #ClutterTransitionClass.detached()
361  * virtual function will be called.
362  *
363  * Since: 1.10
364  */
365 void
366 clutter_transition_set_animatable (ClutterTransition *transition,
367                                    ClutterAnimatable *animatable)
368 {
369   ClutterTransitionPrivate *priv;
370
371   g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
372   g_return_if_fail (animatable == NULL || CLUTTER_IS_ANIMATABLE (animatable));
373
374   priv = transition->priv;
375
376   if (priv->animatable == animatable)
377     return;
378
379   if (priv->animatable != NULL)
380     clutter_transition_detach (transition, priv->animatable);
381
382   g_clear_object (&priv->animatable);
383
384   if (animatable != NULL)
385     {
386       priv->animatable = g_object_ref (animatable);
387       clutter_transition_attach (transition, priv->animatable);
388     }
389 }
390
391 /**
392  * clutter_transition_get_animatable:
393  * @transition: a #ClutterTransition
394  *
395  * Retrieves the #ClutterAnimatable set using clutter_transition_set_animatable().
396  *
397  * Return value: (transfer none): a #ClutterAnimatable, or %NULL; the returned
398  *   animatable is owned by the #ClutterTransition, and it should not be freed
399  *   directly.
400  *
401  * Since: 1.10
402  */
403 ClutterAnimatable *
404 clutter_transition_get_animatable (ClutterTransition *transition)
405 {
406   g_return_val_if_fail (CLUTTER_IS_TRANSITION (transition), NULL);
407
408   return transition->priv->animatable;
409 }
410
411 /**
412  * clutter_transition_set_remove_on_complete:
413  * @transition: a #ClutterTransition
414  * @remove_complete: whether to detach @transition when complete
415  *
416  * Sets whether @transition should be detached from the #ClutterAnimatable
417  * set using clutter_transition_set_animatable() when the
418  * #ClutterTimeline::completed signal is emitted.
419  *
420  * Since: 1.10
421  */
422 void
423 clutter_transition_set_remove_on_complete (ClutterTransition *transition,
424                                            gboolean           remove_complete)
425 {
426   g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
427
428   remove_complete = !!remove_complete;
429
430   if (transition->priv->remove_on_complete == remove_complete)
431     return;
432
433   transition->priv->remove_on_complete = remove_complete;
434
435   g_object_notify_by_pspec (G_OBJECT (transition),
436                             obj_props[PROP_REMOVE_ON_COMPLETE]);
437 }
438
439 /**
440  * clutter_transition_get_remove_on_complete:
441  * @transition: a #ClutterTransition
442  *
443  * Retrieves the value of the #ClutterTransition:remove-on-complete property.
444  *
445  * Return value: %TRUE if the @transition should be detached when complete,
446  *   and %FALSE otherwise
447  *
448  * Since: 1.10
449  */
450 gboolean
451 clutter_transition_get_remove_on_complete (ClutterTransition *transition)
452 {
453   g_return_val_if_fail (CLUTTER_IS_TRANSITION (transition), FALSE);
454
455   return transition->priv->remove_on_complete;
456 }
457
458 typedef void (* IntervalSetFunc) (ClutterInterval *interval,
459                                   const GValue    *value);
460
461 static inline void
462 clutter_transition_set_value (ClutterTransition *transition,
463                               IntervalSetFunc    interval_set_func,
464                               const GValue      *value)
465 {
466   ClutterTransitionPrivate *priv = transition->priv;
467   GType interval_type;
468
469   if (priv->interval == NULL)
470     {
471       priv->interval = clutter_interval_new_with_values (G_VALUE_TYPE (value),
472                                                          value,
473                                                          value);
474       g_object_ref_sink (priv->interval);
475       return;
476     }
477
478   interval_type = clutter_interval_get_value_type (priv->interval);
479
480   if (!g_type_is_a (G_VALUE_TYPE (value), interval_type))
481     {
482       if (g_value_type_compatible (G_VALUE_TYPE (value), interval_type))
483         {
484           interval_set_func (priv->interval, value);
485           return;
486         }
487
488       if (g_value_type_transformable (G_VALUE_TYPE (value), interval_type))
489         {
490           GValue transform = G_VALUE_INIT;
491
492           g_value_init (&transform, interval_type);
493           if (g_value_transform (value, &transform))
494             interval_set_func (priv->interval, &transform);
495           else
496             {
497               g_warning ("%s: Unable to convert a value of type '%s' into "
498                          "the value type '%s' of the interval used by the "
499                          "transition.",
500                          G_STRLOC,
501                          g_type_name (G_VALUE_TYPE (value)),
502                          g_type_name (interval_type));
503             }
504
505           g_value_unset (&transform);
506         }
507     }
508   else
509     interval_set_func (priv->interval, value);
510 }
511
512 /**
513  * clutter_transition_set_from_value:
514  * @transition: a #ClutterTransition
515  * @value: a #GValue with the initial value of the transition
516  *
517  * Sets the initial value of the transition.
518  *
519  * This is a convenience function that will either create the
520  * #ClutterInterval used by @transition, or will update it if
521  * the #ClutterTransition:interval is already set.
522  *
523  * This function will copy the contents of @value, so it is
524  * safe to call g_value_unset() after it returns.
525  *
526  * If @transition already has a #ClutterTransition:interval set,
527  * then @value must hold the same type, or a transformable type,
528  * as the interval's #ClutterInterval:value-type property.
529  *
530  * This function is meant to be used by language bindings.
531  *
532  * Rename to: clutter_transition_set_from
533  *
534  * Since: 1.12
535  */
536 void
537 clutter_transition_set_from_value (ClutterTransition *transition,
538                                    const GValue      *value)
539 {
540   g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
541   g_return_if_fail (G_IS_VALUE (value));
542
543   clutter_transition_set_value (transition,
544                                 clutter_interval_set_initial_value,
545                                 value);
546 }
547
548 /**
549  * clutter_transition_set_to_value:
550  * @transition: a #ClutterTransition
551  * @value: a #GValue with the final value of the transition
552  *
553  * Sets the final value of the transition.
554  *
555  * This is a convenience function that will either create the
556  * #ClutterInterval used by @transition, or will update it if
557  * the #ClutterTransition:interval is already set.
558  *
559  * This function will copy the contents of @value, so it is
560  * safe to call g_value_unset() after it returns.
561  *
562  * If @transition already has a #ClutterTransition:interval set,
563  * then @value must hold the same type, or a transformable type,
564  * as the interval's #ClutterInterval:value-type property.
565  *
566  * This function is meant to be used by language bindings.
567  *
568  * Rename to: clutter_transition_set_to
569  *
570  * Since: 1.12
571  */
572 void
573 clutter_transition_set_to_value (ClutterTransition *transition,
574                                  const GValue      *value)
575 {
576   g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
577   g_return_if_fail (G_IS_VALUE (value));
578
579   clutter_transition_set_value (transition,
580                                 clutter_interval_set_final_value,
581                                 value);
582 }
583
584 /**
585  * clutter_transition_set_from: (skip)
586  * @transition: a #ClutterTransition
587  * @value_type: the type of the value to set
588  * @...: the initial value
589  *
590  * Sets the initial value of the transition.
591  *
592  * This is a convenience function that will either create the
593  * #ClutterInterval used by @transition, or will update it if
594  * the #ClutterTransition:interval is already set.
595  *
596  * If @transition already has a #ClutterTransition:interval set,
597  * then @value must hold the same type, or a transformable type,
598  * as the interval's #ClutterInterval:value-type property.
599  *
600  * This is a convenience function for the C API; language bindings
601  * should use clutter_transition_set_from_value() instead.
602  *
603  * Since: 1.12
604  */
605 void
606 clutter_transition_set_from (ClutterTransition *transition,
607                              GType              value_type,
608                              ...)
609 {
610   GValue value = G_VALUE_INIT;
611   gchar *error = NULL;
612   va_list args;
613
614   g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
615   g_return_if_fail (value_type != G_TYPE_INVALID);
616
617   va_start (args, value_type);
618
619   G_VALUE_COLLECT_INIT (&value, value_type, args, 0, &error);
620
621   va_end (args);
622
623   if (error != NULL)
624     {
625       g_warning ("%s: %s", G_STRLOC, error);
626       g_free (error);
627       return;
628     }
629
630   clutter_transition_set_value (transition,
631                                 clutter_interval_set_initial_value,
632                                 &value);
633
634   g_value_unset (&value);
635 }
636
637 /**
638  * clutter_transition_set_to: (skip)
639  * @transition: a #ClutterTransition
640  * @value_type: the type of the value to set
641  * @...: the final value
642  *
643  * Sets the final value of the transition.
644  *
645  * This is a convenience function that will either create the
646  * #ClutterInterval used by @transition, or will update it if
647  * the #ClutterTransition:interval is already set.
648  *
649  * If @transition already has a #ClutterTransition:interval set,
650  * then @value must hold the same type, or a transformable type,
651  * as the interval's #ClutterInterval:value-type property.
652  *
653  * This is a convenience function for the C API; language bindings
654  * should use clutter_transition_set_to_value() instead.
655  *
656  * Since: 1.12
657  */
658 void
659 clutter_transition_set_to (ClutterTransition *transition,
660                            GType              value_type,
661                            ...)
662 {
663   GValue value = G_VALUE_INIT;
664   gchar *error = NULL;
665   va_list args;
666
667   g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
668   g_return_if_fail (value_type != G_TYPE_INVALID);
669
670   va_start (args, value_type);
671
672   G_VALUE_COLLECT_INIT (&value, value_type, args, 0, &error);
673
674   va_end (args);
675
676   if (error != NULL)
677     {
678       g_warning ("%s: %s", G_STRLOC, error);
679       g_free (error);
680       return;
681     }
682
683   clutter_transition_set_value (transition,
684                                 clutter_interval_set_final_value,
685                                 &value);
686
687   g_value_unset (&value);
688 }