--- /dev/null
+/* ev-timeline.c
+ * this file is part of evince, a gnome document viewer
+ *
+ * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib.h>
+#include <math.h>
+#include <gdk/gdk.h>
+#include "ev-timeline.h"
+
+#define EV_TIMELINE_GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EV_TYPE_TIMELINE, EvTimelinePriv))
+#define MSECS_PER_SEC 1000
+#define FRAME_INTERVAL(nframes) (MSECS_PER_SEC / nframes)
+#define DEFAULT_FPS 30
+
+typedef struct EvTimelinePriv EvTimelinePriv;
+
+struct EvTimelinePriv {
+ guint duration;
+ guint fps;
+ guint source_id;
+
+ GTimer *timer;
+
+ guint loop : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_FPS,
+ PROP_DURATION,
+ PROP_LOOP
+};
+
+enum {
+ STARTED,
+ PAUSED,
+ FINISHED,
+ FRAME,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+
+G_DEFINE_TYPE (EvTimeline, ev_timeline, G_TYPE_OBJECT)
+
+
+static void
+ev_timeline_init (EvTimeline *timeline)
+{
+ EvTimelinePriv *priv;
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+
+ priv->fps = DEFAULT_FPS;
+ priv->duration = 0;
+}
+
+static void
+ev_timeline_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EvTimeline *timeline;
+
+ timeline = EV_TIMELINE (object);
+
+ switch (prop_id) {
+ case PROP_FPS:
+ ev_timeline_set_fps (timeline, g_value_get_uint (value));
+ break;
+ case PROP_DURATION:
+ ev_timeline_set_duration (timeline, g_value_get_uint (value));
+ break;
+ case PROP_LOOP:
+ ev_timeline_set_loop (timeline, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ev_timeline_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EvTimeline *timeline;
+ EvTimelinePriv *priv;
+
+ timeline = EV_TIMELINE (object);
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+
+ switch (prop_id) {
+ case PROP_FPS:
+ g_value_set_uint (value, priv->fps);
+ break;
+ case PROP_DURATION:
+ g_value_set_uint (value, priv->duration);
+ break;
+ case PROP_LOOP:
+ g_value_set_boolean (value, priv->loop);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ev_timeline_finalize (GObject *object)
+{
+ EvTimelinePriv *priv;
+
+ priv = EV_TIMELINE_GET_PRIV (object);
+
+ if (priv->source_id) {
+ g_source_remove (priv->source_id);
+ priv->source_id = 0;
+ }
+
+ if (priv->timer)
+ g_timer_destroy (priv->timer);
+
+ G_OBJECT_CLASS (ev_timeline_parent_class)->finalize (object);
+}
+
+static gboolean
+ev_timeline_run_frame (EvTimeline *timeline)
+{
+ EvTimelinePriv *priv;
+ gdouble progress;
+ guint elapsed_time;
+
+ GDK_THREADS_ENTER ();
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+
+ elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
+ progress = (gdouble) elapsed_time / priv->duration;
+ progress = CLAMP (progress, 0., 1.);
+
+ g_signal_emit (timeline, signals [FRAME], 0, progress);
+
+ if (progress >= 1.0) {
+ if (!priv->loop) {
+ if (priv->source_id) {
+ g_source_remove (priv->source_id);
+ priv->source_id = 0;
+ }
+
+ g_signal_emit (timeline, signals [FINISHED], 0);
+ return FALSE;
+ } else {
+ ev_timeline_rewind (timeline);
+ }
+ }
+
+ GDK_THREADS_LEAVE ();
+
+ return TRUE;
+}
+
+static void
+ev_timeline_real_start (EvTimeline *timeline)
+{
+ EvTimelinePriv *priv;
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+
+ if (!priv->source_id) {
+ if (priv->timer)
+ g_timer_continue (priv->timer);
+ else
+ priv->timer = g_timer_new ();
+
+ /* sanity check */
+ g_assert (priv->fps > 0);
+
+ g_signal_emit (timeline, signals [STARTED], 0);
+
+ priv->source_id = g_timeout_add (FRAME_INTERVAL (priv->fps),
+ (GSourceFunc) ev_timeline_run_frame,
+ timeline);
+ }
+}
+
+static void
+ev_timeline_class_init (EvTimelineClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->set_property = ev_timeline_set_property;
+ object_class->get_property = ev_timeline_get_property;
+ object_class->finalize = ev_timeline_finalize;
+
+ class->start = ev_timeline_real_start;
+
+ g_object_class_install_property (object_class,
+ PROP_FPS,
+ g_param_spec_uint ("fps",
+ "FPS",
+ "Frames per second for the timeline",
+ 1,
+ G_MAXUINT,
+ DEFAULT_FPS,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_DURATION,
+ g_param_spec_uint ("duration",
+ "Animation Duration",
+ "Animation Duration",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOOP,
+ g_param_spec_boolean ("loop",
+ "Loop",
+ "Whether the timeline loops or not",
+ FALSE,
+ G_PARAM_READWRITE));
+ signals[STARTED] =
+ g_signal_new ("started",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EvTimelineClass, started),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[PAUSED] =
+ g_signal_new ("paused",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EvTimelineClass, paused),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[FINISHED] =
+ g_signal_new ("finished",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EvTimelineClass, finished),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[FRAME] =
+ g_signal_new ("frame",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EvTimelineClass, frame),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__DOUBLE,
+ G_TYPE_NONE, 1,
+ G_TYPE_DOUBLE);
+
+ g_type_class_add_private (class, sizeof (EvTimelinePriv));
+}
+
+EvTimeline *
+ev_timeline_new (guint duration)
+{
+ return g_object_new (EV_TYPE_TIMELINE,
+ "duration", duration,
+ NULL);
+}
+
+void
+ev_timeline_start (EvTimeline *timeline)
+{
+ g_return_if_fail (EV_IS_TIMELINE (timeline));
+
+ EV_TIMELINE_GET_CLASS (timeline)->start (timeline);
+}
+
+void
+ev_timeline_pause (EvTimeline *timeline)
+{
+ EvTimelinePriv *priv;
+
+ g_return_if_fail (EV_IS_TIMELINE (timeline));
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+
+ if (priv->source_id) {
+ g_source_remove (priv->source_id);
+ priv->source_id = 0;
+ g_timer_stop (priv->timer);
+ g_signal_emit (timeline, signals [PAUSED], 0);
+ }
+}
+
+void
+ev_timeline_rewind (EvTimeline *timeline)
+{
+ EvTimelinePriv *priv;
+
+ g_return_if_fail (EV_IS_TIMELINE (timeline));
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+
+ /* destroy and re-create timer if neccesary */
+ if (priv->timer) {
+ g_timer_destroy (priv->timer);
+
+ if (ev_timeline_is_running (timeline))
+ priv->timer = g_timer_new ();
+ else
+ priv->timer = NULL;
+ }
+}
+
+gboolean
+ev_timeline_is_running (EvTimeline *timeline)
+{
+ EvTimelinePriv *priv;
+
+ g_return_val_if_fail (EV_IS_TIMELINE (timeline), FALSE);
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+
+ return (priv->source_id != 0);
+}
+
+guint
+ev_timeline_get_fps (EvTimeline *timeline)
+{
+ EvTimelinePriv *priv;
+
+ g_return_val_if_fail (EV_IS_TIMELINE (timeline), 1);
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+ return priv->fps;
+}
+
+void
+ev_timeline_set_fps (EvTimeline *timeline,
+ guint fps)
+{
+ EvTimelinePriv *priv;
+
+ g_return_if_fail (EV_IS_TIMELINE (timeline));
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+
+ priv->fps = fps;
+
+ if (ev_timeline_is_running (timeline)) {
+ g_source_remove (priv->source_id);
+ priv->source_id = g_timeout_add (FRAME_INTERVAL (priv->fps),
+ (GSourceFunc) ev_timeline_run_frame,
+ timeline);
+ }
+
+ g_object_notify (G_OBJECT (timeline), "fps");
+}
+
+gboolean
+ev_timeline_get_loop (EvTimeline *timeline)
+{
+ EvTimelinePriv *priv;
+
+ g_return_val_if_fail (EV_IS_TIMELINE (timeline), FALSE);
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+ return priv->loop;
+}
+
+void
+ev_timeline_set_loop (EvTimeline *timeline,
+ gboolean loop)
+{
+ EvTimelinePriv *priv;
+
+ g_return_if_fail (EV_IS_TIMELINE (timeline));
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+ priv->loop = loop;
+
+ g_object_notify (G_OBJECT (timeline), "loop");
+}
+
+void
+ev_timeline_set_duration (EvTimeline *timeline,
+ guint duration)
+{
+ EvTimelinePriv *priv;
+
+ g_return_if_fail (EV_IS_TIMELINE (timeline));
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+
+ priv->duration = duration;
+
+ g_object_notify (G_OBJECT (timeline), "duration");
+}
+
+guint
+ev_timeline_get_duration (EvTimeline *timeline)
+{
+ EvTimelinePriv *priv;
+
+ g_return_val_if_fail (EV_IS_TIMELINE (timeline), 0);
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+
+ return priv->duration;
+}
+
+gdouble
+ev_timeline_get_progress (EvTimeline *timeline)
+{
+ EvTimelinePriv *priv;
+ gdouble progress;
+ guint elapsed_time;
+
+ g_return_val_if_fail (EV_IS_TIMELINE (timeline), 0.0);
+
+ priv = EV_TIMELINE_GET_PRIV (timeline);
+
+ if (!priv->timer)
+ return 0.;
+
+ elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
+ progress = (gdouble) elapsed_time / priv->duration;
+
+ return CLAMP (progress, 0., 1.);
+}
--- /dev/null
+/* ev-timeline.h
+ * this file is part of evince, a gnome document viewer
+ *
+ * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EV_TIMELINE_H__
+#define __EV_TIMELINE_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_TIMELINE (ev_timeline_get_type ())
+#define EV_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_TIMELINE, EvTimeline))
+#define EV_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_TIMELINE, EvTimelineClass))
+#define EV_IS_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_TIMELINE))
+#define EV_IS_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_TIMELINE))
+#define EV_TIMELINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_TIMELINE, EvTimelineClass))
+
+typedef struct EvTimeline EvTimeline;
+typedef struct EvTimelineClass EvTimelineClass;
+
+struct EvTimeline {
+ GObject parent_instance;
+};
+
+struct EvTimelineClass {
+ GObjectClass parent_class;
+
+ /* vmethods */
+ void (* start) (EvTimeline *timeline);
+
+ /* signals */
+ void (* started) (EvTimeline *timeline);
+ void (* finished) (EvTimeline *timeline);
+ void (* paused) (EvTimeline *timeline);
+
+ void (* frame) (EvTimeline *timeline,
+ gdouble progress);
+};
+
+
+GType ev_timeline_get_type (void) G_GNUC_CONST;
+
+EvTimeline *ev_timeline_new (guint duration);
+
+void ev_timeline_start (EvTimeline *timeline);
+void ev_timeline_pause (EvTimeline *timeline);
+void ev_timeline_rewind (EvTimeline *timeline);
+
+gboolean ev_timeline_is_running (EvTimeline *timeline);
+
+guint ev_timeline_get_fps (EvTimeline *timeline);
+void ev_timeline_set_fps (EvTimeline *timeline,
+ guint fps);
+
+gboolean ev_timeline_get_loop (EvTimeline *timeline);
+void ev_timeline_set_loop (EvTimeline *timeline,
+ gboolean loop);
+
+guint ev_timeline_get_duration (EvTimeline *timeline);
+void ev_timeline_set_duration (EvTimeline *timeline,
+ guint duration);
+
+gdouble ev_timeline_get_progress (EvTimeline *timeline);
+
+
+G_END_DECLS
+
+#endif /* __EV_TIMELINE_H__ */