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;
190 timeline = GTK_TIMELINE (object);
195 _gtk_timeline_set_fps (timeline, g_value_get_uint (value));
198 _gtk_timeline_set_duration (timeline, g_value_get_uint (value));
201 _gtk_timeline_set_loop (timeline, g_value_get_boolean (value));
204 _gtk_timeline_set_direction (timeline, g_value_get_enum (value));
207 _gtk_timeline_set_screen (timeline,
208 GDK_SCREEN (g_value_get_object (value)));
211 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
216 gtk_timeline_get_property (GObject *object,
221 GtkTimeline *timeline;
222 GtkTimelinePriv *priv;
224 timeline = GTK_TIMELINE (object);
225 priv = timeline->priv;
230 g_value_set_uint (value, priv->fps);
233 g_value_set_uint (value, priv->duration);
236 g_value_set_boolean (value, priv->loop);
239 g_value_set_enum (value, priv->direction);
242 g_value_set_object (value, priv->screen);
245 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
250 _gtk_timeline_finalize (GObject *object)
252 GtkTimelinePriv *priv;
253 GtkTimeline *timeline;
255 timeline = (GtkTimeline *) object;
256 priv = timeline->priv;
260 g_source_remove (priv->source_id);
265 g_timer_destroy (priv->timer);
267 G_OBJECT_CLASS (_gtk_timeline_parent_class)->finalize (object);
271 calculate_progress (gdouble linear_progress,
272 GtkTimelineProgressType progress_type)
276 progress = linear_progress;
278 switch (progress_type)
280 case GTK_TIMELINE_PROGRESS_LINEAR:
282 case GTK_TIMELINE_PROGRESS_EASE_IN_OUT:
286 progress = pow (progress, 3) / 2;
288 progress = (pow (progress - 2, 3) + 2) / 2;
291 case GTK_TIMELINE_PROGRESS_EASE:
292 progress = (sin ((progress - 0.5) * G_PI) + 1) / 2;
294 case GTK_TIMELINE_PROGRESS_EASE_IN:
295 progress = pow (progress, 3);
297 case GTK_TIMELINE_PROGRESS_EASE_OUT:
298 progress = pow (progress - 1, 3) + 1;
301 g_warning ("Timeline progress type not implemented");
308 gtk_timeline_run_frame (GtkTimeline *timeline)
310 GtkTimelinePriv *priv;
311 gdouble delta_progress, progress;
314 priv = timeline->priv;
316 elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
317 g_timer_start (priv->timer);
319 if (priv->animations_enabled)
321 delta_progress = (gdouble) elapsed_time / priv->duration;
322 progress = priv->last_progress;
324 if (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD)
325 progress -= delta_progress;
327 progress += delta_progress;
329 priv->last_progress = progress;
331 progress = CLAMP (progress, 0., 1.);
334 progress = (priv->direction == GTK_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0;
336 priv->progress = progress;
337 g_signal_emit (timeline, signals [FRAME], 0,
338 calculate_progress (progress, priv->progress_type));
340 if ((priv->direction == GTK_TIMELINE_DIRECTION_FORWARD && progress == 1.0) ||
341 (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD && progress == 0.0))
345 loop = priv->loop && priv->animations_enabled;
348 _gtk_timeline_rewind (timeline);
353 g_source_remove (priv->source_id);
356 g_timer_stop (priv->timer);
357 g_signal_emit (timeline, signals [FINISHED], 0);
367 * @duration: duration in milliseconds for the timeline
369 * Creates a new #GtkTimeline with the specified number of frames.
371 * Return Value: the newly created #GtkTimeline
374 _gtk_timeline_new (guint duration)
376 return g_object_new (GTK_TYPE_TIMELINE,
377 "duration", duration,
382 _gtk_timeline_new_for_screen (guint duration,
385 return g_object_new (GTK_TYPE_TIMELINE,
386 "duration", duration,
392 * gtk_timeline_start:
393 * @timeline: A #GtkTimeline
395 * Runs the timeline from the current frame.
398 _gtk_timeline_start (GtkTimeline *timeline)
400 GtkTimelinePriv *priv;
401 GtkSettings *settings;
402 gboolean enable_animations = FALSE;
404 g_return_if_fail (GTK_IS_TIMELINE (timeline));
406 priv = timeline->priv;
408 if (!priv->source_id)
411 g_timer_continue (priv->timer);
413 priv->timer = g_timer_new ();
416 g_assert (priv->fps > 0);
420 settings = gtk_settings_get_for_screen (priv->screen);
421 g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
424 priv->animations_enabled = enable_animations;
426 g_signal_emit (timeline, signals [STARTED], 0);
428 if (enable_animations)
429 priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
430 (GSourceFunc) gtk_timeline_run_frame,
433 priv->source_id = gdk_threads_add_idle ((GSourceFunc) gtk_timeline_run_frame,
439 * gtk_timeline_pause:
440 * @timeline: A #GtkTimeline
442 * Pauses the timeline.
445 _gtk_timeline_pause (GtkTimeline *timeline)
447 GtkTimelinePriv *priv;
449 g_return_if_fail (GTK_IS_TIMELINE (timeline));
451 priv = timeline->priv;
455 g_timer_stop (priv->timer);
456 g_source_remove (priv->source_id);
458 g_signal_emit (timeline, signals [PAUSED], 0);
463 * gtk_timeline_rewind:
464 * @timeline: A #GtkTimeline
466 * Rewinds the timeline.
469 _gtk_timeline_rewind (GtkTimeline *timeline)
471 GtkTimelinePriv *priv;
473 g_return_if_fail (GTK_IS_TIMELINE (timeline));
475 priv = timeline->priv;
477 if (_gtk_timeline_get_direction (timeline) != GTK_TIMELINE_DIRECTION_FORWARD)
478 priv->progress = priv->last_progress = 1.;
480 priv->progress = priv->last_progress = 0.;
485 g_timer_start (priv->timer);
487 if (!priv->source_id)
488 g_timer_stop (priv->timer);
493 * gtk_timeline_is_running:
494 * @timeline: A #GtkTimeline
496 * Returns whether the timeline is running or not.
498 * Return Value: %TRUE if the timeline is running
501 _gtk_timeline_is_running (GtkTimeline *timeline)
503 GtkTimelinePriv *priv;
505 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
507 priv = timeline->priv;
509 return (priv->source_id != 0);
513 * gtk_timeline_get_fps:
514 * @timeline: A #GtkTimeline
516 * Returns the number of frames per second.
518 * Return Value: frames per second
521 _gtk_timeline_get_fps (GtkTimeline *timeline)
523 GtkTimelinePriv *priv;
525 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 1);
527 priv = timeline->priv;
532 * gtk_timeline_set_fps:
533 * @timeline: A #GtkTimeline
534 * @fps: frames per second
536 * Sets the number of frames per second that
537 * the timeline will play.
540 _gtk_timeline_set_fps (GtkTimeline *timeline,
543 GtkTimelinePriv *priv;
545 g_return_if_fail (GTK_IS_TIMELINE (timeline));
546 g_return_if_fail (fps > 0);
548 priv = timeline->priv;
552 if (_gtk_timeline_is_running (timeline))
554 g_source_remove (priv->source_id);
555 priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
556 (GSourceFunc) gtk_timeline_run_frame,
560 g_object_notify (G_OBJECT (timeline), "fps");
564 * gtk_timeline_get_loop:
565 * @timeline: A #GtkTimeline
567 * Returns whether the timeline loops to the
568 * beginning when it has reached the end.
570 * Return Value: %TRUE if the timeline loops
573 _gtk_timeline_get_loop (GtkTimeline *timeline)
575 GtkTimelinePriv *priv;
577 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
579 priv = timeline->priv;
584 * gtk_timeline_set_loop:
585 * @timeline: A #GtkTimeline
586 * @loop: %TRUE to make the timeline loop
588 * Sets whether the timeline loops to the beginning
589 * when it has reached the end.
592 _gtk_timeline_set_loop (GtkTimeline *timeline,
595 GtkTimelinePriv *priv;
597 g_return_if_fail (GTK_IS_TIMELINE (timeline));
599 priv = timeline->priv;
601 if (loop != priv->loop)
604 g_object_notify (G_OBJECT (timeline), "loop");
609 _gtk_timeline_set_duration (GtkTimeline *timeline,
612 GtkTimelinePriv *priv;
614 g_return_if_fail (GTK_IS_TIMELINE (timeline));
616 priv = timeline->priv;
618 if (duration != priv->duration)
620 priv->duration = duration;
621 g_object_notify (G_OBJECT (timeline), "duration");
626 _gtk_timeline_get_duration (GtkTimeline *timeline)
628 GtkTimelinePriv *priv;
630 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
632 priv = timeline->priv;
634 return priv->duration;
638 * gtk_timeline_set_direction:
639 * @timeline: A #GtkTimeline
640 * @direction: direction
642 * Sets the direction of the timeline.
645 _gtk_timeline_set_direction (GtkTimeline *timeline,
646 GtkTimelineDirection direction)
648 GtkTimelinePriv *priv;
650 g_return_if_fail (GTK_IS_TIMELINE (timeline));
652 priv = timeline->priv;
653 priv->direction = direction;
657 * gtk_timeline_get_direction:
658 * @timeline: A #GtkTimeline
660 * Returns the direction of the timeline.
662 * Return Value: direction
665 _gtk_timeline_get_direction (GtkTimeline *timeline)
667 GtkTimelinePriv *priv;
669 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_DIRECTION_FORWARD);
671 priv = timeline->priv;
672 return priv->direction;
676 _gtk_timeline_set_screen (GtkTimeline *timeline,
679 GtkTimelinePriv *priv;
681 g_return_if_fail (GTK_IS_TIMELINE (timeline));
682 g_return_if_fail (GDK_IS_SCREEN (screen));
684 priv = timeline->priv;
687 g_object_unref (priv->screen);
689 priv->screen = g_object_ref (screen);
691 g_object_notify (G_OBJECT (timeline), "screen");
695 _gtk_timeline_get_screen (GtkTimeline *timeline)
697 GtkTimelinePriv *priv;
699 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
701 priv = timeline->priv;
706 _gtk_timeline_get_progress (GtkTimeline *timeline)
708 GtkTimelinePriv *priv;
710 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0.);
712 priv = timeline->priv;
713 return calculate_progress (priv->progress, priv->progress_type);
716 GtkTimelineProgressType
717 _gtk_timeline_get_progress_type (GtkTimeline *timeline)
719 GtkTimelinePriv *priv;
721 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_PROGRESS_LINEAR);
723 priv = timeline->priv;
724 return priv->progress_type;
728 _gtk_timeline_set_progress_type (GtkTimeline *timeline,
729 GtkTimelineProgressType progress_type)
731 GtkTimelinePriv *priv;
733 g_return_if_fail (GTK_IS_TIMELINE (timeline));
735 priv = timeline->priv;
736 priv->progress_type = progress_type;