~gnome3-team/mutter/trunk

« back to all changes in this revision

Viewing changes to clutter/clutter/deprecated/clutter-animator.c

  • Committer: Rui Matos
  • Date: 2016-04-27 16:34:03 UTC
  • mfrom: (0.1.7560)
  • Revision ID: git-v1:a7b5d790ac66477ad9e3d940527c198332a03695
Merge clutter's master branch into mutter

https://bugzilla.gnome.org/show_bug.cgi?id=760439

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Clutter.
 
3
 *
 
4
 * An OpenGL based 'interactive canvas' library.
 
5
 *
 
6
 * Copyright (C) 2010 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:
 
22
 *   Øyvind Kolås <pippin@linux.intel.com>
 
23
 */
 
24
 
 
25
/**
 
26
 * SECTION:clutter-animator
 
27
 * @short_description: Multi-actor tweener
 
28
 * @See_Also: #ClutterAnimatable, #ClutterInterval, #ClutterAlpha,
 
29
 *   #ClutterTimeline
 
30
 *
 
31
 * #ClutterAnimator is an object providing declarative animations for
 
32
 * #GObject properties belonging to one or more #GObject<!-- -->s to
 
33
 * #ClutterIntervals.
 
34
 *
 
35
 * #ClutterAnimator is used to build and describe complex animations
 
36
 * in terms of "key frames". #ClutterAnimator is meant to be used
 
37
 * through the #ClutterScript definition format, but it comes with a
 
38
 * convenience C API.
 
39
 *
 
40
 * #ClutterAnimator is available since Clutter 1.2
 
41
 *
 
42
 * #ClutterAnimator has been deprecated in Clutter 1.12. If you
 
43
 * want to combine multiple transitions using key frames, use
 
44
 * #ClutterKeyframeTransition and #ClutterTransitionGroup instead.
 
45
 *
 
46
 * ## Key Frames
 
47
 *
 
48
 * Every animation handled by a #ClutterAnimator can be
 
49
 * described in terms of "key frames". For each #GObject property
 
50
 * there can be multiple key frames, each one defined by the end
 
51
 * value for the property to be computed starting from the current
 
52
 * value to a specific point in time, using a given easing
 
53
 * mode.
 
54
 *
 
55
 * The point in time is defined using a value representing
 
56
 * the progress in the normalized interval of [ 0, 1 ]. This maps
 
57
 * the value returned by clutter_timeline_get_duration().
 
58
 *
 
59
 * ## ClutterAnimator description for ClutterScript
 
60
 *
 
61
 * #ClutterAnimator defines a custom "properties" key
 
62
 * which allows describing the key frames for objects as
 
63
 * an array of key frames.
 
64
 *
 
65
 * The `properties` array has the following syntax:
 
66
 *
 
67
 * |[
 
68
 *  {
 
69
 *    "properties" : [
 
70
 *      {
 
71
 *        "object" : object_id
 
72
 *        "name" : property_name
 
73
 *        "ease-in" : true_or_false
 
74
 *        "interpolation" : interpolation_value
 
75
 *        "keys" : [
 
76
 *          [ progress, easing_mode, final_value ]
 
77
 *        ]
 
78
 *    ]
 
79
 *  }
 
80
 * ]|
 
81
 *
 
82
 * The following JSON fragment defines a #ClutterAnimator
 
83
 * with the duration of 1 second and operating on the x and y
 
84
 * properties of a #ClutterActor named "rect-01", with two frames
 
85
 * for each property. The first frame will linearly move the actor
 
86
 * from its current position to the 100, 100 position in 20 percent
 
87
 * of the duration of the animation; the second will using a cubic
 
88
 * easing to move the actor to the 200, 200 coordinates.
 
89
 *
 
90
 * |[
 
91
 *  {
 
92
 *    "type" : "ClutterAnimator",
 
93
 *    "duration" : 1000,
 
94
 *    "properties" : [
 
95
 *      {
 
96
 *        "object" : "rect-01",
 
97
 *        "name" : "x",
 
98
 *        "ease-in" : true,
 
99
 *        "keys" : [
 
100
 *          [ 0.2, "linear",       100.0 ],
 
101
 *          [ 1.0, "easeOutCubic", 200.0 ]
 
102
 *        ]
 
103
 *      },
 
104
 *      {
 
105
 *        "object" : "rect-01",
 
106
 *        "name" : "y",
 
107
 *        "ease-in" : true,
 
108
 *        "keys" : [
 
109
 *          [ 0.2, "linear",       100.0 ],
 
110
 *          [ 1.0, "easeOutCubic", 200.0 ]
 
111
 *        ]
 
112
 *      }
 
113
 *    ]
 
114
 *  }
 
115
 * ]|
 
116
 */
 
117
 
 
118
#ifdef HAVE_CONFIG_H
 
119
#include "config.h"
 
120
#endif
 
121
 
 
122
#include <string.h>
 
123
#include <math.h>
 
124
 
 
125
#include <gobject/gvaluecollector.h>
 
126
 
 
127
#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
 
128
 
 
129
#include "clutter-animator.h"
 
130
 
 
131
#include "clutter-alpha.h"
 
132
#include "clutter-debug.h"
 
133
#include "clutter-enum-types.h"
 
134
#include "clutter-interval.h"
 
135
#include "clutter-private.h"
 
136
#include "clutter-script-private.h"
 
137
#include "clutter-scriptable.h"
 
138
 
 
139
/* progress values varying by less than this are considered equal */
 
140
#define PROGRESS_EPSILON  0.00001
 
141
 
 
142
struct _ClutterAnimatorPrivate
 
143
{
 
144
  ClutterTimeline  *timeline;
 
145
  ClutterTimeline  *slave_timeline;
 
146
 
 
147
  GList            *score;
 
148
 
 
149
  GHashTable       *properties;
 
150
};
 
151
 
 
152
struct _ClutterAnimatorKey
 
153
{
 
154
  GObject             *object;
 
155
  const gchar         *property_name;
 
156
  guint                mode;
 
157
 
 
158
  GValue               value;
 
159
 
 
160
  /* normalized progress, between 0.0 and 1.0 */
 
161
  gdouble              progress;
 
162
 
 
163
  /* back-pointer to the animator which owns the key */
 
164
  ClutterAnimator     *animator;
 
165
 
 
166
  /* interpolation mode */
 
167
  ClutterInterpolation interpolation;
 
168
 
 
169
  /* ease from the current object state into the animation when it starts */
 
170
  guint                ease_in : 1;
 
171
 
 
172
  /* This key is already being destroyed and shouldn't
 
173
   * trigger additional weak unrefs
 
174
   */
 
175
  guint                is_inert : 1;
 
176
 
 
177
  gint                 ref_count;
 
178
};
 
179
 
 
180
enum
 
181
{
 
182
  PROP_0,
 
183
 
 
184
  PROP_DURATION,
 
185
  PROP_TIMELINE,
 
186
 
 
187
  PROP_LAST
 
188
};
 
189
 
 
190
static GParamSpec *obj_props[PROP_LAST];
 
191
 
 
192
static void clutter_scriptable_init (ClutterScriptableIface *iface);
 
193
 
 
194
G_DEFINE_TYPE_WITH_CODE (ClutterAnimator,
 
195
                         clutter_animator,
 
196
                         G_TYPE_OBJECT,
 
197
                         G_ADD_PRIVATE (ClutterAnimator)
 
198
                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
 
199
                                                clutter_scriptable_init));
 
200
/**
 
201
 * clutter_animator_new:
 
202
 *
 
203
 * Creates a new #ClutterAnimator instance
 
204
 *
 
205
 * Return value: a new #ClutterAnimator.
 
206
 *
 
207
 * Since: 1.2
 
208
 *
 
209
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
210
 */
 
211
ClutterAnimator *
 
212
clutter_animator_new (void)
 
213
{
 
214
  return g_object_new (CLUTTER_TYPE_ANIMATOR, NULL);
 
215
}
 
216
 
 
217
/***/
 
218
 
 
219
typedef struct _PropObjectKey {
 
220
  GObject      *object;
 
221
  const gchar  *property_name;
 
222
  guint         mode;
 
223
  gdouble       progress;
 
224
} PropObjectKey;
 
225
 
 
226
/* Iterator that walks the keys of a property*/
 
227
typedef struct _PropertyIter {
 
228
  PropObjectKey       *key;
 
229
  ClutterInterval     *interval;
 
230
  ClutterAlpha        *alpha;
 
231
 
 
232
  GList               *current;
 
233
 
 
234
  gdouble              start;    /* the progress of current */
 
235
  gdouble              end;      /* until which progress it is valid */
 
236
  ClutterInterpolation interpolation;
 
237
 
 
238
  guint                ease_in : 1;
 
239
} PropertyIter;
 
240
 
 
241
static PropObjectKey *
 
242
prop_actor_key_new (GObject     *object,
 
243
                    const gchar *property_name)
 
244
{
 
245
  PropObjectKey *key = g_slice_new0 (PropObjectKey);
 
246
 
 
247
  key->object = object;
 
248
  key->property_name = g_intern_string (property_name);
 
249
 
 
250
  return key;
 
251
}
 
252
 
 
253
static void
 
254
prop_actor_key_free (gpointer key)
 
255
{
 
256
  if (key != NULL)
 
257
    g_slice_free (PropObjectKey, key);
 
258
}
 
259
 
 
260
static void
 
261
property_iter_free (gpointer key)
 
262
{
 
263
  if (key != NULL)
 
264
    {
 
265
      PropertyIter *property_iter = key;
 
266
 
 
267
      g_object_unref (property_iter->interval);
 
268
      g_object_unref (property_iter->alpha);
 
269
 
 
270
      g_slice_free (PropertyIter, property_iter);
 
271
    }
 
272
}
 
273
 
 
274
static PropertyIter *
 
275
property_iter_new (ClutterAnimator *animator,
 
276
                   PropObjectKey   *key,
 
277
                   GType            type)
 
278
{
 
279
  ClutterAnimatorPrivate *priv = animator->priv;
 
280
  PropertyIter *property_iter = g_slice_new (PropertyIter);
 
281
  ClutterInterval *interval = g_object_new (CLUTTER_TYPE_INTERVAL,
 
282
                                            "value-type", type,
 
283
                                            NULL);
 
284
 
 
285
  /* we own this interval */
 
286
  g_object_ref_sink (interval);
 
287
 
 
288
  property_iter->interval = interval;
 
289
  property_iter->key = key;
 
290
  property_iter->alpha = clutter_alpha_new ();
 
291
  clutter_alpha_set_timeline (property_iter->alpha, priv->slave_timeline);
 
292
 
 
293
  /* as well as the alpha */
 
294
  g_object_ref_sink (property_iter->alpha);
 
295
 
 
296
  return property_iter;
 
297
}
 
