~ubuntu-branches/ubuntu/vivid/clutter-1.0/vivid-proposed

« back to all changes in this revision

Viewing changes to clutter/clutter-timeline.c

  • Committer: Package Import Robot
  • Author(s): Michael Biebl
  • Date: 2012-05-01 23:50:39 UTC
  • mfrom: (4.1.22 experimental)
  • Revision ID: package-import@ubuntu.com-20120501235039-7wehcmtr33nqhv67
Tags: 1.10.4-2
Upload to unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
/**
27
27
 * SECTION:clutter-timeline
28
28
 * @short_description: A class for time-based events
29
 
 *
30
 
 * #ClutterTimeline is a base class for managing time based events such
31
 
 * as animations.
 
29
 * @see_also: #ClutterAnimation, #ClutterAnimator, #ClutterState
 
30
 *
 
31
 * #ClutterTimeline is a base class for managing time-based event that cause
 
32
 * Clutter to redraw a stage, such as animations.
 
33
 *
 
34
 * Each #ClutterTimeline instance has a duration: once a timeline has been
 
35
 * started, using clutter_timeline_start(), it will emit a signal that can
 
36
 * be used to update the state of the actors.
 
37
 *
 
38
 * It is important to note that #ClutterTimeline is not a generic API for
 
39
 * calling closures after an interval; each Timeline is tied into the master
 
40
 * clock used to drive the frame cycle. If you need to schedule a closure
 
41
 * after an interval, see clutter_threads_add_timeout() instead.
 
42
 *
 
43
 * Users of #ClutterTimeline should connect to the #ClutterTimeline::new-frame
 
44
 * signal, which is emitted each time a timeline is advanced during the maste
 
45
 * clock iteration. The #ClutterTimeline::new-frame signal provides the time
 
46
 * elapsed since the beginning of the timeline, in milliseconds. A normalized
 
47
 * progress value can be obtained by calling clutter_timeline_get_progress().
 
48
 * By using clutter_timeline_get_delta() it is possible to obtain the wallclock
 
49
 * time elapsed since the last emission of the #ClutterTimeline::new-frame
 
50
 * signal.
 
51
 *
 
52
 * Initial state can be set up by using the #ClutterTimeline::started signal,
 
53
 * while final state can be set up by using the #ClutterTimeline::completed
 
54
 * signal. The #ClutterTimeline guarantees the emission of at least a single
 
55
 * #ClutterTimeline::new-frame signal, as well as the emission of the
 
56
 * #ClutterTimeline::completed signal.
 
57
 *
 
58
 * It is possible to connect to specific points in the timeline progress by
 
59
 * adding <emphasis>markers</emphasis> using clutter_timeline_add_marker_at_time()
 
60
 * and connecting to the #ClutterTimeline::marker-reached signal.
 
61
 *
 
62
 * Timelines can be made to loop once they reach the end of their duration, by
 
63
 * using clutter_timeline_set_repeat_count(); a looping timeline will still
 
64
 * emit the #ClutterTimeline::completed signal once it reaches the end of its
 
65
 * duration.
 
66
 *
 
67
 * Timelines have a #ClutterTimeline:direction: the default direction is
 
68
 * %CLUTTER_TIMELINE_FORWARD, and goes from 0 to the duration; it is possible
 
69
 * to change the direction to %CLUTTER_TIMELINE_BACKWARD, and have the timeline
 
70
 * go from the duration to 0. The direction can be automatically reversed
 
71
 * when reaching completion by using the #ClutterTimeline:auto-reverse property.
 
72
 *
 
73
 * Timelines are used in the Clutter animation framework by classes like
 
74
 * #ClutterAnimation, #ClutterAnimator, and #ClutterState.
 
75
 *
 
76
 * <refsect2 id="timeline-script">
 
77
 *  <title>Defining Timelines in ClutterScript</title>
 
78
 *  <para>A #ClutterTimeline can be described in #ClutterScript like any
 
79
 *  other object. Additionally, it is possible to define markers directly
 
80
 *  inside the JSON definition by using the <emphasis>markers</emphasis>
 
81
 *  JSON object member, such as:</para>
 
82
 *  <informalexample><programlisting><![CDATA[
 
83
{
 
84
  "type" : "ClutterTimeline",
 
85
  "duration" : 1000,
 
86
  "markers" : [
 
87
    { "name" : "quarter", "time" : 250 },
 
88
    { "name" : "half-time", "time" : 500 },
 
89
    { "name" : "three-quarters", "time" : 750 }
 
90
  ]
 
91
}
 
92
 *  ]]></programlisting></informalexample>
 
93
 * </refsect2>
32
94
 */
33
95
 
34
96
#ifdef HAVE_CONFIG_H
35
97
#include "config.h"
36
98
#endif
37
99
 
 
100
#include "clutter-timeline.h"
 
101
 
38
102
#include "clutter-debug.h"
 
103
#include "clutter-easing.h"
39
104
#include "clutter-enum-types.h"
40
105
#include "clutter-main.h"
41
106
#include "clutter-marshal.h"
42
107
#include "clutter-master-clock.h"
43
108
#include "clutter-private.h"
44
 
#include "clutter-timeline.h"
45
 
 
46
 
G_DEFINE_TYPE (ClutterTimeline, clutter_timeline, G_TYPE_OBJECT);
 
109
#include "clutter-scriptable.h"
 
110
 
 
111
#include "deprecated/clutter-timeline.h"
 
112
 
 
113
static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
 
114
 
 
115
G_DEFINE_TYPE_WITH_CODE (ClutterTimeline, clutter_timeline, G_TYPE_OBJECT,
 
116
                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
 
117
                                                clutter_scriptable_iface_init));
47
118
 
