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, see <http://www.gnu.org/licenses/>.
18 #include <gtk/gtktimeline.h>
19 #include <gtk/gtktypebuiltins.h>
20 #include <gtk/gtksettings.h>
23 #define MSECS_PER_SEC 1000
24 #define FRAME_INTERVAL(nframes) (MSECS_PER_SEC / nframes)
25 #define DEFAULT_FPS 30
27 typedef struct GtkTimelinePriv GtkTimelinePriv;
29 struct GtkTimelinePriv
38 gdouble last_progress;
42 GtkTimelineProgressType progress_type;
44 guint animations_enabled : 1;
65 static guint signals [LAST_SIGNAL] = { 0, };
68 static void gtk_timeline_set_property (GObject *object,
72 static void gtk_timeline_get_property (GObject *object,
76 static void gtk_timeline_finalize (GObject *object);
79 G_DEFINE_TYPE (GtkTimeline, gtk_timeline, G_TYPE_OBJECT)
83 gtk_timeline_class_init (GtkTimelineClass *klass)
85 GObjectClass *object_class = G_OBJECT_CLASS (klass);
87 object_class->set_property = gtk_timeline_set_property;
88 object_class->get_property = gtk_timeline_get_property;
89 object_class->finalize = gtk_timeline_finalize;
91 g_object_class_install_property (object_class,
93 g_param_spec_uint ("duration",
99 g_object_class_install_property (object_class,
101 g_param_spec_boolean ("loop",
103 "Whether the timeline loops or not",
106 g_object_class_install_property (object_class,
108 g_param_spec_object ("screen",
110 "Screen to get the settings from",
115 g_signal_new ("started",
116 G_TYPE_FROM_CLASS (object_class),
118 G_STRUCT_OFFSET (GtkTimelineClass, started),
120 g_cclosure_marshal_VOID__VOID,
124 g_signal_new ("paused",
125 G_TYPE_FROM_CLASS (object_class),
127 G_STRUCT_OFFSET (GtkTimelineClass, paused),
129 g_cclosure_marshal_VOID__VOID,
133 g_signal_new ("finished",
134 G_TYPE_FROM_CLASS (object_class),
136 G_STRUCT_OFFSET (GtkTimelineClass, finished),
138 g_cclosure_marshal_VOID__VOID,
142 g_signal_new ("frame",
143 G_TYPE_FROM_CLASS (object_class),
145 G_STRUCT_OFFSET (GtkTimelineClass, frame),
147 g_cclosure_marshal_VOID__DOUBLE,
151 g_type_class_add_private (klass, sizeof (GtkTimelinePriv));
155 gtk_timeline_init (GtkTimeline *timeline)
157 GtkTimelinePriv *priv;
159 priv = timeline->priv = G_TYPE_INSTANCE_GET_PRIVATE (timeline,
163 priv->duration = 0.0;
164 priv->direction = GTK_TIMELINE_DIRECTION_FORWARD;
165 priv->screen = gdk_screen_get_default ();
167 priv->last_progress = 0;
171 gtk_timeline_set_property (GObject *object,
176 GtkTimeline *timeline;
178 timeline = GTK_TIMELINE (object);
183 gtk_timeline_set_duration (timeline, g_value_get_uint (value));
186 gtk_timeline_set_loop (timeline, g_value_get_boolean (value));
189 gtk_timeline_set_direction (timeline, g_value_get_enum (value));
192 gtk_timeline_set_screen (timeline,
193 GDK_SCREEN (g_value_get_object (value)));
196 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
201 gtk_timeline_get_property (GObject *object,
206 GtkTimeline *timeline;
207 GtkTimelinePriv *priv;
209 timeline = GTK_TIMELINE (object);
210 priv = timeline->priv;
215 g_value_set_uint (value, priv->duration);
218 g_value_set_boolean (value, priv->loop);
221 g_value_set_enum (value, priv->direction);
224 g_value_set_object (value, priv->screen);
227 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
232 gtk_timeline_finalize (GObject *object)
234 GtkTimelinePriv *priv;
235 GtkTimeline *timeline;
237 timeline = (GtkTimeline *) object;
238 priv = timeline->priv;
242 g_source_remove (priv->source_id);
247 g_timer_destroy (priv->timer);
249 G_OBJECT_CLASS (gtk_timeline_parent_class)->finalize (object);
253 calculate_progress (gdouble linear_progress,
254 GtkTimelineProgressType progress_type)
258 progress = linear_progress;
260 switch (progress_type)
262 case GTK_TIMELINE_PROGRESS_LINEAR:
264 case GTK_TIMELINE_PROGRESS_EASE_IN_OUT:
268 progress = pow (progress, 3) / 2;
270 progress = (pow (progress - 2, 3) + 2) / 2;
273 case GTK_TIMELINE_PROGRESS_EASE:
274 progress = (sin ((progress - 0.5) * G_PI) + 1) / 2;
276 case GTK_TIMELINE_PROGRESS_EASE_IN:
277 progress = pow (progress, 3);
279 case GTK_TIMELINE_PROGRESS_EASE_OUT:
280 progress = pow (progress - 1, 3) + 1;
283 g_warning ("Timeline progress type not implemented");
290 gtk_timeline_run_frame (GtkTimeline *timeline)
292 GtkTimelinePriv *priv;
293 gdouble delta_progress, progress;
295 /* the user may unref us during the signals, so save ourselves */
296 g_object_ref (timeline);
298 priv = timeline->priv;
300 priv->elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
301 g_timer_start (priv->timer);
303 if (priv->animations_enabled)
305 delta_progress = (gdouble) priv->elapsed_time / priv->duration;
306 progress = priv->last_progress;
308 if (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD)
309 progress -= delta_progress;
311 progress += delta_progress;
313 priv->last_progress = progress;
315 progress = CLAMP (progress, 0., 1.);
318 progress = (priv->direction == GTK_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0;
320 priv->progress = progress;
321 g_signal_emit (timeline, signals [FRAME], 0,
322 calculate_progress (progress, priv->progress_type));
324 if ((priv->direction == GTK_TIMELINE_DIRECTION_FORWARD && progress == 1.0) ||
325 (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD && progress == 0.0))
329 loop = priv->loop && priv->animations_enabled;
332 gtk_timeline_rewind (timeline);
337 g_source_remove (priv->source_id);
340 g_timer_stop (priv->timer);
341 g_signal_emit (timeline, signals [FINISHED], 0);
342 g_object_unref (timeline);
347 g_object_unref (timeline);
354 * @duration: duration in milliseconds for the timeline
356 * Creates a new #GtkTimeline with the specified number of frames.
358 * Return Value: the newly created #GtkTimeline
361 gtk_timeline_new (guint duration)
363 return g_object_new (GTK_TYPE_TIMELINE,
364 "duration", duration,
369 gtk_timeline_new_for_screen (guint duration,
372 return g_object_new (GTK_TYPE_TIMELINE,
373 "duration", duration,
379 * gtk_timeline_start:
380 * @timeline: A #GtkTimeline
382 * Runs the timeline from the current frame.
385 gtk_timeline_start (GtkTimeline *timeline)
387 GtkTimelinePriv *priv;
388 GtkSettings *settings;
389 gboolean enable_animations = FALSE;
391 g_return_if_fail (GTK_IS_TIMELINE (timeline));
393 priv = timeline->priv;
395 if (!priv->source_id)
398 g_timer_continue (priv->timer);
400 priv->timer = g_timer_new ();
404 settings = gtk_settings_get_for_screen (priv->screen);
405 g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
408 priv->animations_enabled = enable_animations;
410 g_signal_emit (timeline, signals [STARTED], 0);
412 if (enable_animations)
413 priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (DEFAULT_FPS),
414 (GSourceFunc) gtk_timeline_run_frame,
417 priv->source_id = gdk_threads_add_idle ((GSourceFunc) gtk_timeline_run_frame,
423 * gtk_timeline_pause:
424 * @timeline: A #GtkTimeline
426 * Pauses the timeline.
429 gtk_timeline_pause (GtkTimeline *timeline)
431 GtkTimelinePriv *priv;
433 g_return_if_fail (GTK_IS_TIMELINE (timeline));
435 priv = timeline->priv;
439 g_timer_stop (priv->timer);
440 g_source_remove (priv->source_id);
442 g_signal_emit (timeline, signals [PAUSED], 0);
447 * gtk_timeline_rewind:
448 * @timeline: A #GtkTimeline
450 * Rewinds the timeline.
453 gtk_timeline_rewind (GtkTimeline *timeline)
455 GtkTimelinePriv *priv;
457 g_return_if_fail (GTK_IS_TIMELINE (timeline));
459 priv = timeline->priv;
461 if (gtk_timeline_get_direction (timeline) != GTK_TIMELINE_DIRECTION_FORWARD)
462 priv->progress = priv->last_progress = 1.;
464 priv->progress = priv->last_progress = 0.;
469 g_timer_start (priv->timer);
471 if (!priv->source_id)
472 g_timer_stop (priv->timer);
477 * gtk_timeline_is_running:
478 * @timeline: A #GtkTimeline
480 * Returns whether the timeline is running or not.
482 * Return Value: %TRUE if the timeline is running
485 gtk_timeline_is_running (GtkTimeline *timeline)
487 GtkTimelinePriv *priv;
489 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
491 priv = timeline->priv;
493 return (priv->source_id != 0);
497 * gtk_timeline_get_elapsed_time:
498 * @timeline: A #GtkTimeline
500 * Returns the elapsed time since the last GtkTimeline::frame signal
502 * Return Value: elapsed time in milliseconds since the last frame
505 gtk_timeline_get_elapsed_time (GtkTimeline *timeline)
507 GtkTimelinePriv *priv;
509 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
511 priv = timeline->priv;
512 return priv->elapsed_time;
516 * gtk_timeline_get_loop:
517 * @timeline: A #GtkTimeline
519 * Returns whether the timeline loops to the
520 * beginning when it has reached the end.
522 * Return Value: %TRUE if the timeline loops
525 gtk_timeline_get_loop (GtkTimeline *timeline)
527 GtkTimelinePriv *priv;
529 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
531 priv = timeline->priv;
536 * gtk_timeline_set_loop:
537 * @timeline: A #GtkTimeline
538 * @loop: %TRUE to make the timeline loop
540 * Sets whether the timeline loops to the beginning
541 * when it has reached the end.
544 gtk_timeline_set_loop (GtkTimeline *timeline,
547 GtkTimelinePriv *priv;
549 g_return_if_fail (GTK_IS_TIMELINE (timeline));
551 priv = timeline->priv;
553 if (loop != priv->loop)
556 g_object_notify (G_OBJECT (timeline), "loop");
561 gtk_timeline_set_duration (GtkTimeline *timeline,
564 GtkTimelinePriv *priv;
566 g_return_if_fail (GTK_IS_TIMELINE (timeline));
568 priv = timeline->priv;
570 if (duration != priv->duration)
572 priv->duration = duration;
573 g_object_notify (G_OBJECT (timeline), "duration");
578 gtk_timeline_get_duration (GtkTimeline *timeline)
580 GtkTimelinePriv *priv;
582 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
584 priv = timeline->priv;
586 return priv->duration;
590 * gtk_timeline_set_direction:
591 * @timeline: A #GtkTimeline
592 * @direction: direction
594 * Sets the direction of the timeline.
597 gtk_timeline_set_direction (GtkTimeline *timeline,
598 GtkTimelineDirection direction)
600 GtkTimelinePriv *priv;
602 g_return_if_fail (GTK_IS_TIMELINE (timeline));
604 priv = timeline->priv;
605 priv->direction = direction;
609 * gtk_timeline_get_direction:
610 * @timeline: A #GtkTimeline
612 * Returns the direction of the timeline.
614 * Return Value: direction
617 gtk_timeline_get_direction (GtkTimeline *timeline)
619 GtkTimelinePriv *priv;
621 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_DIRECTION_FORWARD);
623 priv = timeline->priv;
624 return priv->direction;
628 gtk_timeline_set_screen (GtkTimeline *timeline,
631 GtkTimelinePriv *priv;
633 g_return_if_fail (GTK_IS_TIMELINE (timeline));
634 g_return_if_fail (GDK_IS_SCREEN (screen));
636 priv = timeline->priv;
639 g_object_unref (priv->screen);
641 priv->screen = g_object_ref (screen);
643 g_object_notify (G_OBJECT (timeline), "screen");
647 gtk_timeline_get_screen (GtkTimeline *timeline)
649 GtkTimelinePriv *priv;
651 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
653 priv = timeline->priv;
658 gtk_timeline_get_progress (GtkTimeline *timeline)
660 GtkTimelinePriv *priv;
662 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0.);
664 priv = timeline->priv;
665 return calculate_progress (priv->progress, priv->progress_type);
668 GtkTimelineProgressType
669 gtk_timeline_get_progress_type (GtkTimeline *timeline)
671 GtkTimelinePriv *priv;
673 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_PROGRESS_LINEAR);
675 priv = timeline->priv;
676 return priv->progress_type;
680 gtk_timeline_set_progress_type (GtkTimeline *timeline,
681 GtkTimelineProgressType progress_type)
683 GtkTimelinePriv *priv;
685 g_return_if_fail (GTK_IS_TIMELINE (timeline));
687 priv = timeline->priv;
688 priv->progress_type = progress_type;