298
 
 
299
static guint
 
300
prop_actor_hash (gconstpointer value)
 
301
{
 
302
  const PropObjectKey *info = value;
 
303
 
 
304
  return GPOINTER_TO_INT (info->property_name)
 
305
       ^ GPOINTER_TO_INT (info->object);
 
306
}
 
307
 
 
308
static gboolean
 
309
prop_actor_equal (gconstpointer a, gconstpointer b)
 
310
{
 
311
  const PropObjectKey *infoa = a;
 
312
  const PropObjectKey *infob = b;
 
313
 
 
314
  /* property name strings are interned so we can just compare pointers */
 
315
  if (infoa->object == infob->object &&
 
316
      (infoa->property_name == infob->property_name))
 
317
    return TRUE;
 
318
 
 
319
  return FALSE;
 
320
}
 
321
 
 
322
static gint
 
323
sort_actor_prop_progress_func (gconstpointer a,
 
324
                               gconstpointer b)
 
325
{
 
326
  const ClutterAnimatorKey *pa = a;
 
327
  const ClutterAnimatorKey *pb = b;
 
328
 
 
329
  if (pa->object == pb->object)
 
330
    {
 
331
      gint pdiff = pb->property_name - pa->property_name;
 
332
 
 
333
      if (pdiff)
 
334
        return pdiff;
 
335
 
 
336
      if (fabs (pa->progress - pb->progress) < PROGRESS_EPSILON)
 
337
        return 0;
 
338
 
 
339
      if (pa->progress > pb->progress)
 
340
        return 1;
 
341
 
 
342
      return -1;
 
343
    }
 
344
 
 
345
  return pa->object - pb->object;
 
346
}
 
347
 
 
348
static gint
 
349
sort_actor_prop_func (gconstpointer a,
 
350
                      gconstpointer b)
 
351
{
 
352
  const ClutterAnimatorKey *pa = a;
 
353
  const ClutterAnimatorKey *pb = b;
 
354
 
 
355
  if (pa->object == pb->object)
 
356
    return pa->property_name - pb->property_name;
 
357
 
 
358
  return pa->object - pb->object;
 
359
}
 
360
 
 
361
static void
 
362
clutter_animator_remove_key_internal (ClutterAnimator *animator,
 
363
                                      GObject         *object,
 
364
                                      const gchar     *property_name,
 
365
                                      gdouble          progress,
 
366
                                      gboolean         is_inert);
 
367
 
 
368
static void
 
369
object_disappeared (gpointer  data,
 
370
                    GObject  *where_the_object_was)
 
371
{
 
372
  clutter_animator_remove_key_internal (data, where_the_object_was, NULL, -1.0,
 
373
                                        TRUE);
 
374
}
 
375
 
 
376
static ClutterAnimatorKey *
 
377
clutter_animator_key_new (ClutterAnimator *animator,
 
378
                          GObject         *object,
 
379
                          const gchar     *property_name,
 
380
                          gdouble          progress,
 
381
                          guint            mode)
 
382
{
 
383
  ClutterAnimatorKey *animator_key;
 
384
 
 
385
  animator_key = g_slice_new (ClutterAnimatorKey);
 
386
 
 
387
  animator_key->ref_count = 1;
 
388
  animator_key->animator = animator;
 
389
  animator_key->object = object;
 
390
  animator_key->mode = mode;
 
391
  memset (&(animator_key->value), 0, sizeof (GValue));
 
392
  animator_key->progress = progress;
 
393
  animator_key->property_name = g_intern_string (property_name);
 
394
  animator_key->interpolation = CLUTTER_INTERPOLATION_LINEAR;
 
395
  animator_key->ease_in = FALSE;
 
396
  animator_key->is_inert = FALSE;
 
397
 
 
398
  /* keep a weak reference on the animator, so that we can release the
 
399
   * back-pointer when needed
 
400
   */
 
401
  g_object_weak_ref (object, object_disappeared,
 
402
                     animator_key->animator);
 
403
 
 
404
  return animator_key;
 
405
}
 
406
 
 
407
static gpointer
 
408
clutter_animator_key_copy (gpointer boxed)
 
409
{
 
410
  ClutterAnimatorKey *key = boxed;
 
411
 
 
412
  if (key != NULL)
 
413
    key->ref_count += 1;
 
414
 
 
415
  return key;
 
416
}
 
417
 
 
418
static void
 
419
clutter_animator_key_free (gpointer boxed)
 
420
{
 
421
  ClutterAnimatorKey *key = boxed;
 
422
 
 
423
  if (key == NULL)
 
424
    return;
 
425
 
 
426
  key->ref_count -= 1;
 
427
 
 
428
  if (key->ref_count > 0)
 
429
    return;
 
430
 
 
431
  if (!key->is_inert)
 
432
    g_object_weak_unref (key->object, object_disappeared, key->animator);
 
433
 
 
434
  g_slice_free (ClutterAnimatorKey, key);
 
435
}
 
436
 
 
437
static void
 
438
clutter_animator_dispose (GObject *object)
 
439
{
 
440
  ClutterAnimator *animator = CLUTTER_ANIMATOR (object);
 
441
  ClutterAnimatorPrivate *priv = animator->priv;
 
442
 
 
443
  clutter_animator_set_timeline (animator, NULL);
 
444
  g_object_unref (priv->slave_timeline);
 
445
 
 
446
  G_OBJECT_CLASS (clutter_animator_parent_class)->dispose (object);
 
447
}
 
448
 
 
449
static void
 
450
clutter_animator_finalize (GObject *object)
 
451
{
 
452
  ClutterAnimator *animator = CLUTTER_ANIMATOR (object);
 
453
  ClutterAnimatorPrivate *priv = animator->priv;
 
454
 
 
455
  g_list_foreach (priv->score, (GFunc) clutter_animator_key_free, NULL);
 
456
  g_list_free (priv->score);
 
457
  priv->score = NULL;
 
458
 
 
459
  g_hash_table_destroy (priv->properties);
 
460
 
 
461
  G_OBJECT_CLASS (clutter_animator_parent_class)->finalize (object);
 
462
}
 
463
 
 
464
/* XXX: this is copied and slightly modified from glib,
 
465
 * there is only one way to do this. */
 
466
static GList *
 
467
list_find_custom_reverse (GList         *list,
 
468
                          gconstpointer  data,
 
469
                          GCompareFunc   func)
 
470
{
 
471
  while (list)
 
472
    {
 
473
      if (! func (list->data, data))
 
474
        return list;
 
475
 
 
476
      list = list->prev;
 
477
    }
 
478
 
 
479
  return NULL;
 
480
}
 
481
 
 
482
/* Ensures that the interval provided by the animator is correct
 
483
 * for the requested progress value.
 
484
 */
 
485
static void
 
486
animation_animator_ensure_animator (ClutterAnimator *animator,
 
487
                                    PropertyIter    *property_iter,
 
488
                                    PropObjectKey   *key,
 
489
                                    gdouble          progress)
 
490
{
 
491
 
 
492
  if (progress > property_iter->end)
 
493
    {
 
494
      while (progress > property_iter->end)
 
495
        {
 
496
          ClutterAnimatorKey *initial_key, *next_key;
 
497
          GList *initial, *next;
 
498
 
 
499
          initial = g_list_find_custom (property_iter->current->next,
 
500
                                        key,
 
501
                                        sort_actor_prop_func);
 
502
 
 
503
          if (initial)
 
504
            {
 
505
              initial_key = initial->data;
 
506
 
 
507
              clutter_interval_set_initial_value (property_iter->interval,
 
508
                                                  &initial_key->value);
 
509
              property_iter->current = initial;
 
510
              property_iter->start = initial_key->progress;
 
511
 
 
512
              next = g_list_find_custom (initial->next,
 
513
                                         key,
 
514
                                         sort_actor_prop_func);
 
515
              if (next)
 
516
                {
 
517
                  next_key = next->data;
 
518
 
 
519
                  property_iter->end = next_key->progress;
 
520
                }
 
521
              else
 
522
                {
 
523
                  next_key = initial_key;
 
524
 
 
525
                  property_iter->end = property_iter->start;
 
526
                }
 
527
 
 
528
              clutter_interval_set_final_value (property_iter->interval,
 
529
                                                &next_key->value);
 
530
 
 
531
              if ((clutter_alpha_get_mode (property_iter->alpha) != next_key->mode))
 
532
                clutter_alpha_set_mode (property_iter->alpha, next_key->mode);
 
533
            }
 
534
          else /* no relevant interval */
 
535
            {
 
536
              ClutterAnimatorKey *current_key = property_iter->current->data;
 
537
              clutter_interval_set_initial_value (property_iter->interval,
 
538
                                                  &current_key->value);
 
539
              clutter_interval_set_final_value (property_iter->interval,
 
540
                                                &current_key->value);
 
541
              break;
 
542
            }
 
543
        }
 
544
    }
 
545
  else if (progress < property_iter->start)
 
546
    {
 
547
      while (progress < property_iter->start)
 
548
        {
 
549
          ClutterAnimatorKey *initial_key, *next_key;
 
550
          GList *initial;
 
551
          GList *old = property_iter->current;
 
552
 
 
553
          initial = list_find_custom_reverse (property_iter->current->prev,
 
554
                                              key,
 
555
                                              sort_actor_prop_func);
 
556
 
 
557
          if (initial)
 
558
            {
 
559
              initial_key = initial->data;
 
560
 
 
561
              clutter_interval_set_initial_value (property_iter->interval,
 
562
                                                  &initial_key->value);
 
563
              property_iter->current = initial;
 
564
              property_iter->end = property_iter->start;
 
565
              property_iter->start = initial_key->progress;
 
566
 
 
567
              if (old)
 
568
                {
 
569
                  next_key = old->data;
 
570
 
 
571
                  property_iter->end = next_key->progress;
 
572
                }
 
573
              else
 
574
                {
 
575
                  next_key = initial_key;
 
576
 
 
577
                  property_iter->end = 1.0;
 
578
                }
 
579
 
 
580
              clutter_interval_set_final_value (property_iter->interval,
 
581
                                                &next_key->value);
 
582
              if ((clutter_alpha_get_mode (property_iter->alpha) != next_key->mode))
 
583
                clutter_alpha_set_mode (property_iter->alpha, next_key->mode);
 
584
            }
 
585
          else
 
586
            break;
 
587
        }
 
588
    }
 
589
}
 