48
119
struct _ClutterTimelinePrivate
49
120
{
66
137
  /* Time we last advanced the elapsed time and showed a frame */
67
138
  gint64 last_frame_time;
68
139
 
69
 
  guint loop               : 1;
 
140
  /* How many times the timeline should repeat */
 
141
  gint repeat_count;
 
142
 
 
143
  /* The number of times the timeline has repeated */
 
144
  gint current_repeat;
 
145
 
 
146
  ClutterTimelineProgressFunc progress_func;
 
147
  gpointer progress_data;
 
148
  GDestroyNotify progress_notify;
 
149
  ClutterAnimationMode progress_mode;
 
150
 
70
151
  guint is_playing         : 1;
71
152
 
72
153
  /* If we've just started playing and haven't yet gotten
91
172
  PROP_DURATION,
92
173
  PROP_DIRECTION,
93
174
  PROP_AUTO_REVERSE,
 
175
  PROP_REPEAT_COUNT,
 
176
  PROP_PROGRESS_MODE,
94
177
 
95
178
  PROP_LAST
96
179
};
135
218
    }
136
219
}
137
220
 
 
221
/*< private >
 
222
 * clutter_timeline_add_marker_internal:
 
223
 * @timeline: a #ClutterTimeline
 
224
 * @marker: a TimelineMarker
 
225
 *
 
226
 * Adds @marker into the hash table of markers for @timeline.
 
227
 *
 
228
 * The TimelineMarker will either be added or, in case of collisions
 
229
 * with another existing marker, freed. In any case, this function
 
230
 * assumes the ownership of the passed @marker.
 
231
 */
 
232
static inline void
 
233
clutter_timeline_add_marker_internal (ClutterTimeline *timeline,
 
234
                                      TimelineMarker  *marker)
 
235
{
 
236
  ClutterTimelinePrivate *priv = timeline->priv;
 
237
  TimelineMarker *old_marker;
 
238
 
 
239
  /* create the hash table that will hold the markers */
 
240
  if (G_UNLIKELY (priv->markers_by_name == NULL))
 
241
    priv->markers_by_name = g_hash_table_new_full (g_str_hash, g_str_equal,
 
242
                                                   NULL,
 
243
                                                   timeline_marker_free);
 
244
 
 
245
  old_marker = g_hash_table_lookup (priv->markers_by_name, marker->name);
 
246
  if (old_marker != NULL)
 
247
    {
 
248
      g_warning ("A marker named '%s' already exists at time %d",
 
249
                 old_marker->name,
 
250
                 old_marker->msecs);
 
251
      timeline_marker_free (marker);
 
252
      return;
 
253
    }
 
254
 
 
255
  g_hash_table_insert (priv->markers_by_name, marker->name, marker);
 
256
}
 
257
 
 
258
static inline void
 
259
clutter_timeline_set_loop_internal (ClutterTimeline *timeline,
 
260
                                    gboolean         loop)
 
261
{
 
262
  gint old_repeat_count;
 
263
 
 
264
  old_repeat_count = timeline->priv->repeat_count;
 
265
 
 
266
  if (loop)
 
267
    clutter_timeline_set_repeat_count (timeline, -1);
 
268
  else
 
269
    clutter_timeline_set_repeat_count (timeline, 0);
 
270
 
 
271
  if (old_repeat_count != timeline->priv->repeat_count)
 
272
    g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_LOOP]);
 
273
}
 
274
 
 
275
/* Scriptable */
 
276
typedef struct _ParseClosure {
 
277
  ClutterTimeline *timeline;
 
278
  ClutterScript *script;
 
279
  GValue *value;
 
280
  gboolean result;
 
281
} ParseClosure;
 
282
 
 
283
static void
 
284
parse_timeline_markers (JsonArray *array,
 
285
                        guint      index_,
 
286
                        JsonNode  *element,
 
287
                        gpointer   data)
 
288
{
 
289
  ParseClosure *clos = data;
 
290
  JsonObject *object;
 
291
  TimelineMarker *marker;
 
292
  GList *markers;
 
293
 
 
294
  if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT)
 
295
    {
 
296
      g_warning ("The 'markers' member of a ClutterTimeline description "
 
297
                 "should be an array of objects, but the element %d of the "
 
298
                 "array is of type '%s'. The element will be ignored.",
 
299
                 index_,
 
300
                 json_node_type_name (element));
 
301
      return;
 
302
    }
 
303
 
 
304
  object = json_node_get_object (element);
 
305
 
 
306
  if (!(json_object_has_member (object, "name") &&
 
307
        json_object_has_member (object, "time")))
 
308
    {
 
309
      g_warning ("The marker definition in a ClutterTimeline description "
 
310
                 "must be an object with the 'name' and 'time' members, "
 
311
                 "but the element %d of the 'markers' array does not have "
 
312
                 "either",
 
313
                 index_);
 
314
      return;
 
315
    }
 
316
 
 
317
  if (G_IS_VALUE (clos->value))
 
318
    markers = g_value_get_pointer (clos->value);
 
319
  else
 
320
    {
 
321
      g_value_init (clos->value, G_TYPE_POINTER);
 
322
      markers = NULL;
 
323
    }
 
324
 
 
325
  marker = timeline_marker_new (json_object_get_string_member (object, "name"),
 
326
                                json_object_get_int_member (object, "time"));
 
327
 
 
328
  markers = g_list_prepend (markers, marker);
 
329
 
 
330
  g_value_set_pointer (clos->value, markers);
 
331
 
 
332
  clos->result = TRUE;
 
333
}
 
334
 
 
335
static gboolean
 
336
clutter_timeline_parse_custom_node (ClutterScriptable *scriptable,
 
337
                                    ClutterScript     *script,
 
338
                                    GValue            *value,
 
339
                                    const gchar       *name,
 
340
                                    JsonNode          *node)
 
