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, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 #include <gtk/gtktimeline.h>
21 #include <gtk/gtktypebuiltins.h>
22 #include <gtk/gtksettings.h>
25 #define MSECS_PER_SEC 1000
26 #define FRAME_INTERVAL(nframes) (MSECS_PER_SEC / nframes)
27 #define DEFAULT_FPS 30
29 typedef struct GtkTimelinePriv GtkTimelinePriv;
31 struct GtkTimelinePriv
40 gdouble last_progress;
44 GtkTimelineProgressType progress_type;
46 guint animations_enabled : 1;
68 static guint signals [LAST_SIGNAL] = { 0, };
71 static void gtk_timeline_set_property (GObject *object,
75 static void gtk_timeline_get_property (GObject *object,
79 static void _gtk_timeline_finalize (GObject *object);
82 G_DEFINE_TYPE (GtkTimeline, _gtk_timeline, G_TYPE_OBJECT)
86 _gtk_timeline_class_init (GtkTimelineClass *klass)
88 GObjectClass *object_class = G_OBJECT_CLASS (klass);
90 object_class->set_property = gtk_timeline_set_property;
91 object_class->get_property = gtk_timeline_get_property;
92 object_class->finalize = _gtk_timeline_finalize;
94 g_object_class_install_property (object_class,
96 g_param_spec_uint ("fps",
98 "Frames per second for the timeline",
102 g_object_class_install_property (object_class,
104 g_param_spec_uint ("duration",
105 "Animation Duration",
106 "Animation Duration",
110 g_object_class_install_property (object_class,
112 g_param_spec_boolean ("loop",
114 "Whether the timeline loops or not",
117 g_object_class_install_property (object_class,
119 g_param_spec_object ("screen",
121 "Screen to get the settings from",
126 g_signal_new ("started",
127 G_TYPE_FROM_CLASS (object_class),
129 G_STRUCT_OFFSET (GtkTimelineClass, started),
131 g_cclosure_marshal_VOID__VOID,
135 g_signal_new ("paused",
136 G_TYPE_FROM_CLASS (object_class),
138 G_STRUCT_OFFSET (GtkTimelineClass, paused),
140 g_cclosure_marshal_VOID__VOID,
144 g_signal_new ("finished",
145 G_TYPE_FROM_CLASS (object_class),
147 G_STRUCT_OFFSET (GtkTimelineClass, finished),
149 g_cclosure_marshal_VOID__VOID,
153 g_signal_new ("frame",
154 G_TYPE_FROM_CLASS (object_class),
156 G_STRUCT_OFFSET (GtkTimelineClass, frame),
158 g_cclosure_marshal_VOID__DOUBLE,
162 g_type_class_add_private (klass, sizeof (GtkTimelinePriv));
166 _gtk_timeline_init (GtkTimeline *timeline)
168 GtkTimelinePriv *priv;
170 priv = timeline->priv = G_TYPE_INSTANCE_GET_PRIVATE (timeline,
174 priv->fps = DEFAULT_FPS;
175 priv->duration = 0.0;
176 priv->direction = GTK_TIMELINE_DIRECTION_FORWARD;
177 priv->screen = gdk_screen_get_default ();
179 priv->last_progress = 0;
183 gtk_timeline_set_property (GObject *object,
188 GtkTimeline *timeline;
189 GtkTimelinePriv *priv;
191 timeline = GTK_TIMELINE (object);
192 priv = timeline->priv;
197 _gtk_timeline_set_fps (timeline, g_value_get_uint (value));
200 _gtk_timeline_set_duration (timeline, g_value_get_uint (value));
203 _gtk_timeline_set_loop (timeline, g_value_get_boolean (value));
206 _gtk_timeline_set_direction (timeline, g_value_get_enum (value));
209 _gtk_timeline_set_screen (timeline,
210 GDK_SCREEN (g_value_get_object (value)));
213 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
218 gtk_timeline_get_property (GObject *object,
223 GtkTimeline *timeline;
224 GtkTimelinePriv *priv;
226 timeline = GTK_TIMELINE (object);
227 priv = timeline->priv;
232 g_value_set_uint (value, priv->fps);
235 g_value_set_uint (value, priv->duration);
238 g_value_set_boolean (value, priv->loop);
241 g_value_set_enum (value, priv->direction);
244 g_value_set_object (value, priv->screen);
247 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
252 _gtk_timeline_finalize (GObject *object)
254 GtkTimelinePriv *priv;
255 GtkTimeline *timeline;
257 timeline = (GtkTimeline *) object;
258 priv = timeline->priv;
262 g_source_remove (priv->source_id);
267 g_timer_destroy (priv->timer);
269 G_OBJECT_CLASS (_gtk_timeline_parent_class)->finalize (object);
273 calculate_progress (gdouble linear_progress,
274 GtkTimelineProgressType progress_type)
278 progress = linear_progress;
280 switch (progress_type)
282 case GTK_TIMELINE_PROGRESS_LINEAR:
284 case GTK_TIMELINE_PROGRESS_EASE_IN_OUT:
288 progress = pow (progress, 3) / 2;
290 progress = (pow (progress - 2, 3) + 2) / 2;
293 case GTK_TIMELINE_PROGRESS_EASE:
294 progress = (sin ((progress - 0.5) * G_PI) + 1) / 2;
296 case GTK_TIMELINE_PROGRESS_EASE_IN:
297 progress = pow (progress, 3);
299 case GTK_TIMELINE_PROGRESS_EASE_OUT:
300 progress = pow (progress - 1, 3) + 1;
303 g_warning ("Timeline progress type not implemented");
310 gtk_timeline_run_frame (GtkTimeline *timeline)
312 GtkTimelinePriv *priv;
313 gdouble delta_progress, progress;
316 priv = timeline->priv;
318 elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
319 g_timer_start (priv->timer);
321 if (priv->animations_enabled)
323 delta_progress = (gdouble) elapsed_time / priv->duration;
324 progress = priv->last_progress;
326 if (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD)
327 progress -= delta_progress;
329 progress += delta_progress;
331 priv->last_progress = progress;
333 progress = CLAMP (progress, 0., 1.);
336 progress = (priv->direction == GTK_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0;
338 priv->progress = progress;
339 g_signal_emit (timeline, signals [FRAME], 0,
340 calculate_progress (progress, priv->progress_type));
342 if ((priv->direction == GTK_TIMELINE_DIRECTION_FORWARD && progress == 1.0) ||
343 (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD && progress == 0.0))
349 g_source_remove (priv->source_id);
352 g_timer_stop (priv->timer);
353 g_signal_emit (timeline, signals [FINISHED], 0);
357 _gtk_timeline_rewind (timeline);
365 * @duration: duration in milliseconds for the timeline
367 * Creates a new #GtkTimeline with the specified number of frames.
369 * Return Value: the newly created #GtkTimeline
372 _gtk_timeline_new (guint duration)
374 return g_object_new (GTK_TYPE_TIMELINE,
375 "duration", duration,
380 _gtk_timeline_new_for_screen (guint duration,
383 return g_object_new (GTK_TYPE_TIMELINE,
384 "duration", duration,
390 * gtk_timeline_start:
391 * @timeline: A #GtkTimeline
393 * Runs the timeline from the current frame.
396 _gtk_timeline_start (GtkTimeline *timeline)
398 GtkTimelinePriv *priv;
399 GtkSettings *settings;
400 gboolean enable_animations = FALSE;
402 g_return_if_fail (GTK_IS_TIMELINE (timeline));
404 priv = timeline->priv;
406 if (!priv->source_id)
409 g_timer_continue (priv->timer);
411 priv->timer = g_timer_new ();
414 g_assert (priv->fps > 0);
418 settings = gtk_settings_get_for_screen (priv->screen);
419 g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
422 priv->animations_enabled = (enable_animations == TRUE);
424 g_signal_emit (timeline, signals [STARTED], 0);
426 if (enable_animations)
427 priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
428 (GSourceFunc) gtk_timeline_run_frame,
431 priv->source_id = gdk_threads_add_idle ((GSourceFunc) gtk_timeline_run_frame,
437 * gtk_timeline_pause:
438 * @timeline: A #GtkTimeline
440 * Pauses the timeline.
443 _gtk_timeline_pause (GtkTimeline *timeline)
445 GtkTimelinePriv *priv;
447 g_return_if_fail (GTK_IS_TIMELINE (timeline));
449 priv = timeline->priv;
453 g_timer_stop (priv->timer);
454 g_source_remove (priv->source_id);
456 g_signal_emit (timeline, signals [PAUSED], 0);
461 * gtk_timeline_rewind:
462 * @timeline: A #GtkTimeline
464 * Rewinds the timeline.
467 _gtk_timeline_rewind (GtkTimeline *timeline)
469 GtkTimelinePriv *priv;
471 g_return_if_fail (GTK_IS_TIMELINE (timeline));
473 priv = timeline->priv;
475 if (_gtk_timeline_get_direction (timeline) != GTK_TIMELINE_DIRECTION_FORWARD)
476 priv->progress = priv->last_progress = 1.;
478 priv->progress = priv->last_progress = 0.;
483 g_timer_start (priv->timer);
485 if (!priv->source_id)
486 g_timer_stop (priv->timer);
491 * gtk_timeline_is_running:
492 * @timeline: A #GtkTimeline
494 * Returns whether the timeline is running or not.
496 * Return Value: %TRUE if the timeline is running
499 _gtk_timeline_is_running (GtkTimeline *timeline)
501 GtkTimelinePriv *priv;
503 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
505 priv = timeline->priv;
507 return (priv->source_id != 0);
511 * gtk_timeline_get_fps:
512 * @timeline: A #GtkTimeline
514 * Returns the number of frames per second.
516 * Return Value: frames per second
519 _gtk_timeline_get_fps (GtkTimeline *timeline)
521 GtkTimelinePriv *priv;
523 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 1);
525 priv = timeline->priv;
530 * gtk_timeline_set_fps:
531 * @timeline: A #GtkTimeline
532 * @fps: frames per second
534 * Sets the number of frames per second that
535 * the timeline will play.
538 _gtk_timeline_set_fps (GtkTimeline *timeline,
541 GtkTimelinePriv *priv;
543 g_return_if_fail (GTK_IS_TIMELINE (timeline));
544 g_return_if_fail (fps > 0);
546 priv = timeline->priv;
550 if (_gtk_timeline_is_running (timeline))
552 g_source_remove (priv->source_id);
553 priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
554 (GSourceFunc) gtk_timeline_run_frame,
558 g_object_notify (G_OBJECT (timeline), "fps");
562 * gtk_timeline_get_loop:
563 * @timeline: A #GtkTimeline
565 * Returns whether the timeline loops to the
566 * beginning when it has reached the end.
568 * Return Value: %TRUE if the timeline loops
571 _gtk_timeline_get_loop (GtkTimeline *timeline)
573 GtkTimelinePriv *priv;
575 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
577 priv = timeline->priv;
582 * gtk_timeline_set_loop:
583 * @timeline: A #GtkTimeline
584 * @loop: %TRUE to make the timeline loop
586 * Sets whether the timeline loops to the beginning
587 * when it has reached the end.
590 _gtk_timeline_set_loop (GtkTimeline *timeline,
593 GtkTimelinePriv *priv;
595 g_return_if_fail (GTK_IS_TIMELINE (timeline));
597 priv = timeline->priv;
599 if (loop != priv->loop)
602 g_object_notify (G_OBJECT (timeline), "loop");
607 _gtk_timeline_set_duration (GtkTimeline *timeline,
610 GtkTimelinePriv *priv;
612 g_return_if_fail (GTK_IS_TIMELINE (timeline));
614 priv = timeline->priv;
616 if (duration != priv->duration)
618 priv->duration = duration;
619 g_object_notify (G_OBJECT (timeline), "duration");
624 _gtk_timeline_get_duration (GtkTimeline *timeline)
626 GtkTimelinePriv *priv;
628 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
630 priv = timeline->priv;
632 return priv->duration;
636 * gtk_timeline_set_direction:
637 * @timeline: A #GtkTimeline
638 * @direction: direction
640 * Sets the direction of the timeline.
643 _gtk_timeline_set_direction (GtkTimeline *timeline,
644 GtkTimelineDirection direction)
646 GtkTimelinePriv *priv;
648 g_return_if_fail (GTK_IS_TIMELINE (timeline));
650 priv = timeline->priv;
651 priv->direction = direction;
655 * gtk_timeline_get_direction:
656 * @timeline: A #GtkTimeline
658 * Returns the direction of the timeline.
660 * Return Value: direction
663 _gtk_timeline_get_direction (GtkTimeline *timeline)
665 GtkTimelinePriv *priv;
667 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_DIRECTION_FORWARD);
669 priv = timeline->priv;
670 return priv->direction;
674 _gtk_timeline_set_screen (GtkTimeline *timeline,
677 GtkTimelinePriv *priv;
679 g_return_if_fail (GTK_IS_TIMELINE (timeline));
680 g_return_if_fail (GDK_IS_SCREEN (screen));
682 priv = timeline->priv;
685 g_object_unref (priv->screen);
687 priv->screen = g_object_ref (screen);
689 g_object_notify (G_OBJECT (timeline), "screen");
693 _gtk_timeline_get_screen (GtkTimeline *timeline)
695 GtkTimelinePriv *priv;
697 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
699 priv = timeline->priv;
704 _gtk_timeline_get_progress (GtkTimeline *timeline)
706 GtkTimelinePriv *priv;
708 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0.);
710 priv = timeline->priv;
711 return calculate_progress (priv->progress, priv->progress_type);
714 GtkTimelineProgressType
715 _gtk_timeline_get_progress_type (GtkTimeline *timeline)
717 GtkTimelinePriv *priv;
719 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_PROGRESS_LINEAR);
721 priv = timeline->priv;
722 return priv->progress_type;
726 _gtk_timeline_set_progress_type (GtkTimeline *timeline,
727 GtkTimelineProgressType progress_type)
729 GtkTimelinePriv *priv;
731 g_return_if_fail (GTK_IS_TIMELINE (timeline));
733 priv = timeline->priv;
734 priv->progress_type = progress_type;