590
 
 
591
/* XXX - this might be useful as an internal function exposed somewhere */
 
592
static gdouble
 
593
cubic_interpolation (const gdouble dx,
 
594
                     const gdouble prev,
 
595
                     const gdouble j,
 
596
                     const gdouble next,
 
597
                     const gdouble nextnext)
 
598
{
 
599
  return (((( - prev + 3 * j - 3 * next + nextnext ) * dx +
 
600
            ( 2 * prev - 5 * j + 4 * next - nextnext ) ) * dx +
 
601
            ( - prev + next ) ) * dx + (j + j) ) / 2.0;
 
602
}
 
603
 
 
604
/* try to get a floating point key value from a key for a property,
 
605
 * failing use the closest key in that direction or the starting point.
 
606
 */
 
607
static gfloat
 
608
list_try_get_rel (GList *list,
 
609
                  gint   count)
 
610
{
 
611
  ClutterAnimatorKey *key;
 
612
  GList *iter = list;
 
613
  GList *best = list;
 
614
 
 
615
  if (count > 0)
 
616
    {
 
617
      while (count -- && iter != NULL)
 
618
        {
 
619
          iter = g_list_find_custom (iter->next, list->data,
 
620
                                     sort_actor_prop_func);
 
621
          if (iter != NULL)
 
622
            best = iter;
 
623
        }
 
624
    }
 
625
  else
 
626
    {
 
627
      while (count ++ < 0 && iter != NULL)
 
628
        {
 
629
          iter = list_find_custom_reverse (iter->prev, list->data,
 
630
                                           sort_actor_prop_func);
 
631
          if (iter != NULL)
 
632
            best = iter;
 
633
        }
 
634
    }
 
635
 
 
636
  if (best != NULL && best->data != NULL)
 
637
    {
 
638
      key = best->data;
 
639
 
 
640
      return g_value_get_float (&(key->value));
 
641
    }
 
642
 
 
643
  return 0;
 
644
}
 
645
 
 
646
static void
 
647
animation_animator_new_frame (ClutterTimeline  *timeline,
 
648
                              gint              msecs,
 
649
                              ClutterAnimator  *animator)
 
650
{
 
651
  gdouble progress;
 
652
  GHashTableIter iter;
 
653
  gpointer key, value;
 
654
 
 
655
  progress  = 1.0 * msecs / clutter_timeline_get_duration (timeline);
 
656
 
 
657
  /* for each property that is managed figure out the GValue to set,
 
658
   * avoid creating new ClutterInterval's for each interval crossed
 
659
   */
 
660
  g_hash_table_iter_init (&iter, animator->priv->properties);
 
661
 
 
662
  key = value = NULL;
 
663
  while (g_hash_table_iter_next (&iter, &key, &value))
 
664
    {
 
665
      PropObjectKey      *prop_actor_key = key;
 
666
      PropertyIter       *property_iter   = value;
 
667
      ClutterAnimatorKey *start_key;
 
668
      gdouble             sub_progress;
 
669
 
 
670
      animation_animator_ensure_animator (animator, property_iter,
 
671
                                          key,
 
672
                                          progress);
 
673
      start_key = property_iter->current->data;
 
674
 
 
675
      if (property_iter->end == property_iter->start)
 
676
        sub_progress = 0.0; /* we're past the final value */
 
677
      else
 
678
        sub_progress = (progress - property_iter->start)
 
679
                     / (property_iter->end - property_iter->start);
 
680
 
 
681
      /* only change values if we active (delayed start) */
 
682
      if (sub_progress >= 0.0 && sub_progress <= 1.0)
 
683
        {
 
684
          GValue tmp_value = G_VALUE_INIT;
 
685
          GType int_type;
 
686
 
 
687
          g_value_init (&tmp_value, G_VALUE_TYPE (&start_key->value));
 
688
 
 
689
          clutter_timeline_advance (animator->priv->slave_timeline,
 
690
                                    sub_progress * 10000);
 
691
 
 
692
          sub_progress = clutter_alpha_get_alpha (property_iter->alpha);
 
693
          int_type = clutter_interval_get_value_type (property_iter->interval);
 
694
 
 
695
          if (property_iter->interpolation == CLUTTER_INTERPOLATION_CUBIC &&
 
696
              int_type == G_TYPE_FLOAT)
 
697
            {
 
698
              gdouble prev, current, next, nextnext;
 
699
              gdouble res;
 
700
 
 
701
              if ((property_iter->ease_in == FALSE ||
 
702
                  (property_iter->ease_in &&
 
703
                   list_find_custom_reverse (property_iter->current->prev,
 
704
                                             property_iter->current->data,
 
705
                                             sort_actor_prop_func))))
 
706
                {
 
707
                  current = g_value_get_float (&start_key->value);
 
708
                  prev = list_try_get_rel (property_iter->current, -1);
 
709
                }
 
710
              else
 
711
                {
 
712
                  /* interpolated and easing in */
 
713
                  clutter_interval_get_initial_value (property_iter->interval,
 
714
                                                      &tmp_value);
 
715
                  prev = current = g_value_get_float (&tmp_value);
 
716
                }
 
717
 
 
718
               next = list_try_get_rel (property_iter->current, 1);
 
719
               nextnext = list_try_get_rel (property_iter->current, 2);
 
720
               res = cubic_interpolation (sub_progress, prev, current, next,
 
721
                                          nextnext);
 
722
 
 
723
               g_value_set_float (&tmp_value, res);
 
724
            }
 
725
          else
 
726
            clutter_interval_compute_value (property_iter->interval,
 
727
                                            sub_progress,
 
728
                                            &tmp_value);
 
729
 
 
730
          g_object_set_property (prop_actor_key->object,
 
731
                                 prop_actor_key->property_name,
 
732
                                 &tmp_value);
 
733
 
 
734
          g_value_unset (&tmp_value);
 
735
        }
 
736
    }
 
737
}
 
738
 
 
739
static void
 
740
animation_animator_started (ClutterTimeline *timeline,
 
741
                            ClutterAnimator *animator)
 
742
{
 
743
  GList *k;
 
744
 
 
745
  /* Ensure that animators exist for all involved properties */
 
746
  for (k = animator->priv->score; k != NULL; k = k->next)
 
747
    {
 
748
      ClutterAnimatorKey *key = k->data;
 
749
      PropertyIter       *property_iter;
 
750
      PropObjectKey      *prop_actor_key;
 
751
 
 
752
      prop_actor_key = prop_actor_key_new (key->object, key->property_name);
 
753
      property_iter = g_hash_table_lookup (animator->priv->properties,
 
754
                                          prop_actor_key);
 
755
      if (property_iter)
 
756
        {
 
757
          prop_actor_key_free (prop_actor_key);
 
758
        }
 
759
      else
 
760
        {
 
761
          GObjectClass *klass = G_OBJECT_GET_CLASS (key->object);
 
762
          GParamSpec *pspec;
 
763
 
 
764
          pspec = g_object_class_find_property (klass, key->property_name);
 
765
 
 
766
          property_iter = property_iter_new (animator, prop_actor_key,
 
767
                                           G_PARAM_SPEC_VALUE_TYPE (pspec));
 
768
          g_hash_table_insert (animator->priv->properties,
 
769
                               prop_actor_key,
 
770
                               property_iter);
 
771
        }
 
772
    }
 
773
 
 
774
  /* initialize animator with initial list pointers */
 
775
  {
 
776
    GHashTableIter iter;
 
777
    gpointer key, value;
 
778
 
 
779
    g_hash_table_iter_init (&iter, animator->priv->properties);
 
780
    while (g_hash_table_iter_next (&iter, &key, &value))
 
781
      {
 
782
        PropertyIter *property_iter = value;
 
783
        ClutterAnimatorKey *initial_key, *next_key;
 
784
        GList *initial;
 
785
        GList *next;
 
786
 
 
787
        initial = g_list_find_custom (animator->priv->score,
 
788
                                      key,
 
789
                                      sort_actor_prop_func);
 
790
        g_assert (initial != NULL);
 
791
        initial_key = initial->data;
 
792
        clutter_interval_set_initial_value (property_iter->interval,
 
793
                                            &initial_key->value);
 
794
 
 
795
        property_iter->current       = initial;
 
796
        property_iter->start         = initial_key->progress;
 
797
        property_iter->ease_in       = initial_key->ease_in;
 
798
        property_iter->interpolation = initial_key->interpolation;
 
799
 
 
800
        if (property_iter->ease_in)
 
801
          {
 
802
            GValue tmp_value = G_VALUE_INIT;
 
803
            GType int_type;
 
804
 
 
805
            int_type = clutter_interval_get_value_type (property_iter->interval);
 
806
            g_value_init (&tmp_value, int_type);
 
807
 
 
808
            g_object_get_property (initial_key->object,
 
809
                                   initial_key->property_name,
 
810
                                   &tmp_value);
 
811
 
 
812
            clutter_interval_set_initial_value (property_iter->interval,
 
813
                                                &tmp_value);
 
814
 
 
815
            g_value_unset (&tmp_value);
 
816
          }
 
817
 
 
818
        next = g_list_find_custom (initial->next, key, sort_actor_prop_func);
 
819
        if (next)
 
820
          {
 
821
            next_key = next->data;
 
822
            property_iter->end = next_key->progress;
 
823
          }
 
824
        else
 
825
          {
 
826
            next_key = initial_key;
 
827
            property_iter->end = 1.0;
 
828
          }
 
829
 
 
830
        clutter_interval_set_final_value (property_iter->interval,
 
831
                                          &next_key->value);
 
832
        if ((clutter_alpha_get_mode (property_iter->alpha) != next_key->mode))
 
833
          clutter_alpha_set_mode (property_iter->alpha, next_key->mode);
 
834
      }
 
835
  }
 
836
}
 
837
 
 
838
/**
 
839
 * clutter_animator_compute_value:
 
840
 * @animator: a #ClutterAnimator
 
841
 * @object: a #GObject
 
842
 * @property_name: the name of the property on object to check
 
843
 * @progress: a value between 0.0 and 1.0
 
844
 * @value: an initialized value to store the computed result
 
845
 *
 
846
 * Compute the value for a managed property at a given progress.
 
847
 *
 
848
 * If the property is an ease-in property, the current value of the property
 
849
 * on the object will be used as the starting point for computation.
 
850
 *
 
851
 * Return value: %TRUE if the computation yields has a value, otherwise (when
 
852
 *   an error occurs or the progress is before any of the keys) %FALSE is
 
853
 *   returned and the #GValue is left untouched
 
854
 *
 
855
 * Since: 1.2
 
856
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
857
 */
 