341
{
 
342
  ParseClosure clos;
 
343
 
 
344
  if (strcmp (name, "markers") != 0)
 
345
    return FALSE;
 
346
 
 
347
  if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
 
348
    return FALSE;
 
349
 
 
350
  clos.timeline = CLUTTER_TIMELINE (scriptable);
 
351
  clos.script = script;
 
352
  clos.value = value;
 
353
  clos.result = FALSE;
 
354
 
 
355
  json_array_foreach_element (json_node_get_array (node),
 
356
                              parse_timeline_markers,
 
357
                              &clos);
 
358
 
 
359
  return clos.result;
 
360
}
 
361
 
 
362
static void
 
363
clutter_timeline_set_custom_property (ClutterScriptable *scriptable,
 
364
                                      ClutterScript     *script,
 
365
                                      const gchar       *name,
 
366
                                      const GValue      *value)
 
367
{
 
368
  if (strcmp (name, "markers") == 0)
 
369
    {
 
370
      ClutterTimeline *timeline = CLUTTER_TIMELINE (scriptable);
 
371
      GList *markers = g_value_get_pointer (value);
 
372
      GList *m;
 
373
 
 
374
      /* the list was created through prepend() */
 
375
      markers = g_list_reverse (markers);
 
376
 
 
377
      for (m = markers; m != NULL; m = m->next)
 
378
        clutter_timeline_add_marker_internal (timeline, m->data);
 
379
 
 
380
      g_list_free (markers);
 
381
    }
 
382
  else
 
383
    g_object_set_property (G_OBJECT (scriptable), name, value);
 
384
}
 
385
 
 
386
 
 
387
static void
 
388
clutter_scriptable_iface_init (ClutterScriptableIface *iface)
 
389
{
 
390
  iface->parse_custom_node = clutter_timeline_parse_custom_node;
 
391
  iface->set_custom_property = clutter_timeline_set_custom_property;
 
392
}
 
393
 
138
394
/* Object */
139
395
 
140
396
static void
148
404
  switch (prop_id)
149
405
    {
150
406
    case PROP_LOOP:
151
 
      clutter_timeline_set_loop (timeline, g_value_get_boolean (value));
 
407
      clutter_timeline_set_loop_internal (timeline, g_value_get_boolean (value));
152
408
      break;
153
409
 
154
410
    case PROP_DELAY:
167
423
      clutter_timeline_set_auto_reverse (timeline, g_value_get_boolean (value));
168
424
      break;
169
425
 
 
426
    case PROP_REPEAT_COUNT:
 
427
      clutter_timeline_set_repeat_count (timeline, g_value_get_int (value));
 
428
      break;
 
429
 
 
430
    case PROP_PROGRESS_MODE:
 
431
      clutter_timeline_set_progress_mode (timeline, g_value_get_enum (value));
 
432
      break;
 
433
 
170
434
    default:
171
435
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
172
436
      break;
185
449
  switch (prop_id)
186
450
    {
187
451
    case PROP_LOOP:
188
 
      g_value_set_boolean (value, priv->loop);
 
452
      g_value_set_boolean (value, priv->repeat_count != 0);
189
453
      break;
190
454
 
191
455
    case PROP_DELAY:
204
468
      g_value_set_boolean (value, priv->auto_reverse);
205
469
      break;
206
470
 
 
471
    case PROP_REPEAT_COUNT:
 
472
      g_value_set_int (value, priv->repeat_count);
 
473
      break;
 
474
 
 
475
    case PROP_PROGRESS_MODE:
 
476
      g_value_set_enum (value, priv->progress_mode);
 
477
      break;
 
478
 
207
479
    default:
208
480
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
209
481
      break;
243
515
      priv->delay_id = 0;
244
516
    }
245
517
 
 
518
  if (priv->progress_notify != NULL)
 
519
    {
 
520
      priv->progress_notify (priv->progress_data);
 
521
      priv->progress_func = NULL;
 
522
      priv->progress_data = NULL;
 
523
      priv->progress_notify = NULL;
 
524
    }
 
525
 
246
526
  G_OBJECT_CLASS (clutter_timeline_parent_class)->dispose (object);
247
527
}
248
528
 
257
537
   * ClutterTimeline:loop:
258
538
   *
259
539
   * Whether the timeline should automatically rewind and restart.
 
540
   *
 
541
   * As a side effect, setting this property to %TRUE will set the
 
542
   * #ClutterTimeline:repeat-count property to -1, while setting this
 
543
   * property to %FALSE will set the #ClutterTimeline:repeat-count
 
544
   * property to 0.
 
545
   *
 
546
   * Deprecated: 1.10: Use the #ClutterTimeline:repeat-count property instead.
260
547
   */
261
548
  obj_props[PROP_LOOP] =
262
549
    g_param_spec_boolean ("loop",
263
550
                          P_("Loop"),
264
551
                          P_("Should the timeline automatically restart"),
265
552
                          FALSE,
266
 
                          CLUTTER_PARAM_READWRITE);
 
553
                          CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
267
554
 
268
555
  /**
269
556
   * ClutterTimeline:delay:
328
615
                          FALSE,
329
616
                          CLUTTER_PARAM_READWRITE);
330
617
 
331
 
  object_class->dispose      = clutter_timeline_dispose;
332
 
  object_class->finalize     = clutter_timeline_finalize;
 
618
  /**
 
619
   * ClutterTimeline:repeat-count:
 
620
   *
 
621
   * Defines how many times the timeline should repeat.
 
622
   *
 
623
   * If the repeat count is 0, the timeline does not repeat.
 
624
   *
 
625
   * If the repeat count is set to -1, the timeline will repeat until it is
 
626
   * stopped.
 
627
   *
 
628
   * Since: 1.10
 
629
   */
 
