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
41 gdouble last_progress;
45 GtkTimelineProgressType progress_type;
47 guint animations_enabled : 1;
69 static guint signals [LAST_SIGNAL] = { 0, };
72 static void gtk_timeline_set_property (GObject *object,
76 static void gtk_timeline_get_property (GObject *object,
80 static void _gtk_timeline_finalize (GObject *object);
83 G_DEFINE_TYPE (GtkTimeline, _gtk_timeline, G_TYPE_OBJECT)
87 _gtk_timeline_class_init (GtkTimelineClass *klass)
89 GObjectClass *object_class = G_OBJECT_CLASS (klass);
91 object_class->set_property = gtk_timeline_set_property;
92 object_class->get_property = gtk_timeline_get_property;
93 object_class->finalize = _gtk_timeline_finalize;
95 g_object_class_install_property (object_class,
97 g_param_spec_uint ("fps",
99 "Frames per second for the timeline",
103 g_object_class_install_property (object_class,
105 g_param_spec_uint ("duration",
106 "Animation Duration",
107 "Animation Duration",
111 g_object_class_install_property (object_class,
113 g_param_spec_boolean ("loop",
115 "Whether the timeline loops or not",
118 g_object_class_install_property (object_class,
120 g_param_spec_object ("screen",
122 "Screen to get the settings from",
127 g_signal_new ("started",
128 G_TYPE_FROM_CLASS (object_class),
130 G_STRUCT_OFFSET (GtkTimelineClass, started),
132 g_cclosure_marshal_VOID__VOID,
136 g_signal_new ("paused",
137 G_TYPE_FROM_CLASS (object_class),
139 G_STRUCT_OFFSET (GtkTimelineClass, paused),
141 g_cclosure_marshal_VOID__VOID,
145 g_signal_new ("finished",
146 G_TYPE_FROM_CLASS (object_class),
148 G_STRUCT_OFFSET (GtkTimelineClass, finished),
150 g_cclosure_marshal_VOID__VOID,
154 g_signal_new ("frame",
155 G_TYPE_FROM_CLASS (object_class),
157 G_STRUCT_OFFSET (GtkTimelineClass, frame),
159 g_cclosure_marshal_VOID__DOUBLE,
163 g_type_class_add_private (klass, sizeof (GtkTimelinePriv));
167 _gtk_timeline_init (GtkTimeline *timeline)
169 GtkTimelinePriv *priv;
171 priv = timeline->priv = G_TYPE_INSTANCE_GET_PRIVATE (timeline,
175 priv->fps = DEFAULT_FPS;
176 priv->duration = 0.0;
177 priv->direction = GTK_TIMELINE_DIRECTION_FORWARD;
178 priv->screen = gdk_screen_get_default ();
180 priv->last_progress = 0;
184 gtk_timeline_set_property (GObject *object,
189 GtkTimeline *timeline;
191 timeline = GTK_TIMELINE (object);
196 _gtk_timeline_set_fps (timeline, g_value_get_uint (value));
199 _gtk_timeline_set_duration (timeline, g_value_get_uint (value));
202 _gtk_timeline_set_loop (timeline, g_value_get_boolean (value));
205 _gtk_timeline_set_direction (timeline, g_value_get_enum (value));
208 _gtk_timeline_set_screen (timeline,
209 GDK_SCREEN (g_value_get_object (value)));
212 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
217 gtk_timeline_get_property (GObject *object,
222 GtkTimeline *timeline;
223 GtkTimelinePriv *priv;
225 timeline = GTK_TIMELINE (object);
226 priv = timeline->priv;
231 g_value_set_uint (value, priv->fps);
234 g_value_set_uint (value, priv->duration);
237 g_value_set_boolean (value, priv->loop);
240 g_value_set_enum (value, priv->direction);
243 g_value_set_object (value, priv->screen);
246 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
251 _gtk_timeline_finalize (GObject *object)
253 GtkTimelinePriv *priv;
254 GtkTimeline *timeline;
256 timeline = (GtkTimeline *) object;
257 priv = timeline->priv;
261 g_source_remove (priv->source_id);
266 g_timer_destroy (priv->timer);
268 G_OBJECT_CLASS (_gtk_timeline_parent_class)->finalize (object);
272 calculate_progress (gdouble linear_progress,
273 GtkTimelineProgressType progress_type)
277 progress = linear_progress;
279 switch (progress_type)
281 case GTK_TIMELINE_PROGRESS_LINEAR:
283 case GTK_TIMELINE_PROGRESS_EASE_IN_OUT:
287 progress = pow (progress, 3) / 2;
289 progress = (pow (progress - 2, 3) + 2) / 2;
292 case GTK_TIMELINE_PROGRESS_EASE:
293 progress = (sin ((progress - 0.5) * G_PI) + 1) / 2;
295 case GTK_TIMELINE_PROGRESS_EASE_IN:
296 progress = pow (progress, 3);
298 case GTK_TIMELINE_PROGRESS_EASE_OUT:
299 progress = pow (progress - 1, 3) + 1;
302 g_warning ("Timeline progress type not implemented");
309 gtk_timeline_run_frame (GtkTimeline *timeline)
311 GtkTimelinePriv *priv;
312 gdouble delta_progress, progress;
314 /* the user may unref us during the signals, so save ourselves */
315 g_object_ref (timeline);
317 priv = timeline->priv;
319 priv->elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
320 g_timer_start (priv->timer);
322 if (priv->animations_enabled)
324 delta_progress = (gdouble) priv->elapsed_time / priv->duration;
325 progress = priv->last_progress;
327 if (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD)
328 progress -= delta_progress;
330 progress += delta_progress;
332 priv->last_progress = progress;
334 progress = CLAMP (progress, 0., 1.);
337 progress = (priv->direction == GTK_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0;
339 priv->progress = progress;
340 g_signal_emit (timeline, signals [FRAME], 0,
341 calculate_progress (progress, priv->progress_type));
343 if ((priv->direction == GTK_TIMELINE_DIRECTION_FORWARD && progress == 1.0) ||
344 (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD && progress == 0.0))
348 loop = priv->loop && priv->animations_enabled;
351 _gtk_timeline_rewind (timeline);
356 g_source_remove (priv->source_id);
359 g_timer_stop (priv->timer);
360 g_signal_emit (timeline, signals [FINISHED], 0);
361 g_object_unref (timeline);
366 g_object_unref (timeline);
373 * @duration: duration in milliseconds for the timeline
375 * Creates a new #GtkTimeline with the specified number of frames.
377 * Return Value: the newly created #GtkTimeline
380 _gtk_timeline_new (guint duration)
382 return g_object_new (GTK_TYPE_TIMELINE,
383 "duration", duration,
388 _gtk_timeline_new_for_screen (guint duration,
391 return g_object_new (GTK_TYPE_TIMELINE,
392 "duration", duration,
398 * _gtk_timeline_start:
399 * @timeline: A #GtkTimeline
401 * Runs the timeline from the current frame.
404 _gtk_timeline_start (GtkTimeline *timeline)
406 GtkTimelinePriv *priv;
407 GtkSettings *settings;
408 gboolean enable_animations = FALSE;
410 g_return_if_fail (GTK_IS_TIMELINE (timeline));
412 priv = timeline->priv;
414 if (!priv->source_id)
417 g_timer_continue (priv->timer);
419 priv->timer = g_timer_new ();
422 g_assert (priv->fps > 0);
426 settings = gtk_settings_get_for_screen (priv->screen);
427 g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
430 priv->animations_enabled = enable_animations;
432 g_signal_emit (timeline, signals [STARTED], 0);
434 if (enable_animations)
435 priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
436 (GSourceFunc) gtk_timeline_run_frame,
439 priv->source_id = gdk_threads_add_idle ((GSourceFunc) gtk_timeline_run_frame,
445 * _gtk_timeline_pause:
446 * @timeline: A #GtkTimeline
448 * Pauses the timeline.
451 _gtk_timeline_pause (GtkTimeline *timeline)
453 GtkTimelinePriv *priv;
455 g_return_if_fail (GTK_IS_TIMELINE (timeline));
457 priv = timeline->priv;
461 g_timer_stop (priv->timer);
462 g_source_remove (priv->source_id);
464 g_signal_emit (timeline, signals [PAUSED], 0);
469 * _gtk_timeline_rewind:
470 * @timeline: A #GtkTimeline
472 * Rewinds the timeline.
475 _gtk_timeline_rewind (GtkTimeline *timeline)
477 GtkTimelinePriv *priv;
479 g_return_if_fail (GTK_IS_TIMELINE (timeline));
481 priv = timeline->priv;
483 if (_gtk_timeline_get_direction (timeline) != GTK_TIMELINE_DIRECTION_FORWARD)
484 priv->progress = priv->last_progress = 1.;
486 priv->progress = priv->last_progress = 0.;
491 g_timer_start (priv->timer);
493 if (!priv->source_id)
494 g_timer_stop (priv->timer);
499 * _gtk_timeline_is_running:
500 * @timeline: A #GtkTimeline
502 * Returns whether the timeline is running or not.
504 * Return Value: %TRUE if the timeline is running
507 _gtk_timeline_is_running (GtkTimeline *timeline)
509 GtkTimelinePriv *priv;
511 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
513 priv = timeline->priv;
515 return (priv->source_id != 0);
519 * _gtk_timeline_get_elapsed_time:
520 * @timeline: A #GtkTimeline
522 * Returns the elapsed time since the last GtkTimeline::frame signal
524 * Return Value: elapsed time in milliseconds since the last frame
527 _gtk_timeline_get_elapsed_time (GtkTimeline *timeline)
529 GtkTimelinePriv *priv;
531 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
533 priv = timeline->priv;
534 return priv->elapsed_time;
538 * _gtk_timeline_get_fps:
539 * @timeline: A #GtkTimeline
541 * Returns the number of frames per second.
543 * Return Value: frames per second
546 _gtk_timeline_get_fps (GtkTimeline *timeline)
548 GtkTimelinePriv *priv;
550 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 1);
552 priv = timeline->priv;
557 * _gtk_timeline_set_fps:
558 * @timeline: A #GtkTimeline
559 * @fps: frames per second
561 * Sets the number of frames per second that
562 * the timeline will play.
565 _gtk_timeline_set_fps (GtkTimeline *timeline,
568 GtkTimelinePriv *priv;
570 g_return_if_fail (GTK_IS_TIMELINE (timeline));
571 g_return_if_fail (fps > 0);
573 priv = timeline->priv;
577 if (_gtk_timeline_is_running (timeline))
579 g_source_remove (priv->source_id);
580 priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
581 (GSourceFunc) gtk_timeline_run_frame,
585 g_object_notify (G_OBJECT (timeline), "fps");
589 * _gtk_timeline_get_loop:
590 * @timeline: A #GtkTimeline
592 * Returns whether the timeline loops to the
593 * beginning when it has reached the end.
595 * Return Value: %TRUE if the timeline loops
598 _gtk_timeline_get_loop (GtkTimeline *timeline)
600 GtkTimelinePriv *priv;
602 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
604 priv = timeline->priv;
609 * _gtk_timeline_set_loop:
610 * @timeline: A #GtkTimeline
611 * @loop: %TRUE to make the timeline loop
613 * Sets whether the timeline loops to the beginning
614 * when it has reached the end.
617 _gtk_timeline_set_loop (GtkTimeline *timeline,
620 GtkTimelinePriv *priv;
622 g_return_if_fail (GTK_IS_TIMELINE (timeline));
624 priv = timeline->priv;
626 if (loop != priv->loop)
629 g_object_notify (G_OBJECT (timeline), "loop");
634 _gtk_timeline_set_duration (GtkTimeline *timeline,
637 GtkTimelinePriv *priv;
639 g_return_if_fail (GTK_IS_TIMELINE (timeline));
641 priv = timeline->priv;
643 if (duration != priv->duration)
645 priv->duration = duration;
646 g_object_notify (G_OBJECT (timeline), "duration");
651 _gtk_timeline_get_duration (GtkTimeline *timeline)
653 GtkTimelinePriv *priv;
655 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
657 priv = timeline->priv;
659 return priv->duration;
663 * _gtk_timeline_set_direction:
664 * @timeline: A #GtkTimeline
665 * @direction: direction
667 * Sets the direction of the timeline.
670 _gtk_timeline_set_direction (GtkTimeline *timeline,
671 GtkTimelineDirection direction)
673 GtkTimelinePriv *priv;
675 g_return_if_fail (GTK_IS_TIMELINE (timeline));
677 priv = timeline->priv;
678 priv->direction = direction;
682 * _gtk_timeline_get_direction:
683 * @timeline: A #GtkTimeline
685 * Returns the direction of the timeline.
687 * Return Value: direction
690 _gtk_timeline_get_direction (GtkTimeline *timeline)
692 GtkTimelinePriv *priv;
694 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_DIRECTION_FORWARD);
696 priv = timeline->priv;
697 return priv->direction;
701 _gtk_timeline_set_screen (GtkTimeline *timeline,
704 GtkTimelinePriv *priv;
706 g_return_if_fail (GTK_IS_TIMELINE (timeline));
707 g_return_if_fail (GDK_IS_SCREEN (screen));
709 priv = timeline->priv;
712 g_object_unref (priv->screen);
714 priv->screen = g_object_ref (screen);
716 g_object_notify (G_OBJECT (timeline), "screen");
720 _gtk_timeline_get_screen (GtkTimeline *timeline)
722 GtkTimelinePriv *priv;
724 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
726 priv = timeline->priv;
731 _gtk_timeline_get_progress (GtkTimeline *timeline)
733 GtkTimelinePriv *priv;
735 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0.);
737 priv = timeline->priv;
738 return calculate_progress (priv->progress, priv->progress_type);
741 GtkTimelineProgressType
742 _gtk_timeline_get_progress_type (GtkTimeline *timeline)
744 GtkTimelinePriv *priv;
746 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_PROGRESS_LINEAR);
748 priv = timeline->priv;
749 return priv->progress_type;
753 _gtk_timeline_set_progress_type (GtkTimeline *timeline,
754 GtkTimelineProgressType progress_type)
756 GtkTimelinePriv *priv;
758 g_return_if_fail (GTK_IS_TIMELINE (timeline));
760 priv = timeline->priv;
761 priv->progress_type = progress_type;