858
gboolean
 
859
clutter_animator_compute_value (ClutterAnimator *animator,
 
860
                                GObject         *object,
 
861
                                const gchar     *property_name,
 
862
                                gdouble          progress,
 
863
                                GValue          *value)
 
864
{
 
865
  ClutterAnimatorPrivate *priv;
 
866
  ClutterAnimatorKey   key;
 
867
  ClutterAnimatorKey  *previous;
 
868
  ClutterAnimatorKey  *next = NULL;
 
869
  GParamSpec          *pspec;
 
870
  GList               *initial_l;
 
871
  GList               *previous_l;
 
872
  GList               *next_l;
 
873
  gboolean             ease_in;
 
874
  ClutterInterpolation interpolation;
 
875
 
 
876
  g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), FALSE);
 
877
  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
 
878
  g_return_val_if_fail (property_name, FALSE);
 
879
  g_return_val_if_fail (value, FALSE);
 
880
 
 
881
  priv = animator->priv;
 
882
 
 
883
  ease_in = clutter_animator_property_get_ease_in (animator, object,
 
884
                                                   property_name);
 
885
  interpolation = clutter_animator_property_get_interpolation (animator,
 
886
                                                   object, property_name);
 
887
 
 
888
  property_name = g_intern_string (property_name);
 
889
 
 
890
  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
 
891
                                        property_name);
 
892
 
 
893
  key.object        = object;
 
894
  key.property_name = property_name;
 
895
 
 
896
  initial_l = g_list_find_custom (animator->priv->score, &key,
 
897
                                  sort_actor_prop_func);
 
898
  if (initial_l == NULL)
 
899
    return FALSE;
 
900
 
 
901
  /* first find the interval we belong in, that is the first interval
 
902
   * existing between keys
 
903
   */
 
904
 
 
905
  for (previous_l = initial_l, next_l = previous_l->next ;
 
906
       previous_l->next ;
 
907
       previous_l = previous_l->next, next_l = previous_l->next)
 
908
    {
 
909
       previous = previous_l->data;
 
910
       if (next_l)
 
911
         {
 
912
           next = next_l->data;
 
913
           if (next->object != object ||
 
914
               next->property_name != property_name)
 
915
             {
 
916
               next_l = NULL;
 
917
               next = NULL;
 
918
             }
 
919
         }
 
920
       else
 
921
         next = NULL;
 
922
 
 
923
       if (progress < previous->progress)
 
924
         {
 
925
            /* we are before the defined values */
 
926
 
 
927
            /* value has not been set */
 
928
            return FALSE;
 
929
         }
 
930
 
 
931
       if (!next && previous->progress <= progress)
 
932
         {
 
933
            /* we only had one key for this object/property */
 
934
            /* and we are past it, that is our value */
 
935
            g_value_copy (&previous->value, value);
 
936
            return TRUE;
 
937
         }
 
938
 
 
939
       if (next && next->progress >= progress)
 
940
         {
 
941
            ClutterInterval *interval;
 
942
            ClutterAlpha    *alpha;
 
943
 
 
944
            gdouble sub_progress = (progress - previous->progress)
 
945
                                 / (next->progress - previous->progress);
 
946
            /* this should be our interval */
 
947
            interval = g_object_new (CLUTTER_TYPE_INTERVAL,
 
948
                                     "value-type", pspec->value_type,
 
949
                                     NULL);
 
950
 
 
951
            if (ease_in && previous_l == initial_l)
 
952
              {
 
953
                GValue tmp_value = {0, };
 
954
                g_value_init (&tmp_value, pspec->value_type);
 
955
                g_object_get_property (object, property_name, &tmp_value);
 
956
                clutter_interval_set_initial_value (interval, &tmp_value);
 
957
                g_value_unset (&tmp_value);
 
958
              }
 
959
            else
 
960
              clutter_interval_set_initial_value (interval, &previous->value);
 
961
 
 
962
            clutter_interval_set_final_value (interval, &next->value);
 
963
 
 
964
            alpha = clutter_alpha_new ();
 
965
            clutter_alpha_set_timeline (alpha, priv->slave_timeline);
 
966
            clutter_alpha_set_mode (alpha, next->mode);
 
967
 
 
968
            clutter_timeline_advance (priv->slave_timeline,
 
969
                                      sub_progress * 10000);
 
970
            sub_progress = clutter_alpha_get_alpha (alpha);
 
971
 
 
972
            if (interpolation == CLUTTER_INTERPOLATION_CUBIC &&
 
973
                pspec->value_type == G_TYPE_FLOAT)
 
974
              {
 
975
                gdouble prev, current, nextv, nextnext;
 
976
                gdouble res;
 
977
 
 
978
                if ((ease_in == FALSE ||
 
979
                    (ease_in &&
 
980
                     list_find_custom_reverse (previous_l->prev,
 
981
                                               previous_l->data,
 
982
                                               sort_actor_prop_func))))
 
983
                  {
 
984
                    current = g_value_get_float (&previous->value);
 
985
                    prev = list_try_get_rel (previous_l, -1);
 
986
                  }
 
987
                else
 
988
                  {
 
989
                    /* interpolated and easing in */
 
990
                    GValue tmp_value = {0, };
 
991
                    g_value_init (&tmp_value, pspec->value_type);
 
992
                    clutter_interval_get_initial_value (interval,
 
993
                                                        &tmp_value);
 
994
                    prev = current = g_value_get_float (&tmp_value);
 
995
                    g_value_unset (&tmp_value);
 
996
                  }
 
997
 
 
998
                 nextv = list_try_get_rel (previous_l, 1);
 
999
                 nextnext = list_try_get_rel (previous_l, 2);
 
1000
                 res = cubic_interpolation (sub_progress, prev, current, nextv,
 
1001
                                            nextnext);
 
1002
                 g_value_set_float (value, res);
 
1003
              }
 
1004
            else
 
1005
              clutter_interval_compute_value (interval,
 
1006
                                              sub_progress,
 
1007
                                              value);
 
1008
 
 
1009
            g_object_ref_sink (interval);
 
1010
            g_object_unref (interval);
 
1011
            g_object_ref_sink (alpha);
 
1012
            g_object_unref (alpha);
 
1013
 
 
1014
            return TRUE;
 
1015
         }
 
1016
 
 
1017
    }
 
1018
 
 
1019
  if (!next)
 
1020
    return FALSE;
 
1021
 
 
1022
  /* We're at, or past the end, use the last value */
 
1023
  g_value_copy (&next->value, value);
 
1024
 
 
1025
  return TRUE;
 
1026
}
 
1027
 
 
1028
 
 
1029
/**
 
1030
 * clutter_animator_set_timeline:
 
1031
 * @animator: a #ClutterAnimator
 
1032
 * @timeline: a #ClutterTimeline
 
1033
 *
 
1034
 * Sets an external timeline that will be used for driving the animation
 
1035
 *
 
1036
 * Since: 1.2
 
1037
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1038
 */
 
1039
void
 
1040
clutter_animator_set_timeline (ClutterAnimator *animator,
 
1041
                               ClutterTimeline *timeline)
 
1042
{
 
1043
  ClutterAnimatorPrivate *priv;
 
1044
 
 
1045
  g_return_if_fail (CLUTTER_IS_ANIMATOR (animator));
 
1046
 
 
1047
  priv = animator->priv;
 
1048
 
 
1049
  if (priv->timeline != NULL)
 
1050
    {
 
1051
      g_signal_handlers_disconnect_by_func (priv->timeline,
 
1052
                                            animation_animator_new_frame,
 
1053
                                            animator);
 
1054
      g_signal_handlers_disconnect_by_func (priv->timeline,
 
1055
                                            animation_animator_started,
 
1056
                                            animator);
 
1057
      g_object_unref (priv->timeline);
 
1058
    }
 
1059
 
 
1060
  priv->timeline = timeline;
 
1061
  if (timeline != NULL)
 
1062
    {
 
1063
      g_object_ref (priv->timeline);
 
1064
 
 
1065
      g_signal_connect (priv->timeline, "new-frame",
 
1066
                        G_CALLBACK (animation_animator_new_frame),
 
1067
                        animator);
 
1068
      g_signal_connect (priv->timeline, "started",
 
1069
                        G_CALLBACK (animation_animator_started),
 
1070
                        animator);
 
1071
    }
 
1072
}
 
1073
 
 
1074
/**
 
1075
 * clutter_animator_get_timeline:
 
1076
 * @animator: a #ClutterAnimator
 
1077
 *
 
1078
 * Get the timeline hooked up for driving the #ClutterAnimator
 
1079
 *
 
1080
 * Return value: (transfer none): the #ClutterTimeline that drives the animator
 
1081
 *
 
1082
 * Since: 1.2
 
1083
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1084
 */
 
1085
ClutterTimeline *
 
1086
clutter_animator_get_timeline (ClutterAnimator *animator)
 
1087
{
 
1088
  g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), NULL);
 
1089
  return animator->priv->timeline;
 
1090
}
 
1091
 
 
1092
/**
 
1093
 * clutter_animator_start:
 
1094
 * @animator: a #ClutterAnimator
 
1095
 *
 
1096
 * Start the ClutterAnimator, this is a thin wrapper that rewinds
 
1097
 * and starts the animators current timeline.
 
1098
 *
 
1099
 * Return value: (transfer none): the #ClutterTimeline that drives
 
1100
 *   the animator. The returned timeline is owned by the #ClutterAnimator
 
1101
 *   and it should not be unreferenced
 
1102
 *
 
1103
 * Since: 1.2
 
1104
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1105
 */
 
1106
ClutterTimeline *
 
1107
clutter_animator_start (ClutterAnimator *animator)
 
1108
{
 
1109
  ClutterAnimatorPrivate *priv;
 
1110
 
 
1111
  g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), NULL);
 
1112
 
 
1113
  priv = animator->priv;
 
1114
 
 
1115
  clutter_timeline_rewind (priv->timeline);
 
1116
  clutter_timeline_start (priv->timeline);
 
1117
 
 
1118
  return priv->timeline;
 
1119
}
 
