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))
347 g_source_remove (priv->source_id);
350 g_timer_stop (priv->timer);
351 g_signal_emit (timeline, signals [FINISHED], 0);
355 _gtk_timeline_rewind (timeline);
363 * @duration: duration in milliseconds for the timeline
365 * Creates a new #GtkTimeline with the specified number of frames.
367 * Return Value: the newly created #GtkTimeline
370 _gtk_timeline_new (guint duration)
372 return g_object_new (GTK_TYPE_TIMELINE,
373 "duration", duration,
378 _gtk_timeline_new_for_screen (guint duration,
381 return g_object_new (GTK_TYPE_TIMELINE,
382 "duration", duration,
388 * gtk_timeline_start:
389 * @timeline: A #GtkTimeline
391 * Runs the timeline from the current frame.
394 _gtk_timeline_start (GtkTimeline *timeline)
396 GtkTimelinePriv *priv;
397 GtkSettings *settings;
398 gboolean enable_animations = FALSE;
400 g_return_if_fail (GTK_IS_TIMELINE (timeline));
402 priv = timeline->priv;
404 if (!priv->source_id)
407 g_timer_continue (priv->timer);
409 priv->timer = g_timer_new ();
412 g_assert (priv->fps > 0);
416 settings = gtk_settings_get_for_screen (priv->screen);
417 g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
420 priv->animations_enabled = (enable_animations == TRUE);
422 g_signal_emit (timeline, signals [STARTED], 0);
424 if (enable_animations)
425 priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
426 (GSourceFunc) gtk_timeline_run_frame,
429 priv->source_id = gdk_threads_add_idle ((GSourceFunc) gtk_timeline_run_frame,
435 * gtk_timeline_pause:
436 * @timeline: A #GtkTimeline
438 * Pauses the timeline.
441 _gtk_timeline_pause (GtkTimeline *timeline)
443 GtkTimelinePriv *priv;
445 g_return_if_fail (GTK_IS_TIMELINE (timeline));
447 priv = timeline->priv;
451 g_timer_stop (priv->timer);
452 g_source_remove (priv->source_id);
454 g_signal_emit (timeline, signals [PAUSED], 0);
459 * gtk_timeline_rewind:
460 * @timeline: A #GtkTimeline
462 * Rewinds the timeline.
465 _gtk_timeline_rewind (GtkTimeline *timeline)
467 GtkTimelinePriv *priv;
469 g_return_if_fail (GTK_IS_TIMELINE (timeline));
471 priv = timeline->priv;
473 if (_gtk_timeline_get_direction (timeline) != GTK_TIMELINE_DIRECTION_FORWARD)
474 priv->progress = priv->last_progress = 1.;
476 priv->progress = priv->last_progress = 0.;
481 g_timer_start (priv->timer);
483 if (!priv->source_id)
484 g_timer_stop (priv->timer);
489 * gtk_timeline_is_running:
490 * @timeline: A #GtkTimeline
492 * Returns whether the timeline is running or not.
494 * Return Value: %TRUE if the timeline is running
497 _gtk_timeline_is_running (GtkTimeline *timeline)
499 GtkTimelinePriv *priv;
501 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
503 priv = timeline->priv;
505 return (priv->source_id != 0);
509 * gtk_timeline_get_fps:
510 * @timeline: A #GtkTimeline
512 * Returns the number of frames per second.
514 * Return Value: frames per second
517 _gtk_timeline_get_fps (GtkTimeline *timeline)
519 GtkTimelinePriv *priv;
521 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 1);
523 priv = timeline->priv;
528 * gtk_timeline_set_fps:
529 * @timeline: A #GtkTimeline
530 * @fps: frames per second
532 * Sets the number of frames per second that
533 * the timeline will play.
536 _gtk_timeline_set_fps (GtkTimeline *timeline,
539 GtkTimelinePriv *priv;
541 g_return_if_fail (GTK_IS_TIMELINE (timeline));
542 g_return_if_fail (fps > 0);
544 priv = timeline->priv;
548 if (_gtk_timeline_is_running (timeline))
550 g_source_remove (priv->source_id);
551 priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
552 (GSourceFunc) gtk_timeline_run_frame,
556 g_object_notify (G_OBJECT (timeline), "fps");
560 * gtk_timeline_get_loop:
561 * @timeline: A #GtkTimeline
563 * Returns whether the timeline loops to the
564 * beginning when it has reached the end.
566 * Return Value: %TRUE if the timeline loops
569 _gtk_timeline_get_loop (GtkTimeline *timeline)
571 GtkTimelinePriv *priv;
573 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
575 priv = timeline->priv;
580 * gtk_timeline_set_loop:
581 * @timeline: A #GtkTimeline
582 * @loop: %TRUE to make the timeline loop
584 * Sets whether the timeline loops to the beginning
585 * when it has reached the end.
588 _gtk_timeline_set_loop (GtkTimeline *timeline,
591 GtkTimelinePriv *priv;
593 g_return_if_fail (GTK_IS_TIMELINE (timeline));
595 priv = timeline->priv;
597 if (loop != priv->loop)
600 g_object_notify (G_OBJECT (timeline), "loop");
605 _gtk_timeline_set_duration (GtkTimeline *timeline,
608 GtkTimelinePriv *priv;
610 g_return_if_fail (GTK_IS_TIMELINE (timeline));
612 priv = timeline->priv;
614 if (duration != priv->duration)
616 priv->duration = duration;
617 g_object_notify (G_OBJECT (timeline), "duration");
622 _gtk_timeline_get_duration (GtkTimeline *timeline)
624 GtkTimelinePriv *priv;
626 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
628 priv = timeline->priv;
630 return priv->duration;
634 * gtk_timeline_set_direction:
635 * @timeline: A #GtkTimeline
636 * @direction: direction
638 * Sets the direction of the timeline.
641 _gtk_timeline_set_direction (GtkTimeline *timeline,
642 GtkTimelineDirection direction)
644 GtkTimelinePriv *priv;
646 g_return_if_fail (GTK_IS_TIMELINE (timeline));
648 priv = timeline->priv;
649 priv->direction = direction;
653 * gtk_timeline_get_direction:
654 * @timeline: A #GtkTimeline
656 * Returns the direction of the timeline.
658 * Return Value: direction
661 _gtk_timeline_get_direction (GtkTimeline *timeline)
663 GtkTimelinePriv *priv;
665 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_DIRECTION_FORWARD);
667 priv = timeline->priv;
668 return priv->direction;
672 _gtk_timeline_set_screen (GtkTimeline *timeline,
675 GtkTimelinePriv *priv;
677 g_return_if_fail (GTK_IS_TIMELINE (timeline));
678 g_return_if_fail (GDK_IS_SCREEN (screen));
680 priv = timeline->priv;
683 g_object_unref (priv->screen);
685 priv->screen = g_object_ref (screen);
687 g_object_notify (G_OBJECT (timeline), "screen");
691 _gtk_timeline_get_screen (GtkTimeline *timeline)
693 GtkTimelinePriv *priv;
695 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
697 priv = timeline->priv;
702 _gtk_timeline_get_progress (GtkTimeline *timeline)
704 GtkTimelinePriv *priv;
706 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0.);
708 priv = timeline->priv;
709 return calculate_progress (priv->progress, priv->progress_type);
712 GtkTimelineProgressType
713 _gtk_timeline_get_progress_type (GtkTimeline *timeline)
715 GtkTimelinePriv *priv;
717 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_PROGRESS_LINEAR);
719 priv = timeline->priv;
720 return priv->progress_type;
724 _gtk_timeline_set_progress_type (GtkTimeline *timeline,
725 GtkTimelineProgressType progress_type)
727 GtkTimelinePriv *priv;
729 g_return_if_fail (GTK_IS_TIMELINE (timeline));
731 priv = timeline->priv;
732 priv->progress_type = progress_type;