630
  obj_props[PROP_REPEAT_COUNT] =
 
631
    g_param_spec_int ("repeat-count",
 
632
                      P_("Repeat Count"),
 
633
                      P_("How many times the timeline should repeat"),
 
634
                      -1, G_MAXINT,
 
635
                      0,
 
636
                      CLUTTER_PARAM_READWRITE);
 
637
 
 
638
  /**
 
639
   * ClutterTimeline:progress-mode:
 
640
   *
 
641
   * Controls the way a #ClutterTimeline computes the normalized progress.
 
642
   *
 
643
   * Since: 1.10
 
644
   */
 
645
  obj_props[PROP_PROGRESS_MODE] =
 
646
    g_param_spec_enum ("progress-mode",
 
647
                       P_("Progress Mode"),
 
648
                       P_("How the timeline should compute the progress"),
 
649
                       CLUTTER_TYPE_ANIMATION_MODE,
 
650
                       CLUTTER_LINEAR,
 
651
                       CLUTTER_PARAM_READWRITE);
 
652
 
 
653
  object_class->dispose = clutter_timeline_dispose;
 
654
  object_class->finalize = clutter_timeline_finalize;
333
655
  object_class->set_property = clutter_timeline_set_property;
334
656
  object_class->get_property = clutter_timeline_get_property;
335
 
  g_object_class_install_properties (object_class,
336
 
                                     PROP_LAST,
337
 
                                     obj_props);
 
657
  g_object_class_install_properties (object_class, PROP_LAST, obj_props);
338
658
 
339
659
  /**
340
660
   * ClutterTimeline::new-frame:
358
678
   * ClutterTimeline::completed:
359
679
   * @timeline: the #ClutterTimeline which received the signal
360
680
   *
361
 
   * The ::completed signal is emitted when the timeline reaches the
362
 
   * number of frames specified by the ClutterTimeline:num-frames property.
 
681
   * The #ClutterTimeline::completed signal is emitted when the timeline's
 
682
   * elapsed time reaches the value of the #ClutterTimeline:duration
 
683
   * property.
 
684
   *
 
685
   * This signal will be emitted even if the #ClutterTimeline is set to be
 
686
   * repeating.
363
687
   */
364
688
  timeline_signals[COMPLETED] =
365
689
    g_signal_new (I_("completed"),
453
777
    G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_TIMELINE,
454
778
                                 ClutterTimelinePrivate);
455
779
 
456
 
  priv->duration = 0;
457
 
  priv->delay = 0;
458
 
  priv->elapsed_time = 0;
 
780
  priv->progress_mode = CLUTTER_LINEAR;
459
781
}
460
782
 
461
783
struct CheckIfMarkerHitClosure
584
906
    {
585
907
      _clutter_master_clock_add_timeline (master_clock, timeline);
586
908
      priv->waiting_first_tick = TRUE;
 
909
      priv->current_repeat = 0;
587
910
    }
588
911
  else
589
912
    {
600
923
 
601
924
  g_object_ref (timeline);
602
925
 
603
 
  CLUTTER_TIMESTAMP (SCHEDULER, "Timeline [%p] activated (cur: %ld)\n",
604
 
                     timeline,
605
 
                     (long) priv->elapsed_time);
 
926
  CLUTTER_NOTE (SCHEDULER, "Timeline [%p] activated (cur: %ld)\n",
 
927
                timeline,
 
928
                (long) priv->elapsed_time);
606
929
 
607
930
  /* Advance time */
608
931
  if (priv->direction == CLUTTER_TIMELINE_FORWARD)
617
940
      emit_frame_signal (timeline);
618
941
      check_markers (timeline, priv->msecs_delta);
619
942
 
620
 
      /* Signal pauses timeline ? */
621
 
      if (!priv->is_playing)
622
 
        {
623
 
          g_object_unref (timeline);
624
 
          return FALSE;
625
 
        }
626
 
 
627
943
      g_object_unref (timeline);
628
 
      return TRUE;
 
944
 
 
945
      return priv->is_playing;
629
946
    }
630
947
  else
631
948
    {
672
989
                    (long) priv->elapsed_time,
673
990
                    (long) priv->msecs_delta);
674
991
 
675
 
      if (!priv->loop && priv->is_playing)
 
992
      if (priv->is_playing &&
 
993
          (priv->repeat_count == 0 ||
 
994
           priv->repeat_count == priv->current_repeat))
676
995
        {
677
 
          /* We remove the timeout now, so that the completed signal handler
 
996
          /* We stop the timeline now, so that the completed signal handler
678
997
           * may choose to re-start the timeline
679
998
           *
680
 
           * XXX Perhaps we should remove this earlier, and regardless
681
 
           * of priv->loop. Are we limiting the things that could be done in
682
 
           * the above new-frame signal handler */
 
999
           * XXX Perhaps we should do this earlier, and regardless of
 
1000
           * priv->repeat_count. Are we limiting the things that could be
 
1001
           * done in the above new-frame signal handler?
 
1002
           */
683
1003
          set_is_playing (timeline, FALSE);
684
1004
        }
685
1005
 
686
1006
      g_signal_emit (timeline, timeline_signals[COMPLETED], 0);
687
1007
 
 
1008
      priv->current_repeat += 1;
 
1009
 
688
1010
      if (priv->auto_reverse)
689
1011
        {
690
1012
          /* :auto-reverse changes the direction of the timeline */
711
1033
          return TRUE;
712
1034
        }
713
1035
 
714
 
      if (priv->loop)
 
1036
      if (priv->repeat_count != 0)