1120
 
 
1121
/**
 
1122
 * clutter_animator_set_duration:
 
1123
 * @animator: a #ClutterAnimator
 
1124
 * @duration: milliseconds a run of the animator should last.
 
1125
 *
 
1126
 * Runs the timeline of the #ClutterAnimator with a duration in msecs
 
1127
 * as specified.
 
1128
 *
 
1129
 * Since: 1.2
 
1130
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1131
 */
 
1132
void
 
1133
clutter_animator_set_duration (ClutterAnimator *animator,
 
1134
                               guint            duration)
 
1135
{
 
1136
  g_return_if_fail (CLUTTER_IS_ANIMATOR (animator));
 
1137
 
 
1138
  clutter_timeline_set_duration (animator->priv->timeline, duration);
 
1139
}
 
1140
 
 
1141
/**
 
1142
 * clutter_animator_get_duration:
 
1143
 * @animator: a #ClutterAnimator
 
1144
 *
 
1145
 * Retrieves the current duration of an animator
 
1146
 *
 
1147
 * Return value: the duration of the animation, in milliseconds
 
1148
 *
 
1149
 * Since: 1.2
 
1150
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1151
 */
 
1152
guint
 
1153
clutter_animator_get_duration  (ClutterAnimator *animator)
 
1154
{
 
1155
  g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), 0);
 
1156
 
 
1157
  return clutter_timeline_get_duration (animator->priv->timeline);
 
1158
}
 
1159
 
 
1160
/**
 
1161
 * clutter_animator_set:
 
1162
 * @animator: a #ClutterAnimator
 
1163
 * @first_object: a #GObject
 
1164
 * @first_property_name: the property to specify a key for
 
1165
 * @first_mode: the id of the alpha function to use
 
1166
 * @first_progress: at which stage of the animation this value applies; the
 
1167
 *   range is a normalized floating point value between 0 and 1
 
1168
 * @...: the value first_property_name should have for first_object
 
1169
 *   at first_progress, followed by more (object, property_name, mode,
 
1170
 *   progress, value) tuples, followed by %NULL
 
1171
 *
 
1172
 * Adds multiple keys to a #ClutterAnimator, specifying the value a given
 
1173
 * property should have at a given progress of the animation. The mode
 
1174
 * specified is the mode used when going to this key from the previous key of
 
1175
 * the @property_name
 
1176
 *
 
1177
 * If a given (object, property, progress) tuple already exist the mode and
 
1178
 * value will be replaced with the new values.
 
1179
 *
 
1180
 * Since: 1.2
 
1181
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1182
 */
 
1183
void
 
1184
clutter_animator_set (ClutterAnimator *animator,
 
1185
                      gpointer         first_object,
 
1186
                      const gchar     *first_property_name,
 
1187
                      guint            first_mode,
 
1188
                      gdouble          first_progress,
 
1189
                      ...)
 
1190
{
 
1191
  GObject      *object;
 
1192
  const gchar  *property_name;
 
1193
  guint         mode;
 
1194
  gdouble       progress;
 
1195
  va_list       args;
 
1196
 
 
1197
  g_return_if_fail (CLUTTER_IS_ANIMATOR (animator));
 
1198
 
 
1199
  object = first_object;
 
1200
  property_name = first_property_name;
 
1201
 
 
1202
  g_return_if_fail (object);
 
1203
  g_return_if_fail (property_name);
 
1204
 
 
1205
  mode = first_mode;
 
1206
  progress = first_progress;
 
1207
 
 
1208
  va_start (args, first_progress);
 
1209
 
 
1210
  while (object != NULL)
 
1211
    {
 
1212
      GParamSpec *pspec;
 
1213
      GObjectClass *klass;
 
1214
      GValue value = G_VALUE_INIT;
 
1215
      gchar *error = NULL;
 
1216
 
 
1217
      klass = G_OBJECT_GET_CLASS (object);
 
1218
      pspec = g_object_class_find_property (klass, property_name);
 
1219
 
 
1220
      if (!pspec)
 
1221
        {
 
1222
          g_warning ("Cannot bind property '%s': object of type '%s' "
 
1223
                     "do not have this property",
 
1224
                     property_name, G_OBJECT_TYPE_NAME (object));
 
1225
          break;
 
1226
        }
 
1227
 
 
1228
      G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec),
 
1229
                            args, 0,
 
1230
                            &error);
 
1231
 
 
1232
      if (error)
 
1233
        {
 
1234
          g_warning ("%s: %s", G_STRLOC, error);
 
1235
          g_free (error);
 
1236
          break;
 
1237
        }
 
1238
 
 
1239
      clutter_animator_set_key (animator,
 
1240
                                object,
 
1241
                                property_name,
 
1242
                                mode,
 
1243
                                progress,
 
1244
                                &value);
 
1245
 
 
1246
      object= va_arg (args, GObject *);
 
1247
      if (object)
 
1248
        {
 
1249
          property_name = va_arg (args, gchar*);
 
1250
          if (!property_name)
 
1251
           {
 
1252
             g_warning ("%s: expected a property name", G_STRLOC);
 
1253
             break;
 
1254
           }
 
1255
          mode = va_arg (args, guint);
 
1256
          progress = va_arg (args, gdouble);
 
1257
        }
 
1258
    }
 
1259
 
 
1260
  va_end (args);
 
1261
}
 
1262
 
 
1263
static inline void
 
1264
clutter_animator_set_key_internal (ClutterAnimator    *animator,
 
1265
                                   ClutterAnimatorKey *key)
 
1266
{
 
1267
  ClutterAnimatorPrivate *priv = animator->priv;
 
1268
  GList                  *old_item;
 
1269
  GList                  *initial_item;
 
1270
  ClutterAnimatorKey     *initial_key = NULL;
 
1271
 
 
1272
  if ((initial_item = g_list_find_custom (animator->priv->score, key,
 
1273
                                          sort_actor_prop_func)))
 
1274
    initial_key = initial_item->data;
 
1275
 
 
1276
  /* The first key for a property specifies ease-in and interpolation,
 
1277
   * if we are replacing; or becoming a new first key we should
 
1278
   * inherit the old flags.
 
1279
   */
 
1280
  if (initial_key &&
 
1281
      initial_key->progress >= key->progress)
 
1282
    {
 
1283
      key->interpolation = initial_key->interpolation;
 
1284
      key->ease_in = initial_key->ease_in;
 
1285
    }
 
1286
 
 
1287
  old_item = g_list_find_custom (priv->score, key,
 
1288
                                 sort_actor_prop_progress_func);
 
1289
 
 
1290
  /* replace the key if we already have a similar one */
 
1291
  if (old_item != NULL)
 
1292
    {
 
1293
      ClutterAnimatorKey *old_key = old_item->data;
 
1294
 
 
1295
      clutter_animator_key_free (old_key);
 
1296
 
 
1297
      priv->score = g_list_remove (priv->score, old_key);
 
1298
    }
 
1299
 
 
1300
  priv->score = g_list_insert_sorted (priv->score, key,
 
1301
                                      sort_actor_prop_progress_func);
 
1302
 
 
1303
  /* if the animator is already running reinitialize internal iterators */
 
1304
  if (clutter_timeline_is_playing (priv->timeline))
 
1305
    animation_animator_started (priv->timeline, animator);
 
1306
}
 
1307
 
 
1308
/**
 
1309
 * clutter_animator_set_key:
 
1310
 * @animator: a #ClutterAnimator
 
1311
 * @object: a #GObject
 
1312
 * @property_name: the property to specify a key for
 
1313
 * @mode: the id of the alpha function to use
 
1314
 * @progress: the normalized range at which stage of the animation this
 
1315
 *   value applies
 
1316
 * @value: the value property_name should have at progress.
 
1317
 *
 
1318
 * Sets a single key in the #ClutterAnimator for the @property_name of
 
1319
 * @object at @progress.
 
1320
 *
 
1321
 * See also: clutter_animator_set()
 
1322
 *
 
1323
 * Return value: (transfer none): The animator instance
 
1324
 *
 
1325
 * Since: 1.2
 
1326
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1327
 */
 
1328
ClutterAnimator *
 
1329
clutter_animator_set_key (ClutterAnimator *animator,
 
1330
                          GObject         *object,
 
1331
                          const gchar     *property_name,
 
1332
                          guint            mode,
 
1333
                          gdouble          progress,
 
1334
                          const GValue    *value)
 
1335
{
 
1336
  ClutterAnimatorKey *animator_key;
 
1337
 
 
1338
  g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), NULL);
 
1339
  g_return_val_if_fail (G_IS_OBJECT (object), NULL);
 
1340
  g_return_val_if_fail (property_name, NULL);
 
1341
  g_return_val_if_fail (value, NULL);
 
1342
 
 
1343
  property_name = g_intern_string (property_name);
 
1344
 
 
1345
  animator_key = clutter_animator_key_new (animator,
 
1346
                                           object, property_name,
 
1347
                                           progress,
 
1348
                                           mode);
 
1349
 
 
1350
  g_value_init (&animator_key->value, G_VALUE_TYPE (value));
 
1351
  g_value_copy (value, &animator_key->value);
 
1352
 
 
1353
  clutter_animator_set_key_internal (animator, animator_key);
 
1354
 
 
1355
  return animator;
 
1356
}
 
1357
 
 
1358
/**
 
1359
 * clutter_animator_get_keys:
 
1360
 * @animator: a #ClutterAnimator instance
 
1361
 * @object: (allow-none): a #GObject to search for, or %NULL for all objects
 
1362
 * @property_name: (allow-none): a specific property name to query for,
 
1363
 *   or %NULL for all properties
 
1364
 * @progress: a specific progress to search for, or a negative value for all
 
1365
 *   progresses
 
1366
 *
 
1367
 * Returns a list of pointers to opaque structures with accessor functions
 
1368
 * that describe the keys added to an animator.
 
1369
 *
 
1370
 * Return value: (transfer container) (element-type Clutter.AnimatorKey): a
 
1371
 *   list of #ClutterAnimatorKey<!-- -->s; the contents of the list are owned
 
1372
 *   by the #ClutterAnimator, but you should free the returned list when done,
 
1373
 *   using g_list_free()
 
1374
 *
 
1375
 * Since: 1.2
 
1376
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1377
 */
 
1378
GList *
 
1379
clutter_animator_get_keys (ClutterAnimator *animator,
 
1380
                           GObject         *object,
 
1381
                           const gchar     *property_name,
 
1382
                           gdouble          progress)
 
1383
{
 
1384
  GList *keys = NULL;
 
1385
  GList *k;
 
1386
 
 
1387
  g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), NULL);
 
1388
  g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL);
 
