1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 #include <gtk/gtktimeline.h>
19 #include <gtk/gtktypebuiltins.h>
20 #include <gtk/gtksettings.h>
23 #define MSECS_PER_SEC 1000
24 #define FRAME_INTERVAL(nframes) (MSECS_PER_SEC / nframes)
25 #define DEFAULT_FPS 30
27 typedef struct GtkTimelinePriv GtkTimelinePriv;
29 struct GtkTimelinePriv
39 gdouble last_progress;
43 GtkTimelineProgressType progress_type;
45 guint animations_enabled : 1;
67 static guint signals [LAST_SIGNAL] = { 0, };
70 static void gtk_timeline_set_property (GObject *object,
74 static void gtk_timeline_get_property (GObject *object,
78 static void _gtk_timeline_finalize (GObject *object);
81 G_DEFINE_TYPE (GtkTimeline, _gtk_timeline, G_TYPE_OBJECT)
85 _gtk_timeline_class_init (GtkTimelineClass *klass)
87 GObjectClass *object_class = G_OBJECT_CLASS (klass);
89 object_class->set_property = gtk_timeline_set_property;
90 object_class->get_property = gtk_timeline_get_property;
91 object_class->finalize = _gtk_timeline_finalize;
93 g_object_class_install_property (object_class,
95 g_param_spec_uint ("fps",
97 "Frames per second for the timeline",
101 g_object_class_install_property (object_class,
103 g_param_spec_uint ("duration",
104 "Animation Duration",
105 "Animation Duration",
109 g_object_class_install_property (object_class,
111 g_param_spec_boolean ("loop",
113 "Whether the timeline loops or not",
116 g_object_class_install_property (object_class,
118 g_param_spec_object ("screen",
120 "Screen to get the settings from",
125 g_signal_new ("started",
126 G_TYPE_FROM_CLASS (object_class),
128 G_STRUCT_OFFSET (GtkTimelineClass, started),
130 g_cclosure_marshal_VOID__VOID,
134 g_signal_new ("paused",
135 G_TYPE_FROM_CLASS (object_class),
137 G_STRUCT_OFFSET (GtkTimelineClass, paused),
139 g_cclosure_marshal_VOID__VOID,
143 g_signal_new ("finished",
144 G_TYPE_FROM_CLASS (object_class),
146 G_STRUCT_OFFSET (GtkTimelineClass, finished),
148 g_cclosure_marshal_VOID__VOID,
152 g_signal_new ("frame",
153 G_TYPE_FROM_CLASS (object_class),
155 G_STRUCT_OFFSET (GtkTimelineClass, frame),
157 g_cclosure_marshal_VOID__DOUBLE,
161 g_type_class_add_private (klass, sizeof (GtkTimelinePriv));
165 _gtk_timeline_init (GtkTimeline *timeline)
167 GtkTimelinePriv *priv;
169 priv = timeline->priv = G_TYPE_INSTANCE_GET_PRIVATE (timeline,
173 priv->fps = DEFAULT_FPS;
174 priv->duration = 0.0;
175 priv->direction = GTK_TIMELINE_DIRECTION_FORWARD;
176 priv->screen = gdk_screen_get_default ();
178 priv->last_progress = 0;
182 gtk_timeline_set_property (GObject *object,
187 GtkTimeline *timeline;
189 timeline = GTK_TIMELINE (object);
194 _gtk_timeline_set_fps (timeline, g_value_get_uint (value));
197 _gtk_timeline_set_duration (timeline, g_value_get_uint (value));
200 _gtk_timeline_set_loop (timeline, g_value_get_boolean (value));
203 _gtk_timeline_set_direction (timeline, g_value_get_enum (value));
206 _gtk_timeline_set_screen (timeline,
207 GDK_SCREEN (g_value_get_object (value)));
210 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
215 gtk_timeline_get_property (GObject *object,
220 GtkTimeline *timeline;
221 GtkTimelinePriv *priv;
223 timeline = GTK_TIMELINE (object);
224 priv = timeline->priv;
229 g_value_set_uint (value, priv->fps);
232 g_value_set_uint (value, priv->duration);
235 g_value_set_boolean (value, priv->loop);
238 g_value_set_enum (value, priv->direction);
241 g_value_set_object (value, priv->screen);
244 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
249 _gtk_timeline_finalize (GObject *object)
251 GtkTimelinePriv *priv;
252 GtkTimeline *timeline;
254 timeline = (GtkTimeline *) object;
255 priv = timeline->priv;
259 g_source_remove (priv->source_id);
264 g_timer_destroy (priv->timer);
266 G_OBJECT_CLASS (_gtk_timeline_parent_class)->finalize (object);
270 calculate_progress (gdouble linear_progress,
271 GtkTimelineProgressType progress_type)
275 progress = linear_progress;
277 switch (progress_type)
279 case GTK_TIMELINE_PROGRESS_LINEAR:
281 case GTK_TIMELINE_PROGRESS_EASE_IN_OUT:
285 progress = pow (progress, 3) / 2;
287 progress = (pow (progress - 2, 3) + 2) / 2;
290 case GTK_TIMELINE_PROGRESS_EASE:
291 progress = (sin ((progress - 0.5) * G_PI) + 1) / 2;
293 case GTK_TIMELINE_PROGRESS_EASE_IN:
294 progress = pow (progress, 3);
296 case GTK_TIMELINE_PROGRESS_EASE_OUT:
297 progress = pow (progress - 1, 3) + 1;
300 g_warning ("Timeline progress type not implemented");
307 gtk_timeline_run_frame (GtkTimeline *timeline)
309 GtkTimelinePriv *priv;
310 gdouble delta_progress, progress;
312 /* the user may unref us during the signals, so save ourselves */
313 g_object_ref (timeline);
315 priv = timeline->priv;
317 priv->elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
318 g_timer_start (priv->timer);
320 if (priv->animations_enabled)
322 delta_progress = (gdouble) priv->elapsed_time / priv->duration;
323 progress = priv->last_progress;
325 if (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD)
326 progress -= delta_progress;
328 progress += delta_progress;
330 priv->last_progress = progress;
332 progress = CLAMP (progress, 0., 1.);
335 progress = (priv->direction == GTK_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0;
337 priv->progress = progress;
338 g_signal_emit (timeline, signals [FRAME], 0,
339 calculate_progress (progress, priv->progress_type));
341 if ((priv->direction == GTK_TIMELINE_DIRECTION_FORWARD && progress == 1.0) ||
342 (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD && progress == 0.0))
346 loop = priv->loop && priv->animations_enabled;
349 _gtk_timeline_rewind (timeline);
354 g_source_remove (priv->source_id);
357 g_timer_stop (priv->timer);
358 g_signal_emit (timeline, signals [FINISHED], 0);
359 g_object_unref (timeline);
364 g_object_unref (timeline);
371 * @duration: duration in milliseconds for the timeline
373 * Creates a new #GtkTimeline with the specified number of frames.
375 * Return Value: the newly created #GtkTimeline
378 _gtk_timeline_new (guint duration)
380 return g_object_new (GTK_TYPE_TIMELINE,
381 "duration", duration,
386 _gtk_timeline_new_for_screen (guint duration,
389 return g_object_new (GTK_TYPE_TIMELINE,
390 "duration", duration,
396 * _gtk_timeline_start:
397 * @timeline: A #GtkTimeline
399 * Runs the timeline from the current frame.
402 _gtk_timeline_start (GtkTimeline *timeline)
404 GtkTimelinePriv *priv;
405 GtkSettings *settings;
406 gboolean enable_animations = FALSE;
408 g_return_if_fail (GTK_IS_TIMELINE (timeline));
410 priv = timeline->priv;
412 if (!priv->source_id)
415 g_timer_continue (priv->timer);
417 priv->timer = g_timer_new ();
420 g_assert (priv->fps > 0);
424 settings = gtk_settings_get_for_screen (priv->screen);
425 g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
428 priv->animations_enabled = enable_animations;
430 g_signal_emit (timeline, signals [STARTED], 0);
432 if (enable_animations)
433 priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
434 (GSourceFunc) gtk_timeline_run_frame,
437 priv->source_id = gdk_threads_add_idle ((GSourceFunc) gtk_timeline_run_frame,
443 * _gtk_timeline_pause:
444 * @timeline: A #GtkTimeline
446 * Pauses the timeline.
449 _gtk_timeline_pause (GtkTimeline *timeline)
451 GtkTimelinePriv *priv;
453 g_return_if_fail (GTK_IS_TIMELINE (timeline));
455 priv = timeline->priv;
459 g_timer_stop (priv->timer);
460 g_source_remove (priv->source_id);
462 g_signal_emit (timeline, signals [PAUSED], 0);
467 * _gtk_timeline_rewind:
468 * @timeline: A #GtkTimeline
470 * Rewinds the timeline.
473 _gtk_timeline_rewind (GtkTimeline *timeline)
475 GtkTimelinePriv *priv;
477 g_return_if_fail (GTK_IS_TIMELINE (timeline));
479 priv = timeline->priv;
481 if (_gtk_timeline_get_direction (timeline) != GTK_TIMELINE_DIRECTION_FORWARD)
482 priv->progress = priv->last_progress = 1.;
484 priv->progress = priv->last_progress = 0.;
489 g_timer_start (priv->timer);
491 if (!priv->source_id)
492 g_timer_stop (priv->timer);
497 * _gtk_timeline_is_running:
498 * @timeline: A #GtkTimeline
500 * Returns whether the timeline is running or not.
502 * Return Value: %TRUE if the timeline is running
505 _gtk_timeline_is_running (GtkTimeline *timeline)
507 GtkTimelinePriv *priv;
509 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
511 priv = timeline->priv;
513 return (priv->source_id != 0);
517 * _gtk_timeline_get_elapsed_time:
518 * @timeline: A #GtkTimeline
520 * Returns the elapsed time since the last GtkTimeline::frame signal
522 * Return Value: elapsed time in milliseconds since the last frame
525 _gtk_timeline_get_elapsed_time (GtkTimeline *timeline)
527 GtkTimelinePriv *priv;
529 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
531 priv = timeline->priv;
532 return priv->elapsed_time;
536 * _gtk_timeline_get_fps:
537 * @timeline: A #GtkTimeline
539 * Returns the number of frames per second.
541 * Return Value: frames per second
544 _gtk_timeline_get_fps (GtkTimeline *timeline)
546 GtkTimelinePriv *priv;
548 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 1);
550 priv = timeline->priv;
555 * _gtk_timeline_set_fps:
556 * @timeline: A #GtkTimeline
557 * @fps: frames per second
559 * Sets the number of frames per second that
560 * the timeline will play.
563 _gtk_timeline_set_fps (GtkTimeline *timeline,
566 GtkTimelinePriv *priv;
568 g_return_if_fail (GTK_IS_TIMELINE (timeline));
569 g_return_if_fail (fps > 0);
571 priv = timeline->priv;
575 if (_gtk_timeline_is_running (timeline))
577 g_source_remove (priv->source_id);
578 priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
579 (GSourceFunc) gtk_timeline_run_frame,
583 g_object_notify (G_OBJECT (timeline), "fps");
587 * _gtk_timeline_get_loop:
588 * @timeline: A #GtkTimeline
590 * Returns whether the timeline loops to the
591 * beginning when it has reached the end.
593 * Return Value: %TRUE if the timeline loops
596 _gtk_timeline_get_loop (GtkTimeline *timeline)
598 GtkTimelinePriv *priv;
600 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
602 priv = timeline->priv;
607 * _gtk_timeline_set_loop:
608 * @timeline: A #GtkTimeline
609 * @loop: %TRUE to make the timeline loop
611 * Sets whether the timeline loops to the beginning
612 * when it has reached the end.
615 _gtk_timeline_set_loop (GtkTimeline *timeline,
618 GtkTimelinePriv *priv;
620 g_return_if_fail (GTK_IS_TIMELINE (timeline));
622 priv = timeline->priv;
624 if (loop != priv->loop)
627 g_object_notify (G_OBJECT (timeline), "loop");
632 _gtk_timeline_set_duration (GtkTimeline *timeline,
635 GtkTimelinePriv *priv;
637 g_return_if_fail (GTK_IS_TIMELINE (timeline));
639 priv = timeline->priv;
641 if (duration != priv->duration)
643 priv->duration = duration;
644 g_object_notify (G_OBJECT (timeline), "duration");
649 _gtk_timeline_get_duration (GtkTimeline *timeline)
651 GtkTimelinePriv *priv;
653 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
655 priv = timeline->priv;
657 return priv->duration;
661 * _gtk_timeline_set_direction:
662 * @timeline: A #GtkTimeline
663 * @direction: direction
665 * Sets the direction of the timeline.
668 _gtk_timeline_set_direction (GtkTimeline *timeline,
669 GtkTimelineDirection direction)
671 GtkTimelinePriv *priv;
673 g_return_if_fail (GTK_IS_TIMELINE (timeline));
675 priv = timeline->priv;
676 priv->direction = direction;
680 * _gtk_timeline_get_direction:
681 * @timeline: A #GtkTimeline
683 * Returns the direction of the timeline.
685 * Return Value: direction
688 _gtk_timeline_get_direction (GtkTimeline *timeline)
690 GtkTimelinePriv *priv;
692 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_DIRECTION_FORWARD);
694 priv = timeline->priv;
695 return priv->direction;
699 _gtk_timeline_set_screen (GtkTimeline *timeline,
702 GtkTimelinePriv *priv;
704 g_return_if_fail (GTK_IS_TIMELINE (timeline));
705 g_return_if_fail (GDK_IS_SCREEN (screen));
707 priv = timeline->priv;
710 g_object_unref (priv->screen);
712 priv->screen = g_object_ref (screen);
714 g_object_notify (G_OBJECT (timeline), "screen");
718 _gtk_timeline_get_screen (GtkTimeline *timeline)
720 GtkTimelinePriv *priv;
722 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
724 priv = timeline->priv;
729 _gtk_timeline_get_progress (GtkTimeline *timeline)
731 GtkTimelinePriv *priv;
733 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0.);
735 priv = timeline->priv;
736 return calculate_progress (priv->progress, priv->progress_type);
739 GtkTimelineProgressType
740 _gtk_timeline_get_progress_type (GtkTimeline *timeline)
742 GtkTimelinePriv *priv;
744 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_PROGRESS_LINEAR);
746 priv = timeline->priv;
747 return priv->progress_type;
751 _gtk_timeline_set_progress_type (GtkTimeline *timeline,
752 GtkTimelineProgressType progress_type)
754 GtkTimelinePriv *priv;
756 g_return_if_fail (GTK_IS_TIMELINE (timeline));
758 priv = timeline->priv;
759 priv->progress_type = progress_type;