715
1037
        {
716
1038
          /* We try and interpolate smoothly around a loop */
717
1039
          if (saved_direction == CLUTTER_TIMELINE_FORWARD)
842
1164
 * @loop: %TRUE for enable looping
843
1165
 *
844
1166
 * Sets whether @timeline should loop.
 
1167
 *
 
1168
 * This function is equivalent to calling clutter_timeline_set_repeat_count()
 
1169
 * with -1 if @loop is %TRUE, and with 0 if @loop is %FALSE.
 
1170
 *
 
1171
 * Deprecated: 1.10: Use clutter_timeline_set_repeat_count() instead.
845
1172
 */
846
1173
void
847
1174
clutter_timeline_set_loop (ClutterTimeline *timeline,
849
1176
{
850
1177
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
851
1178
 
852
 
  if (timeline->priv->loop != loop)
853
 
    {
854
 
      timeline->priv->loop = loop;
855
 
 
856
 
      g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_LOOP]);
857
 
    }
 
1179
  clutter_timeline_set_loop_internal (timeline, loop);
858
1180
}
859
1181
 
860
1182
/**
864
1186
 * Gets whether @timeline is looping
865
1187
 *
866
1188
 * Return value: %TRUE if the timeline is looping
 
1189
 *
 
1190
 * Deprecated: 1.10: Use clutter_timeline_get_repeat_count() instead.
867
1191
 */
868
1192
gboolean
869
1193
clutter_timeline_get_loop (ClutterTimeline *timeline)
870
1194
{
871
1195
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
872
1196
 
873
 
  return timeline->priv->loop;
 
1197
  return timeline->priv->repeat_count != 0;
874
1198
}
875
1199
 
876
1200
/**
996
1320
 * Create a new #ClutterTimeline instance which has property values
997
1321
 * matching that of supplied timeline. The cloned timeline will not
998
1322
 * be started and will not be positioned to the current position of
999
 
 * @timeline: you will have to start it with clutter_timeline_start().
1000
 
 *
1001
 
 * Return Value: (transfer full): a new #ClutterTimeline, cloned
 
1323
 * the original @timeline: you will have to start it with clutter_timeline_start().
 
1324
 *
 
1325
 * <note><para>The only cloned properties are:</para>
 
1326
 * <itemizedlist>
 
1327
 *   <listitem><simpara>#ClutterTimeline:duration</simpara></listitem>
 
1328
 *   <listitem><simpara>#ClutterTimeline:loop</simpara></listitem>
 
1329
 *   <listitem><simpara>#ClutterTimeline:delay</simpara></listitem>
 
1330
 *   <listitem><simpara>#ClutterTimeline:direction</simpara></listitem>
 
1331
 * </itemizedlist></note>
 
1332
 *
 
1333
 * Return value: (transfer full): a new #ClutterTimeline, cloned
1002
1334
 *   from @timeline
1003
1335
 *
1004
1336
 * Since: 0.4
 
1337
 *
 
1338
 * Deprecated: 1.10: Use clutter_timeline_new() or g_object_new()
 
1339
 *   instead
1005
1340
 */
1006
1341
ClutterTimeline *
1007
1342
clutter_timeline_clone (ClutterTimeline *timeline)
1008
1343
{
1009
 
  ClutterTimeline *copy;
1010
 
 
1011
1344
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
1012
1345
 
1013
 
  copy = g_object_new (CLUTTER_TYPE_TIMELINE,
1014
 
                       "duration", clutter_timeline_get_duration (timeline),
1015
 
                       "loop", clutter_timeline_get_loop (timeline),
1016
 
                       "delay", clutter_timeline_get_delay (timeline),
1017
 
                       "direction", clutter_timeline_get_direction (timeline),
 
1346
  return g_object_new (CLUTTER_TYPE_TIMELINE,
 
1347
                       "duration", timeline->priv->duration,
 
1348
                       "loop", timeline->priv->repeat_count != 0,
 
1349
                       "delay", timeline->priv->delay,
 
1350
                       "direction", timeline->priv->direction,
1018
1351
                       NULL);
1019
 
 
1020
 
  return copy;
1021
1352
}
1022
1353
 
1023
1354
/**
1139
1470
 * clutter_timeline_get_progress:
1140
1471
 * @timeline: a #ClutterTimeline
1141
1472
 *
1142
 
 * The position of the timeline in a [0, 1] interval.
1143
 
 *
1144
 
 * Return value: the position of the timeline.
 
1473
 * The position of the timeline in a normalized [-1, 2] interval.
 
1474
 *
 
1475
 * The return value of this function is determined by the progress
 
1476
 * mode set using clutter_timeline_set_progress_mode(), or by the
 
1477
 * progress function set using clutter_timeline_set_progress_func().
 
1478
 *
 
1479
 * Return value: the normalized current position in the timeline.
1145
1480
 *
1146
1481
 * Since: 0.6
1147
1482
 */
1154
1489
 
1155
1490
  priv = timeline->priv;
1156
1491
 
1157
 
  return (gdouble) priv->elapsed_time / (gdouble) priv->duration;
 
1492
  /* short-circuit linear progress */
 
1493
  if (priv->progress_func == NULL)
 
1494
    return (gdouble) priv->elapsed_time / (gdouble) priv->duration;
 
1495
  else
 
1496
    return priv->progress_func (timeline,
 
1497
                                (gdouble) priv->elapsed_time,
 
1498
                                (gdouble) priv->duration,
 
1499
                                priv->progress_data);
1158
1500
}
1159
1501
 
1160
1502
/**
1297
1639
    }
1298
1640
}
1299
1641
 
1300
 
static inline void
1301
 
clutter_timeline_add_marker_internal (ClutterTimeline *timeline,
1302
 
                                      const gchar     *marker_name,
1303
 
                                      guint            msecs)
1304
 
{
1305
 
  ClutterTimelinePrivate *priv = timeline->priv;
1306
 
  TimelineMarker *marker;
1307
 
 
1308
 
  /* create the hash table that will hold the markers */