1389
 
 
1390
  property_name = g_intern_string (property_name);
 
1391
 
 
1392
  for (k = animator->priv->score; k; k = k->next)
 
1393
    {
 
1394
      ClutterAnimatorKey *key = k->data;
 
1395
 
 
1396
      if ((object == NULL || (object == key->object)) &&
 
1397
          (property_name == NULL || (property_name == key->property_name)) &&
 
1398
          (progress < 0  || fabs (progress - key->progress) < PROGRESS_EPSILON))
 
1399
        {
 
1400
          keys = g_list_prepend (keys, key);
 
1401
        }
 
1402
    }
 
1403
 
 
1404
  return g_list_reverse (keys);
 
1405
}
 
1406
 
 
1407
static void
 
1408
clutter_animator_remove_key_internal (ClutterAnimator *animator,
 
1409
                                      GObject         *object,
 
1410
                                      const gchar     *property_name,
 
1411
                                      gdouble          progress,
 
1412
                                      gboolean         is_inert)
 
1413
{
 
1414
  ClutterAnimatorPrivate *priv;
 
1415
  GList *k;
 
1416
 
 
1417
  g_return_if_fail (CLUTTER_IS_ANIMATOR (animator));
 
1418
  g_return_if_fail (object == NULL || G_IS_OBJECT (object));
 
1419
 
 
1420
  property_name = g_intern_string (property_name);
 
1421
 
 
1422
  priv = animator->priv;
 
1423
 
 
1424
again:
 
1425
  for (k = priv->score; k != NULL; k = k->next)
 
1426
    {
 
1427
      ClutterAnimatorKey *key = k->data;
 
1428
 
 
1429
      if ((object == NULL        || (object == key->object)) &&
 
1430
          (property_name == NULL || ((property_name == key->property_name))) &&
 
1431
          (progress < 0  || fabs (progress - key->progress) < PROGRESS_EPSILON)
 
1432
         )
 
1433
        {
 
1434
          ClutterAnimatorKey *prev_key = NULL;
 
1435
          key->is_inert = is_inert;
 
1436
 
 
1437
 
 
1438
          /* FIXME: non performant since we reiterate the list many times */
 
1439
 
 
1440
          prev_key = k->prev ? k->prev->data : NULL;
 
1441
 
 
1442
          if (!prev_key || prev_key->object   != key->object ||
 
1443
                           prev_key->property_name != key->property_name)
 
1444
            { /* We are removing the first key for a property ... */
 
1445
              ClutterAnimatorKey *next_key = k->next ? k->next->data : NULL;
 
1446
              if (next_key && next_key->object == key->object &&
 
1447
                              next_key->property_name == key->property_name)
 
1448
                {
 
1449
                  /* ... and there is a key of our own type following us,
 
1450
                   * copy interpolation/ease_in flags to the new first key
 
1451
                   */
 
1452
                  next_key->interpolation = key->interpolation;
 
1453
                  next_key->ease_in = key->ease_in;
 
1454
                }
 
1455
            }
 
1456
 
 
1457
          clutter_animator_key_free (key);
 
1458
          priv->score = g_list_remove (priv->score, key);
 
1459
          goto again;
 
1460
        }
 
1461
    }
 
1462
 
 
1463
  /* clear off cached state for all properties, this is regenerated in a
 
1464
   * correct state by animation_animator_started
 
1465
   */
 
1466
  g_hash_table_remove_all (priv->properties);
 
1467
 
 
1468
  /* if the animator is already running reinitialize internal iterators */
 
1469
  if (priv->timeline != NULL && clutter_timeline_is_playing (priv->timeline))
 
1470
    animation_animator_started (priv->timeline, animator);
 
1471
}
 
1472
 
 
1473
/**
 
1474
 * clutter_animator_remove_key:
 
1475
 * @animator: a #ClutterAnimator
 
1476
 * @object: (allow-none): a #GObject to search for, or %NULL for all
 
1477
 * @property_name: (allow-none): a specific property name to query for,
 
1478
 *   or %NULL for all
 
1479
 * @progress: a specific progress to search for or a negative value
 
1480
 *   for all
 
1481
 *
 
1482
 * Removes all keys matching the conditions specificed in the arguments.
 
1483
 *
 
1484
 * Since: 1.2
 
1485
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1486
 */
 
1487
void
 
1488
clutter_animator_remove_key (ClutterAnimator *animator,
 
1489
                             GObject         *object,
 
1490
                             const gchar     *property_name,
 
1491
                             gdouble          progress)
 
1492
{
 
1493
  clutter_animator_remove_key_internal (animator, object, property_name, 
 
1494
                                        progress, FALSE);
 
1495
}
 
1496
 
 
1497
 
 
1498
 
 
1499
 
 
1500
typedef struct _ParseClosure {
 
1501
  ClutterAnimator *animator;
 
1502
  ClutterScript *script;
 
1503
 
 
1504
  GValue *value;
 
1505
 
 
1506
  gboolean result;
 
1507
} ParseClosure;
 
1508
 
 
1509
static ClutterInterpolation
 
1510
resolve_interpolation (JsonNode *node)
 
1511
{
 
1512
  if ((JSON_NODE_TYPE (node) != JSON_NODE_VALUE))
 
1513
    return CLUTTER_INTERPOLATION_LINEAR;
 
1514
 
 
1515
  if (json_node_get_value_type (node) == G_TYPE_INT64)
 
1516
    {
 
1517
      return json_node_get_int (node);
 
1518
    }
 
1519
  else if (json_node_get_value_type (node) == G_TYPE_STRING)
 
1520
    {
 
1521
      const gchar *str = json_node_get_string (node);
 
1522
      gboolean res;
 
1523
      gint enum_value;
 
1524
 
 
1525
      res = _clutter_script_enum_from_string (CLUTTER_TYPE_INTERPOLATION,
 
1526
                                              str,
 
1527
                                              &enum_value);
 
1528
      if (res)
 
1529
        return enum_value;
 
1530
    }
 
1531
 
 
1532
  return CLUTTER_INTERPOLATION_LINEAR;
 
1533
}
 
1534
 
 
1535
static void
 
1536
parse_animator_property (JsonArray *array,
 
1537
                         guint      index_,
 
1538
                         JsonNode  *element,
 
1539
                         gpointer   data)
 
1540
{
 
1541
  ParseClosure *clos = data;
 
1542
  JsonObject *object;
 
1543
  JsonArray *keys;
 
1544
  GObject *gobject;
 
1545
  const gchar *id_, *pname;
 
1546
  GObjectClass *klass;
 
1547
  GParamSpec *pspec;
 
1548
  GSList *valid_keys = NULL;
 
1549
  GList *array_keys, *k;
 
1550
  ClutterInterpolation interpolation = CLUTTER_INTERPOLATION_LINEAR;
 
1551
  gboolean ease_in = FALSE;
 
1552
 
 
1553
  if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT)
 
1554
    {
 
1555
      g_warning ("The 'properties' member of a ClutterAnimator description "
 
1556
                 "should be an array of objects, but the element %d of the "
 
1557
                 "array is of type '%s'. The element will be ignored.",
 
1558
                 index_,
 
1559
                 json_node_type_name (element));
 
1560
      return;
 
1561
    }
 
1562
 
 
1563
  object = json_node_get_object (element);
 
1564
 
 
1565
  if (!json_object_has_member (object, "object") ||
 
1566
      !json_object_has_member (object, "name") ||
 
1567
      !json_object_has_member (object, "keys"))
 
1568
    {
 
1569
      g_warning ("The property description at index %d is missing one of "
 
1570
                 "the mandatory fields: object, name and keys",
 
1571
                 index_);
 
1572
      return;
 
1573
    }
 
1574
 
 
1575
  id_ = json_object_get_string_member (object, "object");
 
1576
  gobject = clutter_script_get_object (clos->script, id_);
 
1577
  if (gobject == NULL)
 
1578
    {
 
1579
      g_warning ("No object with id '%s' has been defined.", id_);
 
1580
      return;
 
1581
    }
 
1582
 
 
1583
  pname = json_object_get_string_member (object, "name");
 
1584
  klass = G_OBJECT_GET_CLASS (gobject);
 
1585
  pspec = g_object_class_find_property (klass, pname);
 
1586
  if (pspec == NULL)
 
1587
    {
 
1588
      g_warning ("The object of type '%s' and name '%s' has no "
 
1589
                 "property named '%s'",
 
1590
                 G_OBJECT_TYPE_NAME (gobject),
 
1591
                 id_,
 
1592
                 pname);
 
1593
      return;
 
1594
    }
 
1595
 
 
1596
  if (json_object_has_member (object, "ease-in"))
 
1597
    ease_in = json_object_get_boolean_member (object, "ease-in");
 
1598
 
 
1599
  if (json_object_has_member (object, "interpolation"))
 
1600
    {
 
1601
      JsonNode *node = json_object_get_member (object, "interpolation");
 
1602
 
 
1603
      interpolation = resolve_interpolation (node);
 
1604
    }
 
1605
 
 
1606
  keys = json_object_get_array_member (object, "keys");
 
1607
  if (keys == NULL)
 
1608
    {
 
1609
      g_warning ("The property description at index %d has an invalid "
 
1610
                 "key field of type '%s' when an array was expected.",
 
1611
                 index_,
 
1612
                 json_node_type_name (json_object_get_member (object, "keys")));
 
1613
      return;
 
1614
    }
 
1615
 
 
1616
  if (G_IS_VALUE (clos->value))
 
1617
    valid_keys = g_slist_reverse (g_value_get_pointer (clos->value));
 
1618
  else
 
1619
    g_value_init (clos->value, G_TYPE_POINTER);
 
1620
 
 
1621
  array_keys = json_array_get_elements (keys);
 
1622
  for (k = array_keys; k != NULL; k = k->next)
 
