]> Pileus Git - ~andy/gtk/blob - gtk/gtktimeline.c
Strip "Custom." prefix when getting default options from cups.
[~andy/gtk] / gtk / gtktimeline.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #include <gtk/gtktimeline.h>
21 #include <gtk/gtktypebuiltins.h>
22 #include <gtk/gtksettings.h>
23 #include <math.h>
24
25 #define MSECS_PER_SEC 1000
26 #define FRAME_INTERVAL(nframes) (MSECS_PER_SEC / nframes)
27 #define DEFAULT_FPS 30
28
29 typedef struct GtkTimelinePriv GtkTimelinePriv;
30
31 struct GtkTimelinePriv
32 {
33   guint duration;
34   guint fps;
35   guint source_id;
36
37   GTimer *timer;
38
39   gdouble progress;
40   gdouble last_progress;
41
42   GdkScreen *screen;
43
44   GtkTimelineProgressType progress_type;
45
46   guint animations_enabled : 1;
47   guint loop               : 1;
48   guint direction          : 1;
49 };
50
51 enum {
52   PROP_0,
53   PROP_FPS,
54   PROP_DURATION,
55   PROP_LOOP,
56   PROP_DIRECTION,
57   PROP_SCREEN
58 };
59
60 enum {
61   STARTED,
62   PAUSED,
63   FINISHED,
64   FRAME,
65   LAST_SIGNAL
66 };
67
68 static guint signals [LAST_SIGNAL] = { 0, };
69
70
71 static void  gtk_timeline_set_property  (GObject         *object,
72                                          guint            prop_id,
73                                          const GValue    *value,
74                                          GParamSpec      *pspec);
75 static void  gtk_timeline_get_property  (GObject         *object,
76                                          guint            prop_id,
77                                          GValue          *value,
78                                          GParamSpec      *pspec);
79 static void  _gtk_timeline_finalize     (GObject *object);
80
81
82 G_DEFINE_TYPE (GtkTimeline, _gtk_timeline, G_TYPE_OBJECT)
83
84
85 static void
86 _gtk_timeline_class_init (GtkTimelineClass *klass)
87 {
88   GObjectClass *object_class = G_OBJECT_CLASS (klass);
89
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;
93
94   g_object_class_install_property (object_class,
95                                    PROP_FPS,
96                                    g_param_spec_uint ("fps",
97                                                       "FPS",
98                                                       "Frames per second for the timeline",
99                                                       1, G_MAXUINT,
100                                                       DEFAULT_FPS,
101                                                       G_PARAM_READWRITE));
102   g_object_class_install_property (object_class,
103                                    PROP_DURATION,
104                                    g_param_spec_uint ("duration",
105                                                       "Animation Duration",
106                                                       "Animation Duration",
107                                                       0, G_MAXUINT,
108                                                       0,
109                                                       G_PARAM_READWRITE));
110   g_object_class_install_property (object_class,
111                                    PROP_LOOP,
112                                    g_param_spec_boolean ("loop",
113                                                          "Loop",
114                                                          "Whether the timeline loops or not",
115                                                          FALSE,
116                                                          G_PARAM_READWRITE));
117   g_object_class_install_property (object_class,
118                                    PROP_SCREEN,
119                                    g_param_spec_object ("screen",
120                                                         "Screen",
121                                                         "Screen to get the settings from",
122                                                         GDK_TYPE_SCREEN,
123                                                         G_PARAM_READWRITE));
124
125   signals[STARTED] =
126     g_signal_new ("started",
127                   G_TYPE_FROM_CLASS (object_class),
128                   G_SIGNAL_RUN_LAST,
129                   G_STRUCT_OFFSET (GtkTimelineClass, started),
130                   NULL, NULL,
131                   g_cclosure_marshal_VOID__VOID,
132                   G_TYPE_NONE, 0);
133
134   signals[PAUSED] =
135     g_signal_new ("paused",
136                   G_TYPE_FROM_CLASS (object_class),
137                   G_SIGNAL_RUN_LAST,
138                   G_STRUCT_OFFSET (GtkTimelineClass, paused),
139                   NULL, NULL,
140                   g_cclosure_marshal_VOID__VOID,
141                   G_TYPE_NONE, 0);
142
143   signals[FINISHED] =
144     g_signal_new ("finished",
145                   G_TYPE_FROM_CLASS (object_class),
146                   G_SIGNAL_RUN_LAST,
147                   G_STRUCT_OFFSET (GtkTimelineClass, finished),
148                   NULL, NULL,
149                   g_cclosure_marshal_VOID__VOID,
150                   G_TYPE_NONE, 0);
151
152   signals[FRAME] =
153     g_signal_new ("frame",
154                   G_TYPE_FROM_CLASS (object_class),
155                   G_SIGNAL_RUN_LAST,
156                   G_STRUCT_OFFSET (GtkTimelineClass, frame),
157                   NULL, NULL,
158                   g_cclosure_marshal_VOID__DOUBLE,
159                   G_TYPE_NONE, 1,
160                   G_TYPE_DOUBLE);
161
162   g_type_class_add_private (klass, sizeof (GtkTimelinePriv));
163 }
164
165 static void
166 _gtk_timeline_init (GtkTimeline *timeline)
167 {
168   GtkTimelinePriv *priv;
169
170   priv = timeline->priv = G_TYPE_INSTANCE_GET_PRIVATE (timeline,
171                                                        GTK_TYPE_TIMELINE,
172                                                        GtkTimelinePriv);
173
174   priv->fps = DEFAULT_FPS;
175   priv->duration = 0.0;
176   priv->direction = GTK_TIMELINE_DIRECTION_FORWARD;
177   priv->screen = gdk_screen_get_default ();
178
179   priv->last_progress = 0;
180 }
181
182 static void
183 gtk_timeline_set_property (GObject      *object,
184                            guint         prop_id,
185                            const GValue *value,
186                            GParamSpec   *pspec)
187 {
188   GtkTimeline *timeline;
189
190   timeline = GTK_TIMELINE (object);
191
192   switch (prop_id)
193     {
194     case PROP_FPS:
195       _gtk_timeline_set_fps (timeline, g_value_get_uint (value));
196       break;
197     case PROP_DURATION:
198       _gtk_timeline_set_duration (timeline, g_value_get_uint (value));
199       break;
200     case PROP_LOOP:
201       _gtk_timeline_set_loop (timeline, g_value_get_boolean (value));
202       break;
203     case PROP_DIRECTION:
204       _gtk_timeline_set_direction (timeline, g_value_get_enum (value));
205       break;
206     case PROP_SCREEN:
207       _gtk_timeline_set_screen (timeline,
208                                 GDK_SCREEN (g_value_get_object (value)));
209       break;
210     default:
211       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
212     }
213 }
214
215 static void
216 gtk_timeline_get_property (GObject    *object,
217                            guint       prop_id,
218                            GValue     *value,
219                            GParamSpec *pspec)
220 {
221   GtkTimeline *timeline;
222   GtkTimelinePriv *priv;
223
224   timeline = GTK_TIMELINE (object);
225   priv = timeline->priv;
226
227   switch (prop_id)
228     {
229     case PROP_FPS:
230       g_value_set_uint (value, priv->fps);
231       break;
232     case PROP_DURATION:
233       g_value_set_uint (value, priv->duration);
234       break;
235     case PROP_LOOP:
236       g_value_set_boolean (value, priv->loop);
237       break;
238     case PROP_DIRECTION:
239       g_value_set_enum (value, priv->direction);
240       break;
241     case PROP_SCREEN:
242       g_value_set_object (value, priv->screen);
243       break;
244     default:
245       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
246     }
247 }
248
249 static void
250 _gtk_timeline_finalize (GObject *object)
251 {
252   GtkTimelinePriv *priv;
253   GtkTimeline *timeline;
254
255   timeline = (GtkTimeline *) object;
256   priv = timeline->priv;
257
258   if (priv->source_id)
259     {
260       g_source_remove (priv->source_id);
261       priv->source_id = 0;
262     }
263
264   if (priv->timer)
265     g_timer_destroy (priv->timer);
266
267   G_OBJECT_CLASS (_gtk_timeline_parent_class)->finalize (object);
268 }
269
270 static gdouble
271 calculate_progress (gdouble                 linear_progress,
272                     GtkTimelineProgressType progress_type)
273 {
274   gdouble progress;
275
276   progress = linear_progress;
277
278   switch (progress_type)
279     {
280     case GTK_TIMELINE_PROGRESS_LINEAR:
281       break;
282     case GTK_TIMELINE_PROGRESS_EASE_IN_OUT:
283       progress *= 2;
284
285       if (progress < 1)
286         progress = pow (progress, 3) / 2;
287       else
288         progress = (pow (progress - 2, 3) + 2) / 2;
289
290       break;
291     case GTK_TIMELINE_PROGRESS_EASE:
292       progress = (sin ((progress - 0.5) * G_PI) + 1) / 2;
293       break;
294     case GTK_TIMELINE_PROGRESS_EASE_IN:
295       progress = pow (progress, 3);
296       break;
297     case GTK_TIMELINE_PROGRESS_EASE_OUT:
298       progress = pow (progress - 1, 3) + 1;
299       break;
300     default:
301       g_warning ("Timeline progress type not implemented");
302     }
303
304   return progress;
305 }
306
307 static gboolean
308 gtk_timeline_run_frame (GtkTimeline *timeline)
309 {
310   GtkTimelinePriv *priv;
311   gdouble delta_progress, progress;
312   guint elapsed_time;
313
314   priv = timeline->priv;
315
316   elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
317   g_timer_start (priv->timer);
318
319   if (priv->animations_enabled)
320     {
321       delta_progress = (gdouble) elapsed_time / priv->duration;
322       progress = priv->last_progress;
323
324       if (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD)
325         progress -= delta_progress;
326       else
327         progress += delta_progress;
328
329       priv->last_progress = progress;
330
331       progress = CLAMP (progress, 0., 1.);
332     }
333   else
334     progress = (priv->direction == GTK_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0;
335
336   priv->progress = progress;
337   g_signal_emit (timeline, signals [FRAME], 0,
338                  calculate_progress (progress, priv->progress_type));
339
340   if ((priv->direction == GTK_TIMELINE_DIRECTION_FORWARD && progress == 1.0) ||
341       (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD && progress == 0.0))
342     {
343       gboolean loop;
344
345       loop = priv->loop && priv->animations_enabled;
346
347       if (loop)
348         _gtk_timeline_rewind (timeline);
349       else
350         {
351           if (priv->source_id)
352             {
353               g_source_remove (priv->source_id);
354               priv->source_id = 0;
355             }
356           g_timer_stop (priv->timer);
357           g_signal_emit (timeline, signals [FINISHED], 0);
358           return FALSE;
359         }
360     }
361
362   return TRUE;
363 }
364
365 /**
366  * gtk_timeline_new:
367  * @duration: duration in milliseconds for the timeline
368  *
369  * Creates a new #GtkTimeline with the specified number of frames.
370  *
371  * Return Value: the newly created #GtkTimeline
372  **/
373 GtkTimeline *
374 _gtk_timeline_new (guint duration)
375 {
376   return g_object_new (GTK_TYPE_TIMELINE,
377                        "duration", duration,
378                        NULL);
379 }
380
381 GtkTimeline *
382 _gtk_timeline_new_for_screen (guint      duration,
383                               GdkScreen *screen)
384 {
385   return g_object_new (GTK_TYPE_TIMELINE,
386                        "duration", duration,
387                        "screen", screen,
388                        NULL);
389 }
390
391 /**
392  * gtk_timeline_start:
393  * @timeline: A #GtkTimeline
394  *
395  * Runs the timeline from the current frame.
396  **/
397 void
398 _gtk_timeline_start (GtkTimeline *timeline)
399 {
400   GtkTimelinePriv *priv;
401   GtkSettings *settings;
402   gboolean enable_animations = FALSE;
403
404   g_return_if_fail (GTK_IS_TIMELINE (timeline));
405
406   priv = timeline->priv;
407
408   if (!priv->source_id)
409     {
410       if (priv->timer)
411         g_timer_continue (priv->timer);
412       else
413         priv->timer = g_timer_new ();
414
415       /* sanity check */
416       g_assert (priv->fps > 0);
417
418       if (priv->screen)
419         {
420           settings = gtk_settings_get_for_screen (priv->screen);
421           g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
422         }
423
424       priv->animations_enabled = enable_animations;
425
426       g_signal_emit (timeline, signals [STARTED], 0);
427
428       if (enable_animations)
429         priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
430                                                    (GSourceFunc) gtk_timeline_run_frame,
431                                                    timeline);
432       else
433         priv->source_id = gdk_threads_add_idle ((GSourceFunc) gtk_timeline_run_frame,
434                                                 timeline);
435     }
436 }
437
438 /**
439  * gtk_timeline_pause:
440  * @timeline: A #GtkTimeline
441  *
442  * Pauses the timeline.
443  **/
444 void
445 _gtk_timeline_pause (GtkTimeline *timeline)
446 {
447   GtkTimelinePriv *priv;
448
449   g_return_if_fail (GTK_IS_TIMELINE (timeline));
450
451   priv = timeline->priv;
452
453   if (priv->source_id)
454     {
455       g_timer_stop (priv->timer);
456       g_source_remove (priv->source_id);
457       priv->source_id = 0;
458       g_signal_emit (timeline, signals [PAUSED], 0);
459     }
460 }
461
462 /**
463  * gtk_timeline_rewind:
464  * @timeline: A #GtkTimeline
465  *
466  * Rewinds the timeline.
467  **/
468 void
469 _gtk_timeline_rewind (GtkTimeline *timeline)
470 {
471   GtkTimelinePriv *priv;
472
473   g_return_if_fail (GTK_IS_TIMELINE (timeline));
474
475   priv = timeline->priv;
476
477   if (_gtk_timeline_get_direction (timeline) != GTK_TIMELINE_DIRECTION_FORWARD)
478     priv->progress = priv->last_progress = 1.;
479   else
480     priv->progress = priv->last_progress = 0.;
481
482   /* reset timer */
483   if (priv->timer)
484     {
485       g_timer_start (priv->timer);
486
487       if (!priv->source_id)
488         g_timer_stop (priv->timer);
489     }
490 }
491
492 /**
493  * gtk_timeline_is_running:
494  * @timeline: A #GtkTimeline
495  *
496  * Returns whether the timeline is running or not.
497  *
498  * Return Value: %TRUE if the timeline is running
499  **/
500 gboolean
501 _gtk_timeline_is_running (GtkTimeline *timeline)
502 {
503   GtkTimelinePriv *priv;
504
505   g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
506
507   priv = timeline->priv;
508
509   return (priv->source_id != 0);
510 }
511
512 /**
513  * gtk_timeline_get_fps:
514  * @timeline: A #GtkTimeline
515  *
516  * Returns the number of frames per second.
517  *
518  * Return Value: frames per second
519  **/
520 guint
521 _gtk_timeline_get_fps (GtkTimeline *timeline)
522 {
523   GtkTimelinePriv *priv;
524
525   g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 1);
526
527   priv = timeline->priv;
528   return priv->fps;
529 }
530
531 /**
532  * gtk_timeline_set_fps:
533  * @timeline: A #GtkTimeline
534  * @fps: frames per second
535  *
536  * Sets the number of frames per second that
537  * the timeline will play.
538  **/
539 void
540 _gtk_timeline_set_fps (GtkTimeline *timeline,
541                       guint        fps)
542 {
543   GtkTimelinePriv *priv;
544
545   g_return_if_fail (GTK_IS_TIMELINE (timeline));
546   g_return_if_fail (fps > 0);
547
548   priv = timeline->priv;
549
550   priv->fps = fps;
551
552   if (_gtk_timeline_is_running (timeline))
553     {
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,
557                                                  timeline);
558     }
559
560   g_object_notify (G_OBJECT (timeline), "fps");
561 }
562
563 /**
564  * gtk_timeline_get_loop:
565  * @timeline: A #GtkTimeline
566  *
567  * Returns whether the timeline loops to the
568  * beginning when it has reached the end.
569  *
570  * Return Value: %TRUE if the timeline loops
571  **/
572 gboolean
573 _gtk_timeline_get_loop (GtkTimeline *timeline)
574 {
575   GtkTimelinePriv *priv;
576
577   g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
578
579   priv = timeline->priv;
580   return priv->loop;
581 }
582
583 /**
584  * gtk_timeline_set_loop:
585  * @timeline: A #GtkTimeline
586  * @loop: %TRUE to make the timeline loop
587  *
588  * Sets whether the timeline loops to the beginning
589  * when it has reached the end.
590  **/
591 void
592 _gtk_timeline_set_loop (GtkTimeline *timeline,
593                         gboolean     loop)
594 {
595   GtkTimelinePriv *priv;
596
597   g_return_if_fail (GTK_IS_TIMELINE (timeline));
598
599   priv = timeline->priv;
600
601   if (loop != priv->loop)
602     {
603       priv->loop = loop;
604       g_object_notify (G_OBJECT (timeline), "loop");
605     }
606 }
607
608 void
609 _gtk_timeline_set_duration (GtkTimeline *timeline,
610                             guint        duration)
611 {
612   GtkTimelinePriv *priv;
613
614   g_return_if_fail (GTK_IS_TIMELINE (timeline));
615
616   priv = timeline->priv;
617
618   if (duration != priv->duration)
619     {
620       priv->duration = duration;
621       g_object_notify (G_OBJECT (timeline), "duration");
622     }
623 }
624
625 guint
626 _gtk_timeline_get_duration (GtkTimeline *timeline)
627 {
628   GtkTimelinePriv *priv;
629
630   g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
631
632   priv = timeline->priv;
633
634   return priv->duration;
635 }
636
637 /**
638  * gtk_timeline_set_direction:
639  * @timeline: A #GtkTimeline
640  * @direction: direction
641  *
642  * Sets the direction of the timeline.
643  **/
644 void
645 _gtk_timeline_set_direction (GtkTimeline          *timeline,
646                              GtkTimelineDirection  direction)
647 {
648   GtkTimelinePriv *priv;
649
650   g_return_if_fail (GTK_IS_TIMELINE (timeline));
651
652   priv = timeline->priv;
653   priv->direction = direction;
654 }
655
656 /**
657  * gtk_timeline_get_direction:
658  * @timeline: A #GtkTimeline
659  *
660  * Returns the direction of the timeline.
661  *
662  * Return Value: direction
663  **/
664 GtkTimelineDirection
665 _gtk_timeline_get_direction (GtkTimeline *timeline)
666 {
667   GtkTimelinePriv *priv;
668
669   g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_DIRECTION_FORWARD);
670
671   priv = timeline->priv;
672   return priv->direction;
673 }
674
675 void
676 _gtk_timeline_set_screen (GtkTimeline *timeline,
677                           GdkScreen   *screen)
678 {
679   GtkTimelinePriv *priv;
680
681   g_return_if_fail (GTK_IS_TIMELINE (timeline));
682   g_return_if_fail (GDK_IS_SCREEN (screen));
683
684   priv = timeline->priv;
685
686   if (priv->screen)
687     g_object_unref (priv->screen);
688
689   priv->screen = g_object_ref (screen);
690
691   g_object_notify (G_OBJECT (timeline), "screen");
692 }
693
694 GdkScreen *
695 _gtk_timeline_get_screen (GtkTimeline *timeline)
696 {
697   GtkTimelinePriv *priv;
698
699   g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
700
701   priv = timeline->priv;
702   return priv->screen;
703 }
704
705 gdouble
706 _gtk_timeline_get_progress (GtkTimeline *timeline)
707 {
708   GtkTimelinePriv *priv;
709
710   g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0.);
711
712   priv = timeline->priv;
713   return calculate_progress (priv->progress, priv->progress_type);
714 }
715
716 GtkTimelineProgressType
717 _gtk_timeline_get_progress_type (GtkTimeline *timeline)
718 {
719   GtkTimelinePriv *priv;
720
721   g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_PROGRESS_LINEAR);
722
723   priv = timeline->priv;
724   return priv->progress_type;
725 }
726
727 void
728 _gtk_timeline_set_progress_type (GtkTimeline             *timeline,
729                                  GtkTimelineProgressType  progress_type)
730 {
731   GtkTimelinePriv *priv;
732
733   g_return_if_fail (GTK_IS_TIMELINE (timeline));
734
735   priv = timeline->priv;
736   priv->progress_type = progress_type;
737 }