1309
 
  if (G_UNLIKELY (priv->markers_by_name == NULL))
1310
 
    priv->markers_by_name = g_hash_table_new_full (g_str_hash, g_str_equal,
1311
 
                                                   NULL,
1312
 
                                                   timeline_marker_free);
1313
 
 
1314
 
  marker = g_hash_table_lookup (priv->markers_by_name, marker_name);
1315
 
  if (G_UNLIKELY (marker))
1316
 
    {
1317
 
      g_warning ("A marker named '%s' already exists at time %d",
1318
 
                 marker->name,
1319
 
                 marker->msecs);
1320
 
      return;
1321
 
    }
1322
 
 
1323
 
  marker = timeline_marker_new (marker_name, msecs);
1324
 
  g_hash_table_insert (priv->markers_by_name, marker->name, marker);
1325
 
}
1326
 
 
1327
1642
/**
1328
1643
 * clutter_timeline_add_marker_at_time:
1329
1644
 * @timeline: a #ClutterTimeline
1347
1662
                                     const gchar     *marker_name,
1348
1663
                                     guint            msecs)
1349
1664
{
 
1665
  TimelineMarker *marker;
 
1666
 
1350
1667
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
1351
1668
  g_return_if_fail (marker_name != NULL);
1352
1669
  g_return_if_fail (msecs <= clutter_timeline_get_duration (timeline));
1353
1670
 
1354
 
  clutter_timeline_add_marker_internal (timeline, marker_name, msecs);
 
1671
  marker = timeline_marker_new (marker_name, msecs);
 
1672
  clutter_timeline_add_marker_internal (timeline, marker);
1355
1673
}
1356
1674
 
1357
1675
struct CollectMarkersClosure
1433
1751
                            &data);
1434
1752
 
1435
1753
      i = data.markers->len;
1436
 
      retval = (gchar **) g_array_free (data.markers, FALSE);
 
1754
      retval = (gchar **) (void *) g_array_free (data.markers, FALSE);
1437
1755
    }
1438
1756
 
1439
1757
  if (n_markers)
1575
1893
 * }
1576
1894
 * ...
1577
1895
 *   timeline = clutter_timeline_new (1000);
1578
 
 *   clutter_timeline_set_loop (timeline);
 
1896
 *   clutter_timeline_set_repeat_count (timeline, -1);
1579
1897
 *   g_signal_connect (timeline, "completed",
1580
1898
 *                     G_CALLBACK (reverse_timeline),
1581
1899
 *                     NULL);
1585
1903
 *
1586
1904
 * |[
1587
1905
 *   timeline = clutter_timeline_new (1000);
1588
 
 *   clutter_timeline_set_loop (timeline);
 
1906
 *   clutter_timeline_set_repeat_count (timeline, -1);
1589
1907
 *   clutter_timeline_set_auto_reverse (timeline);
1590
1908
 * ]|
1591
1909
 *
1630
1948
 
1631
1949
  return timeline->priv->auto_reverse;
1632
1950
}
 
1951
 
 
1952
/**
 
1953
 * clutter_timeline_set_repeat_count:
 
1954
 * @timeline: a #ClutterTimeline
 
1955
 * @count: the number of times the timeline should repeat
 
1956
 *
 
1957
 * Sets the number of times the @timeline should repeat.
 
1958
 *
 
1959
 * If @count is 0, the timeline never repeats.
 
1960
 *
 
1961
 * If @count is -1, the timeline will always repeat until
 
1962
 * it's stopped.
 
1963
 *
 
1964
 * Since: 1.10
 
1965
 */
 
1966
void
 
1967
clutter_timeline_set_repeat_count (ClutterTimeline *timeline,
 
1968
                                   gint             count)
 
1969
{
 
1970
  ClutterTimelinePrivate *priv;
 
1971
 
 
1972
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
 
1973
  g_return_if_fail (count >= -1);
 
1974
 
 
1975
  priv = timeline->priv;
 
1976
 
 
1977
  if (priv->repeat_count != count)
 
1978
    {
 
1979
      priv->repeat_count = count;
 
1980
 
 
1981
      g_object_notify_by_pspec (G_OBJECT (timeline),
 
1982
                                obj_props[PROP_REPEAT_COUNT]);
 
1983
    }
 
1984
}
 
1985
 
 
1986
/**
 
1987
 * clutter_timeline_get_repeat_count:
 
1988
 * @timeline: a #ClutterTimeline
 
1989
 *
 
1990
 * Retrieves the number set using clutter_timeline_set_repeat_count().
 
1991
 *
 
1992
 * Return value: the number of repeats
 
1993
 *
 
1994
 * Since: 1.10
 
1995
 */
 
1996
gint
 
1997
clutter_timeline_get_repeat_count (ClutterTimeline *timeline)
 
1998
{
 
1999
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
 
2000
 
 
2001
  return timeline->priv->repeat_count;
 
2002
}
 
2003
 
 
2004
/**
 
2005
 * clutter_timeline_set_progress_func:
 
2006
 * @timeline: a #ClutterTimeline
 
2007
 * @func: (scope notified) (allow-none): a progress function, or %NULL
 
2008
 * @data: (closure): data to pass to @func
 
2009
 * @notify: a function to be called when the progress function is removed
 
2010
 *    or the timeline is disposed
 
2011
 *
 
2012
 * Sets a custom progress function for @timeline. The progress function will
 
2013
 * be called by clutter_timeline_get_progress() and will be used to compute
 
2014
 * the progress value based on the elapsed time and the total duration of the
 
2015
 * timeline.
 
2016
 *
 
2017
 * If @func is not %NULL, the #ClutterTimeline:progress-mode property will
 
2018
 * be set to %CLUTTER_CUSTOM_MODE.
 
2019
 *
 
2020
 * If @func is %NULL, any previously set progress function will be unset, and
 
2021
 * the #ClutterTimeline:progress-mode property will be set to %CLUTTER_LINEAR.
 
2022
 *
 
2023
 * Since: 1.10
 
2024
 */
 