1623
    {
 
1624
      JsonNode *node = k->data;
 
1625
      JsonArray *key = json_node_get_array (node);
 
1626
      ClutterAnimatorKey *animator_key;
 
1627
      gdouble progress;
 
1628
      gulong mode;
 
1629
      gboolean res;
 
1630
 
 
1631
      progress = json_array_get_double_element (key, 0);
 
1632
      mode = _clutter_script_resolve_animation_mode (json_array_get_element (key, 1));
 
1633
 
 
1634
      animator_key = clutter_animator_key_new (clos->animator,
 
1635
                                               gobject,
 
1636
                                               pname,
 
1637
                                               progress,
 
1638
                                               mode);
 
1639
 
 
1640
      res = _clutter_script_parse_node (clos->script,
 
1641
                                        &(animator_key->value),
 
1642
                                        pname,
 
1643
                                        json_array_get_element (key, 2),
 
1644
                                        pspec);
 
1645
      if (!res)
 
1646
        {
 
1647
          g_warning ("Unable to parse the key value for the "
 
1648
                     "property '%s' (progress: %.2f) at index %d",
 
1649
                     pname,
 
1650
                     progress,
 
1651
                     index_);
 
1652
          continue;
 
1653
        }
 
1654
 
 
1655
      animator_key->ease_in = ease_in;
 
1656
      animator_key->interpolation = interpolation;
 
1657
 
 
1658
      valid_keys = g_slist_prepend (valid_keys, animator_key);
 
1659
    }
 
1660
 
 
1661
  g_list_free (array_keys);
 
1662
 
 
1663
  g_value_set_pointer (clos->value, g_slist_reverse (valid_keys));
 
1664
 
 
1665
  clos->result = TRUE;
 
1666
}
 
1667
 
 
1668
static gboolean
 
1669
clutter_animator_parse_custom_node (ClutterScriptable *scriptable,
 
1670
                                    ClutterScript     *script,
 
1671
                                    GValue            *value,
 
1672
                                    const gchar       *name,
 
1673
                                    JsonNode          *node)
 
1674
{
 
1675
  ParseClosure parse_closure;
 
1676
 
 
1677
  if (strcmp (name, "properties") != 0)
 
1678
    return FALSE;
 
1679
 
 
1680
  if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
 
1681
    return FALSE;
 
1682
 
 
1683
  parse_closure.animator = CLUTTER_ANIMATOR (scriptable);
 
1684
  parse_closure.script = script;
 
1685
  parse_closure.value = value;
 
1686
  parse_closure.result = FALSE;
 
1687
 
 
1688
  json_array_foreach_element (json_node_get_array (node),
 
1689
                              parse_animator_property,
 
1690
                              &parse_closure);
 
1691
 
 
1692
  /* we return TRUE if we had at least one key parsed */
 
1693
 
 
1694
  return parse_closure.result;
 
1695
}
 
1696
 
 
1697
static void
 
1698
clutter_animator_set_custom_property (ClutterScriptable *scriptable,
 
1699
                                      ClutterScript     *script,
 
1700
                                      const gchar       *name,
 
1701
                                      const GValue      *value)
 
1702
{
 
1703
  if (strcmp (name, "properties") == 0)
 
1704
    {
 
1705
      ClutterAnimator *animator = CLUTTER_ANIMATOR (scriptable);
 
1706
      GSList *keys = g_value_get_pointer (value);
 
1707
      GSList *k;
 
1708
 
 
1709
      for (k = keys; k != NULL; k = k->next)
 
1710
        clutter_animator_set_key_internal (animator, k->data);
 
1711
 
 
1712
      g_slist_free (keys);
 
1713
    }
 
1714
  else
 
1715
    g_object_set_property (G_OBJECT (scriptable), name, value);
 
1716
}
 
1717
 
 
1718
static void
 
1719
clutter_scriptable_init (ClutterScriptableIface *iface)
 
1720
{
 
1721
  iface->parse_custom_node = clutter_animator_parse_custom_node;
 
1722
  iface->set_custom_property = clutter_animator_set_custom_property;
 
1723
}
 
1724
 
 
1725
static void
 
1726
clutter_animator_set_property (GObject      *gobject,
 
1727
                               guint         prop_id,
 
1728
                               const GValue *value,
 
1729
                               GParamSpec   *pspec)
 
1730
{
 
1731
  ClutterAnimator *self = CLUTTER_ANIMATOR (gobject);
 
1732
 
 
1733
  switch (prop_id)
 
1734
    {
 
1735
    case PROP_DURATION:
 
1736
      clutter_animator_set_duration (self, g_value_get_uint (value));
 
1737
      break;
 
1738
 
 
1739
    case PROP_TIMELINE:
 
1740
      clutter_animator_set_timeline (self, g_value_get_object (value));
 
1741
      break;
 
1742
 
 
1743
    default:
 
1744
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
 
1745
      break;
 
1746
    }
 
1747
}
 
1748
 
 
1749
static void
 
1750
clutter_animator_get_property (GObject    *gobject,
 
1751
                               guint       prop_id,
 
1752
                               GValue     *value,
 
1753
                               GParamSpec *pspec)
 
1754
{
 
1755
  ClutterAnimatorPrivate *priv = CLUTTER_ANIMATOR (gobject)->priv;
 
1756
 
 
1757
  switch (prop_id)
 
1758
    {
 
1759
    case PROP_DURATION:
 
1760
      g_value_set_uint (value, clutter_timeline_get_duration (priv->timeline));
 
1761
      break;
 
1762
 
 
1763
    case PROP_TIMELINE:
 
1764
      g_value_set_object (value, priv->timeline);
 
1765
      break;
 
1766
 
 
1767
    default:
 
1768
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
 
1769
      break;
 
1770
    }
 
1771
}
 
1772
 
 
1773
static void
 
1774
clutter_animator_class_init (ClutterAnimatorClass *klass)
 
1775
{
 
1776
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
1777
 
 
1778
  gobject_class->set_property = clutter_animator_set_property;
 
1779
  gobject_class->get_property = clutter_animator_get_property;
 
1780
  gobject_class->dispose = clutter_animator_dispose;
 
1781
  gobject_class->finalize = clutter_animator_finalize;
 
1782
 
 
1783
  /**
 
1784
   * ClutterAnimator:duration:
 
1785
   *
 
1786
   * The duration of the #ClutterTimeline used by the #ClutterAnimator
 
1787
   * to drive the animation
 
1788
   *
 
1789
   * Since: 1.2
 
1790
   * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1791
   */
 
1792
  obj_props[PROP_DURATION] =
 
1793
    g_param_spec_uint ("duration",
 
1794
                       P_("Duration"),
 
1795
                       P_("The duration of the animation"),
 
1796
                       0, G_MAXUINT,
 
1797
                       2000,
 
1798
                       CLUTTER_PARAM_READWRITE);
 
1799
 
 
1800
  /**
 
1801
   * ClutterAnimator:timeline:
 
1802
   *
 
1803
   * The #ClutterTimeline used by the #ClutterAnimator to drive the
 
1804
   * animation
 
1805
   *
 
1806
   * Since: 1.2
 
1807
   * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1808
   */
 
1809
  obj_props[PROP_TIMELINE] =
 
1810
    g_param_spec_object ("timeline",
 
1811
                         P_("Timeline"),
 
1812
                         P_("The timeline of the animation"),
 
1813
                         CLUTTER_TYPE_TIMELINE,
 
1814
                         CLUTTER_PARAM_READWRITE);
 
1815
 
 
1816
  g_object_class_install_properties (gobject_class,
 
1817
                                     PROP_LAST,
 
1818
                                     obj_props);
 
1819
}
 
1820
 
 
1821
static void
 
1822
clutter_animator_init (ClutterAnimator *animator)
 
1823
{
 
1824
  ClutterAnimatorPrivate *priv;
 
1825
  ClutterTimeline *timeline;
 
1826
 
 
1827
  animator->priv = priv = clutter_animator_get_instance_private (animator);
 
1828
 
 
1829
  priv->properties = g_hash_table_new_full (prop_actor_hash,
 
1830
                                            prop_actor_equal,
 
1831
                                            prop_actor_key_free,
 
1832
                                            property_iter_free);
 
1833
 
 
1834
  timeline = clutter_timeline_new (2000);
 
1835
  clutter_animator_set_timeline (animator, timeline);
 
1836
  g_object_unref (timeline);
 
1837
 
 
1838
  priv->slave_timeline = clutter_timeline_new (10000);
 
1839
}
 
1840
 
 
1841
 
 
1842
/**
 
1843
 * clutter_animator_property_get_ease_in:
 
1844
 * @animator: a #ClutterAnimatorKey
 
1845
 * @object: a #GObject
 
1846
 * @property_name: the name of a property on object
 
1847
 *
 
1848
 * Checks if a property value is to be eased into the animation.
 
1849
 *
 
1850
 * Return value: %TRUE if the property is eased in
 
1851
 *
 
1852
 * Since: 1.2
 
1853
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1854
 */
 
1855
gboolean
 
1856
clutter_animator_property_get_ease_in (ClutterAnimator *animator,
 
1857
                                       GObject         *object,
 
1858
                                       const gchar     *property_name)
 
1859
{
 
1860
  ClutterAnimatorKey  key, *initial_key;
 
1861
  GList              *initial;
 
1862
 
 
1863
  g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), FALSE);
 
1864
  g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
 
1865
  g_return_val_if_fail (property_name, FALSE);
 
1866
 
 
1867
  key.object        = object;
 
1868
  key.property_name = g_intern_string (property_name);
 
1869
  initial = g_list_find_custom (animator->priv->score, &key,
 
1870
                                sort_actor_prop_func);
 
1871
  if (initial != NULL)
 
1872
    {
 
1873
      initial_key = initial->data;
 
1874
 
 
1875
      return initial_key->ease_in;
 
1876
    }
 
1877
 
 
1878
  return FALSE;
 
1879
}
 
1880
 
 
1881
/**
 
1882
 * clutter_animator_property_set_ease_in:
 
1883
 * @animator: a #ClutterAnimatorKey
 
1884
 * @object: a #GObject
 
1885
 * @property_name: the name of a property on object
 
1886
 * @ease_in: we are going to be easing in this property
 
1887
 *
 
1888
 * Sets whether a property value is to be eased into the animation.
 
1889
 *
 
1890
 * Since: 1.2
 
1891
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1892
 */
 
1893
void
 
1894
clutter_animator_property_set_ease_in (ClutterAnimator *animator,
 
1895
                                       GObject         *object,
 
1896
                                       const gchar     *property_name,
 
1897
                                       gboolean         ease_in)
 
1898
{
 
1899
  ClutterAnimatorKey  key, *initial_key;
 
1900
  GList              *initial;
 
1901
 
 
1902
  g_return_if_fail (CLUTTER_IS_ANIMATOR (animator));
 
1903
  g_return_if_fail (G_IS_OBJECT (object));
 
1904
  g_return_if_fail (property_name);
 
1905
 
 
1906
  key.object        = object;
 
1907
  key.property_name = g_intern_string (property_name);
 
1908
  initial = g_list_find_custom (animator->priv->score, &key,
 
1909
                                sort_actor_prop_func);
 
1910
  if (initial)
 
1911
    {
 
1912
      initial_key = initial->data;
 
1913
      initial_key->ease_in = ease_in;
 
1914
    }
 
1915
  else
 
1916
    g_warning ("The animator has no object of type '%s' with a "
 
1917
               "property named '%s'",
 
1918
               G_OBJECT_TYPE_NAME (object),
 
1919
               property_name);
 
1920
}
 
