property-transition: Verify the interval on compute_value()
[profile/ivi/clutter.git] / clutter / clutter-property-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
22 /**
23  * SECTION:clutter-property-transition
24  * @Title: ClutterPropertyTransition
25  * @Short_Description: Property transitions
26  *
27  * #ClutterPropertyTransition is a specialized #ClutterTransition that
28  * can be used to tween a property of a #ClutterAnimatable instance.
29  *
30  * #ClutterPropertyTransition is available since Clutter 1.10
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include "clutter-property-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-transition.h"
44
45 struct _ClutterPropertyTransitionPrivate
46 {
47   char *property_name;
48
49   GParamSpec *pspec;
50 };
51
52 enum
53 {
54   PROP_0,
55
56   PROP_PROPERTY_NAME,
57
58   PROP_LAST
59 };
60
61 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
62
63 G_DEFINE_TYPE (ClutterPropertyTransition, clutter_property_transition, CLUTTER_TYPE_TRANSITION)
64
65 static inline void
66 clutter_property_transition_ensure_interval (ClutterPropertyTransition *transition,
67                                              ClutterAnimatable         *animatable,
68                                              ClutterInterval           *interval)
69 {
70   ClutterPropertyTransitionPrivate *priv = transition->priv;
71   GValue *value_p;
72
73   if (clutter_interval_is_valid (interval))
74     return;
75
76   /* if no initial value has been set, use the current value */
77   value_p = clutter_interval_peek_initial_value (interval);
78   if (!G_IS_VALUE (value_p))
79     {
80       g_value_init (value_p, clutter_interval_get_value_type (interval));
81       clutter_animatable_get_initial_state (animatable,
82                                             priv->property_name,
83                                             value_p);
84     }
85
86   /* if no final value has been set, use the current value */
87   value_p = clutter_interval_peek_final_value (interval);
88   if (!G_IS_VALUE (value_p))
89     {
90       g_value_init (value_p, clutter_interval_get_value_type (interval));
91       clutter_animatable_get_initial_state (animatable,
92                                             priv->property_name,
93                                             value_p);
94     }
95 }
96
97 static void
98 clutter_property_transition_attached (ClutterTransition *transition,
99                                       ClutterAnimatable *animatable)
100 {
101   ClutterPropertyTransition *self = CLUTTER_PROPERTY_TRANSITION (transition);
102   ClutterPropertyTransitionPrivate *priv = self->priv;
103   ClutterInterval *interval;
104
105   if (priv->property_name == NULL)
106     return;
107
108   priv->pspec =
109     clutter_animatable_find_property (animatable, priv->property_name);
110
111   if (priv->pspec == NULL)
112     return;
113
114   interval = clutter_transition_get_interval (transition);
115   if (interval == NULL)
116     return;
117
118   clutter_property_transition_ensure_interval (self, animatable, interval);
119 }
120
121 static void
122 clutter_property_transition_detached (ClutterTransition *transition,
123                                       ClutterAnimatable *animatable)
124 {
125   ClutterPropertyTransition *self = CLUTTER_PROPERTY_TRANSITION (transition);
126   ClutterPropertyTransitionPrivate *priv = self->priv;
127
128   priv->pspec = NULL; 
129 }
130
131 static void
132 clutter_property_transition_compute_value (ClutterTransition *transition,
133                                            ClutterAnimatable *animatable,
134                                            ClutterInterval   *interval,
135                                            gdouble            progress)
136 {
137   ClutterPropertyTransition *self = CLUTTER_PROPERTY_TRANSITION (transition);
138   ClutterPropertyTransitionPrivate *priv = self->priv;
139   GValue value = G_VALUE_INIT;
140   gboolean res;
141
142   /* if we have a GParamSpec we also have an animatable instance */
143   if (priv->pspec == NULL)
144     return;
145
146   clutter_property_transition_ensure_interval (self, animatable, interval);
147
148   g_value_init (&value, clutter_interval_get_value_type (interval));
149
150   res = clutter_animatable_interpolate_value (animatable,
151                                               priv->property_name,
152                                               interval,
153                                               progress,
154                                               &value);
155
156   if (res)
157     clutter_animatable_set_final_state (animatable,
158                                         priv->property_name,
159                                         &value);
160
161   g_value_unset (&value);
162 }
163
164 static void
165 clutter_property_transition_set_property (GObject      *gobject,
166                                           guint         prop_id,
167                                           const GValue *value,
168                                           GParamSpec   *pspec)
169 {
170   ClutterPropertyTransition *self = CLUTTER_PROPERTY_TRANSITION (gobject);
171
172   switch (prop_id)
173     {
174     case PROP_PROPERTY_NAME:
175       clutter_property_transition_set_property_name (self,
176                                                      g_value_get_string (value));
177       break;
178
179     default:
180       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
181     }
182 }
183
184 static void
185 clutter_property_transition_get_property (GObject    *gobject,
186                                           guint       prop_id,
187                                           GValue     *value,
188                                           GParamSpec *pspec)
189 {
190   ClutterPropertyTransitionPrivate *priv = CLUTTER_PROPERTY_TRANSITION (gobject)->priv;
191
192   switch (prop_id)
193     {
194     case PROP_PROPERTY_NAME:
195       g_value_set_string (value, priv->property_name);
196       break;
197
198     default:
199       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
200     }
201 }
202
203 static void
204 clutter_property_transition_finalize (GObject *gobject)
205 {
206   ClutterPropertyTransitionPrivate *priv;
207
208   priv = CLUTTER_PROPERTY_TRANSITION (gobject)->priv;
209
210   g_free (priv->property_name);
211
212   G_OBJECT_CLASS (clutter_property_transition_parent_class)->finalize (gobject);
213 }
214
215 static void
216 clutter_property_transition_class_init (ClutterPropertyTransitionClass *klass)
217 {
218   ClutterTransitionClass *transition_class = CLUTTER_TRANSITION_CLASS (klass);
219   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
220
221   g_type_class_add_private (klass, sizeof (ClutterPropertyTransitionPrivate));
222
223   transition_class->attached = clutter_property_transition_attached;
224   transition_class->detached = clutter_property_transition_detached;
225   transition_class->compute_value = clutter_property_transition_compute_value;
226
227   gobject_class->set_property = clutter_property_transition_set_property;
228   gobject_class->get_property = clutter_property_transition_get_property;
229   gobject_class->finalize = clutter_property_transition_finalize;
230
231   /**
232    * ClutterPropertyTransition:property-name:
233    *
234    * The name of the property of a #ClutterAnimatable to animate.
235    *
236    * Since: 1.10
237    */
238   obj_props[PROP_PROPERTY_NAME] =
239     g_param_spec_string ("property-name",
240                          P_("Property Name"),
241                          P_("The name of the property to animate"),
242                          NULL,
243                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
244
245   g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
246 }
247
248 static void
249 clutter_property_transition_init (ClutterPropertyTransition *self)
250 {
251   self->priv =
252     G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_PROPERTY_TRANSITION,
253                                  ClutterPropertyTransitionPrivate);
254 }
255
256 /**
257  * clutter_property_transition_new:
258  * @property_name: (allow-none): a property of @animatable, or %NULL
259  *
260  * Creates a new #ClutterPropertyTransition.
261  *
262  * Return value: (transfer full): the newly created #ClutterPropertyTransition.
263  *   Use g_object_unref() when done
264  *
265  * Since: 1.10
266  */
267 ClutterTransition *
268 clutter_property_transition_new (const char *property_name)
269 {
270   return g_object_new (CLUTTER_TYPE_PROPERTY_TRANSITION,
271                        "property-name", property_name,
272                        NULL);
273 }
274
275 /**
276  * clutter_property_transition_set_property_name:
277  * @transition: a #ClutterPropertyTransition
278  * @property_name: (allow-none): a property name
279  *
280  * Sets the #ClutterPropertyTransition:property-name property of @transition.
281  *
282  * Since: 1.10
283  */
284 void
285 clutter_property_transition_set_property_name (ClutterPropertyTransition *transition,
286                                                const char                *property_name)
287 {
288   ClutterPropertyTransitionPrivate *priv;
289   ClutterAnimatable *animatable;
290
291   g_return_if_fail (CLUTTER_IS_PROPERTY_TRANSITION (transition));
292
293   priv = transition->priv;
294
295   if (g_strcmp0 (priv->property_name, property_name) == 0)
296     return;
297
298   g_free (priv->property_name);
299   priv->property_name = g_strdup (property_name);
300   priv->pspec = NULL;
301
302   animatable =
303     clutter_transition_get_animatable (CLUTTER_TRANSITION (transition));
304   if (animatable != NULL)
305     {
306       priv->pspec = clutter_animatable_find_property (animatable,
307                                                       priv->property_name);
308     }
309
310   g_object_notify_by_pspec (G_OBJECT (transition),
311                             obj_props[PROP_PROPERTY_NAME]);
312 }
313
314 /**
315  * clutter_property_transition_get_property_name:
316  * @transition: a #ClutterPropertyTransition
317  *
318  * Retrieves the value of the #ClutterPropertyTransition:property-name
319  * property.
320  *
321  * Return value: the name of the property being animated, or %NULL if
322  *   none is set. The returned string is owned by the @transition and
323  *   it should not be freed.
324  *
325  * Since: 1.10
326  */
327 const char *
328 clutter_property_transition_get_property_name (ClutterPropertyTransition *transition)
329 {
330   g_return_val_if_fail (CLUTTER_IS_PROPERTY_TRANSITION (transition), NULL);
331
332   return transition->priv->property_name;
333 }