2025
void
 
2026
clutter_timeline_set_progress_func (ClutterTimeline             *timeline,
 
2027
                                    ClutterTimelineProgressFunc  func,
 
2028
                                    gpointer                     data,
 
2029
                                    GDestroyNotify               notify)
 
2030
{
 
2031
  ClutterTimelinePrivate *priv;
 
2032
 
 
2033
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
 
2034
 
 
2035
  priv = timeline->priv;
 
2036
 
 
2037
  if (priv->progress_notify != NULL)
 
2038
    priv->progress_notify (priv->progress_data);
 
2039
 
 
2040
  priv->progress_func = func;
 
2041
  priv->progress_data = data;
 
2042
  priv->progress_notify = notify;
 
2043
 
 
2044
  if (priv->progress_func != NULL)
 
2045
    priv->progress_mode = CLUTTER_CUSTOM_MODE;
 
2046
  else
 
2047
    priv->progress_mode = CLUTTER_LINEAR;
 
2048
 
 
2049
  g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_PROGRESS_MODE]);
 
2050
}
 
2051
 
 
2052
/*< private >
 
2053
 * _clutter_animation_modes:
 
2054
 *
 
2055
 * A mapping of animation modes and easing functions.
 
2056
 */
 
2057
static const struct {
 
2058
  ClutterAnimationMode mode;
 
2059
  ClutterEasingFunc func;
 
2060
  const char *name;
 
2061
} _clutter_animation_modes[] = {
 
2062
  { CLUTTER_CUSTOM_MODE,         NULL, "custom" },
 
2063
 
 
2064
  { CLUTTER_LINEAR,              clutter_linear, "linear" },
 
2065
  { CLUTTER_EASE_IN_QUAD,        clutter_ease_in_quad, "easeInQuad" },
 
2066
  { CLUTTER_EASE_OUT_QUAD,       clutter_ease_out_quad, "easeOutQuad" },
 
2067
  { CLUTTER_EASE_IN_OUT_QUAD,    clutter_ease_in_out_quad, "easeInOutQuad" },
 
2068
  { CLUTTER_EASE_IN_CUBIC,       clutter_ease_in_cubic, "easeInCubic" },
 
2069
  { CLUTTER_EASE_OUT_CUBIC,      clutter_ease_out_cubic, "easeOutCubic" },
 
2070
  { CLUTTER_EASE_IN_OUT_CUBIC,   clutter_ease_in_out_cubic, "easeInOutCubic" },
 
2071
  { CLUTTER_EASE_IN_QUART,       clutter_ease_in_quart, "easeInQuart" },
 
2072
  { CLUTTER_EASE_OUT_QUART,      clutter_ease_out_quart, "easeOutQuart" },
 
2073
  { CLUTTER_EASE_IN_OUT_QUART,   clutter_ease_in_out_quart, "easeInOutQuart" },
 
2074
  { CLUTTER_EASE_IN_QUINT,       clutter_ease_in_quint, "easeInQuint" },
 
2075
  { CLUTTER_EASE_OUT_QUINT,      clutter_ease_out_quint, "easeOutQuint" },
 
2076
  { CLUTTER_EASE_IN_OUT_QUINT,   clutter_ease_in_out_quint, "easeInOutQuint" },
 
2077
  { CLUTTER_EASE_IN_SINE,        clutter_ease_in_sine, "easeInSine" },
 
2078
  { CLUTTER_EASE_OUT_SINE,       clutter_ease_out_sine, "easeOutSine" },
 
2079
  { CLUTTER_EASE_IN_OUT_SINE,    clutter_ease_in_out_sine, "easeInOutSine" },
 
2080
  { CLUTTER_EASE_IN_EXPO,        clutter_ease_in_expo, "easeInExpo" },
 
2081
  { CLUTTER_EASE_OUT_EXPO,       clutter_ease_out_expo, "easeOutExpo" },
 
2082
  { CLUTTER_EASE_IN_OUT_EXPO,    clutter_ease_in_out_expo, "easeInOutExpo" },
 
2083
  { CLUTTER_EASE_IN_CIRC,        clutter_ease_in_circ, "easeInCirc" },
 
2084
  { CLUTTER_EASE_OUT_CIRC,       clutter_ease_out_circ, "easeOutCirc" },
 
2085
  { CLUTTER_EASE_IN_OUT_CIRC,    clutter_ease_in_out_circ, "easeInOutCirc" },
 
2086
  { CLUTTER_EASE_IN_ELASTIC,     clutter_ease_in_elastic, "easeInElastic" },
 
2087
  { CLUTTER_EASE_OUT_ELASTIC,    clutter_ease_out_elastic, "easeOutElastic" },
 
2088
  { CLUTTER_EASE_IN_OUT_ELASTIC, clutter_ease_in_out_elastic, "easeInOutElastic" },
 
2089
  { CLUTTER_EASE_IN_BACK,        clutter_ease_in_back, "easeInBack" },
 
2090
  { CLUTTER_EASE_OUT_BACK,       clutter_ease_out_back, "easeOutBack" },
 
2091
  { CLUTTER_EASE_IN_OUT_BACK,    clutter_ease_in_out_back, "easeInOutBack" },
 
2092
  { CLUTTER_EASE_IN_BOUNCE,      clutter_ease_in_bounce, "easeInBounce" },
 
2093
  { CLUTTER_EASE_OUT_BOUNCE,     clutter_ease_out_bounce, "easeOutBounce" },
 
2094
  { CLUTTER_EASE_IN_OUT_BOUNCE,  clutter_ease_in_out_bounce, "easeInOutBounce" },
 
2095
 
 
2096
  { CLUTTER_ANIMATION_LAST,      NULL, "sentinel" },
 
2097
};
 