1921
 
 
1922
 
 
1923
/**
 
1924
 * clutter_animator_property_get_interpolation:
 
1925
 * @animator: a #ClutterAnimatorKey
 
1926
 * @object: a #GObject
 
1927
 * @property_name: the name of a property on object
 
1928
 *
 
1929
 * Get the interpolation used by animator for a property on a particular
 
1930
 * object.
 
1931
 *
 
1932
 * Returns: a ClutterInterpolation value.
 
1933
 * Since: 1.2
 
1934
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1935
 */
 
1936
ClutterInterpolation
 
1937
clutter_animator_property_get_interpolation (ClutterAnimator *animator,
 
1938
                                             GObject         *object,
 
1939
                                             const gchar     *property_name)
 
1940
{
 
1941
  GList              *initial;
 
1942
  ClutterAnimatorKey  key, *initial_key;
 
1943
 
 
1944
  g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator),
 
1945
                        CLUTTER_INTERPOLATION_LINEAR);
 
1946
  g_return_val_if_fail (G_IS_OBJECT (object),
 
1947
                        CLUTTER_INTERPOLATION_LINEAR);
 
1948
  g_return_val_if_fail (property_name,
 
1949
                        CLUTTER_INTERPOLATION_LINEAR);
 
1950
 
 
1951
  key.object        = object;
 
1952
  key.property_name = g_intern_string (property_name);
 
1953
  initial = g_list_find_custom (animator->priv->score, &key,
 
1954
                                sort_actor_prop_func);
 
1955
  if (initial)
 
1956
    {
 
1957
      initial_key = initial->data;
 
1958
 
 
1959
      return initial_key->interpolation;
 
1960
    }
 
1961
 
 
1962
  return CLUTTER_INTERPOLATION_LINEAR;
 
1963
}
 
1964
 
 
1965
/**
 
1966
 * clutter_animator_property_set_interpolation:
 
1967
 * @animator: a #ClutterAnimatorKey
 
1968
 * @object: a #GObject
 
1969
 * @property_name: the name of a property on object
 
1970
 * @interpolation: the #ClutterInterpolation to use
 
1971
 *
 
1972
 * Set the interpolation method to use, %CLUTTER_INTERPOLATION_LINEAR causes
 
1973
 * the values to linearly change between the values, and
 
1974
 * %CLUTTER_INTERPOLATION_CUBIC causes the values to smoothly change between
 
1975
 * the values.
 
1976
 *
 
1977
 * Since: 1.2
 
1978
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
1979
 */
 
1980
void
 
1981
clutter_animator_property_set_interpolation (ClutterAnimator     *animator,
 
1982
                                             GObject             *object,
 
1983
                                             const gchar         *property_name,
 
1984
                                             ClutterInterpolation interpolation)
 
1985
{
 
1986
  GList              *initial;
 
1987
  ClutterAnimatorKey  key, *initial_key;
 
1988
 
 
1989
  g_return_if_fail (CLUTTER_IS_ANIMATOR (animator));
 
1990
  g_return_if_fail (G_IS_OBJECT (object));
 
1991
  g_return_if_fail (property_name);
 
1992
 
 
1993
  key.object        = object;
 
1994
  key.property_name = g_intern_string (property_name);
 
1995
  initial = g_list_find_custom (animator->priv->score, &key,
 
1996
                                sort_actor_prop_func);
 
1997
  if (initial)
 
1998
    {
 
1999
      initial_key = initial->data;
 
2000
      initial_key->interpolation = interpolation;
 
2001
    }
 
2002
}
 
2003
 
 
2004
G_DEFINE_BOXED_TYPE (ClutterAnimatorKey, clutter_animator_key,
 
2005
                     clutter_animator_key_copy,
 
2006
                     clutter_animator_key_free);
 
2007
 
 
2008
/**
 
2009
 * clutter_animator_key_get_object:
 
2010
 * @key: a #ClutterAnimatorKey
 
2011
 *
 
2012
 * Retrieves the object a key applies to.
 
2013
 *
 
2014
 * Return value: (transfer none): the object an animator_key exist for.
 
2015
 *
 
2016
 * Since: 1.2
 
2017
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
2018
 */
 
2019
GObject *
 
2020
clutter_animator_key_get_object (const ClutterAnimatorKey *key)
 
2021
{
 
2022
  g_return_val_if_fail (key != NULL, NULL);
 
2023
 
 
2024
  return key->object;
 
2025
}
 
2026
 
 
2027
/**
 
2028
 * clutter_animator_key_get_property_name:
 
2029
 * @key: a #ClutterAnimatorKey
 
2030
 *
 
2031
 * Retrieves the name of the property a key applies to.
 
2032
 *
 
2033
 * Return value: the name of the property an animator_key exist for.
 
2034
 *
 
2035
 * Since: 1.2
 
2036
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
2037
 */
 
2038
const gchar *
 
2039
clutter_animator_key_get_property_name (const ClutterAnimatorKey *key)
 
2040
{
 
2041
  g_return_val_if_fail (key != NULL, NULL);
 
2042
 
 
2043
  return key->property_name;
 
2044
}
 
2045
 
 
2046
/**
 
2047
 * clutter_animator_key_get_property_type:
 
2048
 * @key: a #ClutterAnimatorKey
 
2049
 *
 
2050
 * Retrieves the #GType of the property a key applies to
 
2051
 *
 
2052
 * You can use this type to initialize the #GValue to pass to
 
2053
 * clutter_animator_key_get_value()
 
2054
 *
 
2055
 * Return value: the #GType of the property
 
2056
 *
 
2057
 * Since: 1.2
 
2058
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
2059
 */
 
2060
GType
 
2061
clutter_animator_key_get_property_type (const ClutterAnimatorKey *key)
 
2062
{
 
2063
  g_return_val_if_fail (key != NULL, G_TYPE_INVALID);
 
2064
 
 
2065
  return G_VALUE_TYPE (&key->value);
 
2066
}
 
2067
 
 
2068
/**
 
2069
 * clutter_animator_key_get_mode:
 
2070
 * @key: a #ClutterAnimatorKey
 
2071
 *
 
2072
 * Retrieves the mode of a #ClutterAnimator key, for the first key of a
 
2073
 * property for an object this represents the whether the animation is
 
2074
 * open ended and or curved for the remainding keys for the property it
 
2075
 * represents the easing mode.
 
2076
 *
 
2077
 * Return value: the mode of a #ClutterAnimatorKey
 
2078
 *
 
2079
 * Since: 1.2
 
2080
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
2081
 */
 
2082
gulong
 
2083
clutter_animator_key_get_mode (const ClutterAnimatorKey *key)
 
2084
{
 
2085
  g_return_val_if_fail (key != NULL, 0);
 
2086
 
 
2087
  return key->mode;
 
2088
}
 
2089
 
 
2090
/**
 
2091
 * clutter_animator_key_get_progress:
 
2092
 * @key: a #ClutterAnimatorKey
 
2093
 *
 
2094
 * Retrieves the progress of an clutter_animator_key
 
2095
 *
 
2096
 * Return value: the progress defined for a #ClutterAnimator key.
 
2097
 *
 
2098
 * Since: 1.2
 
2099
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
2100
 */
 
2101
gdouble
 
2102
clutter_animator_key_get_progress (const ClutterAnimatorKey *key)
 
2103
{
 
2104
  g_return_val_if_fail (key != NULL, 0.0);
 
2105
 
 
2106
  return key->progress;
 
2107
}
 
2108
 
 
2109
/**
 
2110
 * clutter_animator_key_get_value:
 
2111
 * @key: a #ClutterAnimatorKey
 
2112
 * @value: a #GValue initialized with the correct type for the animator key
 
2113
 *
 
2114
 * Retrieves a copy of the value for a #ClutterAnimatorKey.
 
2115
 *
 
2116
 * The passed in #GValue needs to be already initialized for the value
 
2117
 * type of the key or to a type that allow transformation from the value
 
2118
 * type of the key.
 
2119
 *
 
2120
 * Use g_value_unset() when done.
 
2121
 *
 
2122
 * Return value: %TRUE if the passed #GValue was successfully set, and
 
2123
 *   %FALSE otherwise
 
2124
 *
 
2125
 * Since: 1.2
 
2126
 * Deprecated: 1.12: Use #ClutterKeyframeTransition instead
 
2127
 */
 
2128
gboolean
 
2129
clutter_animator_key_get_value (const ClutterAnimatorKey *key,
 
2130
                                GValue                   *value)
 
2131
{
 
2132
  g_return_val_if_fail (key != NULL, FALSE);
 
2133
  g_return_val_if_fail (value != NULL, FALSE);
 
2134
  g_return_val_if_fail (G_VALUE_TYPE (value) != G_TYPE_INVALID, FALSE);
 
2135
 
 
2136
  if (!g_type_is_a (G_VALUE_TYPE (&key->value), G_VALUE_TYPE (value)))
 
2137
    {
 
2138
      if (g_value_type_compatible (G_VALUE_TYPE (&key->value),
 
2139
                                   G_VALUE_TYPE (value)))
 
2140
        {
 
2141
          g_value_copy (&key->value, value);
 
2142
          return TRUE;
 
2143
        }
 
2144
 
 
2145
      if (g_value_type_transformable (G_VALUE_TYPE (&key->value),
 
2146
                                      G_VALUE_TYPE (value)))
 
2147
        {
 
2148
          if (g_value_transform (&key->value, value))
 
2149
            return TRUE;
 
2150
        }
 
2151
 
 
2152
      g_warning ("%s: Unable to convert from %s to %s for the "
 
2153
                 "property '%s' of object %s in the animator key",
 
2154
                 G_STRLOC,
 
2155
                 g_type_name (G_VALUE_TYPE (&key->value)),
 
2156
                 g_type_name (G_VALUE_TYPE (value)),
 
2157
                 key->property_name,
 
2158
                 G_OBJECT_TYPE_NAME (key->object));
 
2159
 
 
2160
      return FALSE;
 
2161
    }
 
2162
  else
 
2163
    g_value_copy (&key->value, value);
 
2164
 
 
2165
  return TRUE;
 
2166
}