2098
 
 
2099
static gdouble
 
2100
clutter_timeline_progress_func (ClutterTimeline *timeline,
 
2101
                                gdouble          elapsed,
 
2102
                                gdouble          duration,
 
2103
                                gpointer         user_data G_GNUC_UNUSED)
 
2104
{
 
2105
  ClutterTimelinePrivate *priv = timeline->priv;
 
2106
  ClutterEasingFunc easing_func;
 
2107
 
 
2108
  g_assert (_clutter_animation_modes[priv->progress_mode].mode == priv->progress_mode);
 
2109
  g_assert (_clutter_animation_modes[priv->progress_mode].func != NULL);
 
2110
 
 
2111
  easing_func = _clutter_animation_modes[priv->progress_mode].func;
 
2112
 
 
2113
  return easing_func (elapsed, duration);
 
2114
}
 
2115
 
 
2116
/**
 
2117
 * clutter_timeline_set_progress_mode:
 
2118
 * @timeline: a #ClutterTimeline
 
2119
 * @mode: the progress mode, as a #ClutterAnimationMode
 
2120
 *
 
2121
 * Sets the progress function using a value from the #ClutterAnimationMode
 
2122
 * enumeration. The @mode cannot be %CLUTTER_CUSTOM_MODE or bigger than
 
2123
 * %CLUTTER_ANIMATION_LAST.
 
2124
 *
 
2125
 * Since: 1.10
 
2126
 */
 
2127
void
 
2128
clutter_timeline_set_progress_mode (ClutterTimeline      *timeline,
 
2129
                                    ClutterAnimationMode  mode)
 
2130
{
 
2131
  ClutterTimelinePrivate *priv;
 
2132
 
 
2133
  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
 
2134
  g_return_if_fail (mode < CLUTTER_ANIMATION_LAST);
 
2135
  g_return_if_fail (mode != CLUTTER_CUSTOM_MODE);
 
2136
 
 
2137
  priv = timeline->priv;
 
2138
 
 
2139
  if (priv->progress_mode == mode)
 
2140
    return;
 
2141
 
 
2142
  if (priv->progress_notify != NULL)
 
2143
    priv->progress_notify (priv->progress_data);
 
2144
 
 
2145
  priv->progress_mode = mode;
 
2146
 
 
2147
  /* short-circuit linear progress */
 
2148
  if (priv->progress_mode != CLUTTER_LINEAR)
 
2149
    priv->progress_func = clutter_timeline_progress_func;
 
2150
  else
 
2151
    priv->progress_func = NULL;
 
2152
 
 
2153
  priv->progress_data = NULL;
 
2154
  priv->progress_notify = NULL;
 
2155
 
 
2156
  g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_PROGRESS_MODE]);
 
2157
}
 
2158
 
 
2159
/**
 
2160
 * clutter_timeline_get_progress_mode:
 
2161
 * @timeline: a #ClutterTimeline
 
2162
 *
 
2163
 * Retrieves the progress mode set using clutter_timeline_set_progress_mode()
 
2164
 * or clutter_timeline_set_progress_func().
 
2165
 *
 
2166
 * Return value: a #ClutterAnimationMode
 
2167
 *
 
2168
 * Since: 1.10
 
2169
 */
 
2170
ClutterAnimationMode
 
2171
clutter_timeline_get_progress_mode (ClutterTimeline *timeline)
 
2172
{
 
2173
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), CLUTTER_LINEAR);
 
2174
 
 
2175
  return timeline->priv->progress_mode;
 
2176
}
 
2177
 
 
2178
/**
 
2179
 * clutter_timeline_get_duration_hint:
 
2180
 * @timeline: a #ClutterTimeline
 
2181
 *
 
2182
 * Retrieves the full duration of the @timeline, taking into account the
 
2183
 * current value of the #ClutterTimeline:repeat-count property.
 
2184
 *
 
2185
 * If the #ClutterTimeline:repeat-count property is set to -1, this function
 
2186
 * will return %G_MAXINT64.
 
2187
 *
 
2188
 * The returned value is to be considered a hint, and it's only valid
 
2189
 * as long as the @timeline hasn't been changed.
 
2190
 *
 
2191
 * Return value: the full duration of the #ClutterTimeline
 
2192
 *
 
2193
 * Since: 1.10
 
2194
 */
 
2195
gint64
 
2196
clutter_timeline_get_duration_hint (ClutterTimeline *timeline)
 
2197
{
 
2198
  ClutterTimelinePrivate *priv;
 
2199
 
 
2200
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
 
2201
 
 
2202
  priv = timeline->priv;
 
2203
 
 
2204
  if (priv->repeat_count == 0)
 
2205
    return priv->duration;
 
2206
  else if (priv->repeat_count < 0)
 
2207
    return G_MAXINT64;
 
2208
  else
 
2209
    return priv->repeat_count * priv->duration;
 
2210
}
 
2211
 
 
2212
/**
 
2213
 * clutter_timeline_get_current_repeat:
 
2214
 * @timeline: a #ClutterTimeline
 
2215
 *
 
2216
 * Retrieves the current repeat for a timeline.
 
2217
 *
 
2218
 * Repeats start at 0.
 
2219
 *
 
2220
 * Return value: the current repeat
 
2221
 *
 
2222
 * Since: 1.10
 
2223
 */
 
2224
gint
 
2225
clutter_timeline_get_current_repeat (ClutterTimeline *timeline)
 
2226
{
 
2227
  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
 
2228
 
 
2229
  return timeline->priv->current_repeat;
 
2230
}