diff --git a/ChangeLog b/ChangeLog
index 88035ffdb..5c55c4f98 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2007-11-15  Emmanuele Bassi  <ebassi@openedhand.com>
+
+	* clutter/clutter-timeline.[ch]: Add ClutterTimeline:direction
+	property, defining the direction (forward or backward) of the
+	timeline.
+
+	* tests/test-timeline.c: Test the direction property.
+
+	* clutter.symbols: Update exported symbols
+
 2007-11-15  Emmanuele Bassi  <ebassi@openedhand.com>
 
 	* clutter/clutter-effect.h:
diff --git a/clutter.symbols b/clutter.symbols
index fa89f8a85..330295895 100644
--- a/clutter.symbols
+++ b/clutter.symbols
@@ -422,9 +422,12 @@ clutter_texture_has_generated_tiles
 clutter_texture_is_tiled
 clutter_timeline_get_type
 clutter_timeline_new               
+clutter_timeline_new_for_duration
 clutter_timeline_clone             
 clutter_timeline_get_speed
 clutter_timeline_set_speed
+clutter_timeline_get_direction
+clutter_timeline_set_direction
 clutter_timeline_start
 clutter_timeline_pause
 clutter_timeline_stop
@@ -439,6 +442,8 @@ clutter_timeline_get_n_frames
 clutter_timeline_is_playing
 clutter_timeline_set_delay
 clutter_timeline_get_delay
+clutter_timeline_set_duration
+clutter_timeline_get_duration
 clutter_timeout_pool_new    
 clutter_timeout_pool_add
 clutter_timeout_pool_remove
diff --git a/clutter/clutter-timeline.c b/clutter/clutter-timeline.c
index 9fc6a1637..88dd49fda 100644
--- a/clutter/clutter-timeline.c
+++ b/clutter/clutter-timeline.c
@@ -49,6 +49,7 @@
 #include "clutter-marshal.h"
 #include "clutter-private.h"
 #include "clutter-debug.h"
+#include "clutter-enum-types.h"
 
 G_DEFINE_TYPE (ClutterTimeline, clutter_timeline, G_TYPE_OBJECT);
 
@@ -57,12 +58,15 @@ G_DEFINE_TYPE (ClutterTimeline, clutter_timeline, G_TYPE_OBJECT);
 
 struct _ClutterTimelinePrivate
 {
+  ClutterTimelineDirection direction;
+
   guint timeout_id;
   guint delay_id;
 
+  gint current_frame_num;
+
   guint fps;
   guint n_frames;
-  guint current_frame_num;
   guint delay;
   guint duration;
 
@@ -80,7 +84,8 @@ enum
   PROP_NUM_FRAMES,
   PROP_LOOP,
   PROP_DELAY,
-  PROP_DURATION
+  PROP_DURATION,
+  PROP_DIRECTION
 };
 
 enum
@@ -186,6 +191,9 @@ clutter_timeline_set_property (GObject      *object,
     case PROP_DURATION:
       clutter_timeline_set_duration (timeline, g_value_get_uint (value));
       break;
+    case PROP_DIRECTION:
+      clutter_timeline_set_direction (timeline, g_value_get_enum (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -221,6 +229,9 @@ clutter_timeline_get_property (GObject    *object,
     case PROP_DURATION:
       g_value_set_uint (value, priv->duration);
       break;
+    case PROP_DIRECTION:
+      g_value_set_enum (value, priv->direction);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -341,6 +352,22 @@ clutter_timeline_class_init (ClutterTimelineClass *klass)
                                                       0, G_MAXUINT,
                                                       1000,
                                                       CLUTTER_PARAM_READWRITE));
+  /**
+   * ClutterTimeline:direction:
+   * 
+   * The direction of the timeline, either %CLUTTER_TIMELINE_FORWARD or
+   * %CLUTTER_TIMELINE_BACKWARD.
+   *
+   * Since: 0.6
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_DIRECTION,
+                                   g_param_spec_enum ("direction",
+                                                      "Direction",
+                                                      "Direction of the timeline",
+                                                      CLUTTER_TYPE_TIMELINE_DIRECTION,
+                                                      CLUTTER_TIMELINE_FORWARD,
+                                                      CLUTTER_PARAM_READWRITE));
 
   /**
    * ClutterTimeline::new-frame:
@@ -499,10 +526,16 @@ timeline_timeout_func (gpointer data)
   priv->last_frame_msecs = msecs;
 
   /* Advance frames */
-  priv->current_frame_num += n_frames;;
+  if (priv->direction == CLUTTER_TIMELINE_FORWARD)
+    priv->current_frame_num += n_frames;
+  else
+    priv->current_frame_num -= n_frames;
 
   /* Handle loop or stop */
-  if (priv->current_frame_num > priv->n_frames)
+  if (((priv->direction == CLUTTER_TIMELINE_FORWARD) &&
+       (priv->current_frame_num > priv->n_frames)) ||
+      ((priv->direction == CLUTTER_TIMELINE_BACKWARD) &&
+       (priv->current_frame_num < 0)))
     {
       guint frame_diff;
 
@@ -513,8 +546,16 @@ timeline_timeout_func (gpointer data)
                     priv->n_frames,
                     n_frames - 1);
 
-      frame_diff = priv->current_frame_num - priv->n_frames;
-      priv->current_frame_num = priv->n_frames;
+      if (priv->direction == CLUTTER_TIMELINE_FORWARD)
+        {
+          frame_diff = priv->current_frame_num - priv->n_frames;
+          priv->current_frame_num = priv->n_frames;
+        }
+      else
+        {
+          frame_diff = priv->n_frames - priv->current_frame_num;
+          priv->current_frame_num = 0;
+        }
 
       /* if we skipped some frame to get here let's see whether we still need
        * to emit the last new-frame signal with the last frame
@@ -699,9 +740,16 @@ clutter_timeline_get_loop (ClutterTimeline *timeline)
 void
 clutter_timeline_rewind (ClutterTimeline *timeline)
 {
+  ClutterTimelinePrivate *priv;
+
   g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
 
-  clutter_timeline_advance (timeline, 0);
+  priv = timeline->priv;
+
+  if (priv->direction == CLUTTER_TIMELINE_FORWARD)
+    clutter_timeline_advance (timeline, 0);
+  else if (priv->direction == CLUTTER_TIMELINE_BACKWARD)
+    clutter_timeline_advance (timeline, priv->n_frames);
 }
 
 /**
@@ -723,8 +771,20 @@ clutter_timeline_skip (ClutterTimeline *timeline,
 
   priv->current_frame_num += n_frames;
 
-  if (priv->current_frame_num > priv->n_frames)
-    priv->current_frame_num = 1;
+  if (priv->direction == CLUTTER_TIMELINE_FORWARD)
+    {
+      priv->current_frame_num += n_frames;
+
+      if (priv->current_frame_num > priv->n_frames)
+        priv->current_frame_num = 1;
+    }
+  else if (priv->direction == CLUTTER_TIMELINE_BACKWARD)
+    {
+      priv->current_frame_num -= n_frames;
+
+      if (priv->current_frame_num < 1)
+        priv->current_frame_num = priv->n_frames - 1;
+    }
 }
 
 /**
@@ -744,8 +804,7 @@ clutter_timeline_advance (ClutterTimeline *timeline,
 
   priv = timeline->priv;
 
-  if (frame_num < priv->n_frames)
-    priv->current_frame_num = frame_num;
+  priv->current_frame_num = CLAMP (frame_num, 0, priv->n_frames);
 }
 
 /**
@@ -800,12 +859,9 @@ clutter_timeline_set_n_frames (ClutterTimeline *timeline,
 
   if (priv->n_frames != n_frames)
     {
-      g_object_ref (timeline);
-
       priv->n_frames = n_frames;
 
       g_object_notify (G_OBJECT (timeline), "num-frames");
-      g_object_unref (timeline);
     }
 }
 
@@ -905,6 +961,7 @@ clutter_timeline_clone (ClutterTimeline *timeline)
                        "num-frames", clutter_timeline_get_n_frames (timeline),
                        "loop", clutter_timeline_get_loop (timeline),
                        "delay", clutter_timeline_get_delay (timeline),
+                       "direction", clutter_timeline_get_direction (timeline),
                        NULL);
 
   return copy;
@@ -1091,6 +1148,60 @@ clutter_timeline_get_progressx (ClutterTimeline *timeline)
 
   priv = timeline->priv;
 
-  return CLUTTER_FIXED_DIV (CLUTTER_INT_TO_FIXED (priv->current_frame_num),
-                            CLUTTER_INT_TO_FIXED (priv->n_frames));
+  if (priv->direction == CLUTTER_TIMELINE_FORWARD)
+    return CLUTTER_FIXED_DIV (CLUTTER_INT_TO_FIXED (priv->current_frame_num),
+                              CLUTTER_INT_TO_FIXED (priv->n_frames));
+  else
+    return CLUTTER_FIXED_DIV (CLUTTER_INT_TO_FIXED (priv->n_frames),
+                              CLUTTER_INT_TO_FIXED (priv->current_frame_num));
+}
+
+/**
+ * clutter_timeline_get_direction:
+ * @timeline: a #ClutterTimeline
+ *
+ * Retrieves the direction of the timeline set with
+ * clutter_timeline_set_direction().
+ *
+ * Return value: the direction of the timeline
+ *
+ * Since: 0.6
+ */
+ClutterTimelineDirection
+clutter_timeline_get_direction (ClutterTimeline *timeline)
+{
+  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), CLUTTER_TIMELINE_FORWARD);
+
+  return timeline->priv->direction;
+}
+
+/**
+ * clutter_timeline_set_direction:
+ * @timeline: a #ClutterTimeline
+ * @direction: the direction of the timeline
+ *
+ * Sets the direction of @timeline, either %CLUTTER_TIMELINE_FORWARD or
+ * %CLUTTER_TIMELINE_BACKWARD.
+ *
+ * Since: 0.6
+ */
+void
+clutter_timeline_set_direction (ClutterTimeline          *timeline,
+                                ClutterTimelineDirection  direction)
+{
+  ClutterTimelinePrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
+
+  priv = timeline->priv;
+
+  if (priv->direction != direction)
+    {
+      priv->direction = direction;
+
+      if (priv->current_frame_num == 0)
+        priv->current_frame_num = priv->n_frames;
+
+      g_object_notify (G_OBJECT (timeline), "direction");
+    }
 }
diff --git a/clutter/clutter-timeline.h b/clutter/clutter-timeline.h
index 42fd6e000..0a28dc034 100644
--- a/clutter/clutter-timeline.h
+++ b/clutter/clutter-timeline.h
@@ -55,6 +55,20 @@ G_BEGIN_DECLS
   (G_TYPE_INSTANCE_GET_CLASS ((obj), \
   CLUTTER_TYPE_TIMELINE, ClutterTimelineClass))
 
+/**
+ * ClutterTimelineDirection:
+ * @CLUTTER_TIMELINE_FORWARD: forward direction for a timeline
+ * @CLUTTER_TIMELINE_BACKWARD: backward direction for a timeline
+ *
+ * The direction of a #ClutterTimeline
+ *
+ * Since: 0.6
+ */
+typedef enum {
+  CLUTTER_TIMELINE_FORWARD,
+  CLUTTER_TIMELINE_BACKWARD
+} ClutterTimelineDirection;
+
 typedef struct _ClutterTimeline        ClutterTimeline;
 typedef struct _ClutterTimelineClass   ClutterTimelineClass; 
 typedef struct _ClutterTimelinePrivate ClutterTimelinePrivate;
@@ -89,38 +103,41 @@ struct _ClutterTimelineClass
 
 GType clutter_timeline_get_type (void) G_GNUC_CONST;
 
-ClutterTimeline *clutter_timeline_new               (guint            n_frames,
-                                                     guint            fps);
-ClutterTimeline *clutter_timeline_new_for_duration  (guint            msecs);
-ClutterTimeline *clutter_timeline_clone             (ClutterTimeline *timeline);
+ClutterTimeline *clutter_timeline_new                   (guint            n_frames,
+                                                         guint            fps);
+ClutterTimeline *clutter_timeline_new_for_duration      (guint            msecs);
+ClutterTimeline *clutter_timeline_clone                 (ClutterTimeline *timeline);
 
-guint            clutter_timeline_get_duration      (ClutterTimeline *timeline);
-void             clutter_timeline_set_duration      (ClutterTimeline *timeline,
-                                                     guint            msecs);
-guint            clutter_timeline_get_speed         (ClutterTimeline *timeline);
-void             clutter_timeline_set_speed         (ClutterTimeline *timeline,
-                                                     guint            fps);
-void             clutter_timeline_start             (ClutterTimeline *timeline);
-void             clutter_timeline_pause             (ClutterTimeline *timeline);
-void             clutter_timeline_stop              (ClutterTimeline *timeline);
-void             clutter_timeline_set_loop          (ClutterTimeline *timeline,
-                                                     gboolean         loop);
-gboolean         clutter_timeline_get_loop          (ClutterTimeline *timeline);
-void             clutter_timeline_rewind            (ClutterTimeline *timeline);
-void             clutter_timeline_skip              (ClutterTimeline *timeline,
-                                                     guint            n_frames);
-void             clutter_timeline_advance           (ClutterTimeline *timeline,
-                                                     guint            frame_num);
-gint             clutter_timeline_get_current_frame (ClutterTimeline *timeline);
-gdouble          clutter_timeline_get_progress      (ClutterTimeline *timeline);
-ClutterFixed     clutter_timeline_get_progressx     (ClutterTimeline *timeline);
-void             clutter_timeline_set_n_frames      (ClutterTimeline *timeline,
-                                                     guint            n_frames);
-guint            clutter_timeline_get_n_frames      (ClutterTimeline *timeline);
-gboolean         clutter_timeline_is_playing        (ClutterTimeline *timeline);
-void             clutter_timeline_set_delay         (ClutterTimeline *timeline,
-                                                     guint            msecs);
-guint            clutter_timeline_get_delay         (ClutterTimeline *timeline);
+guint            clutter_timeline_get_duration          (ClutterTimeline *timeline);
+void             clutter_timeline_set_duration          (ClutterTimeline *timeline,
+                                                         guint            msecs);
+guint            clutter_timeline_get_speed             (ClutterTimeline *timeline);
+void             clutter_timeline_set_speed             (ClutterTimeline *timeline,
+                                                         guint            fps);
+ClutterTimelineDirection clutter_timeline_get_direction (ClutterTimeline *timeline);
+void             clutter_timeline_set_direction         (ClutterTimeline *timeline,
+                                                         ClutterTimelineDirection direction);
+void             clutter_timeline_start                 (ClutterTimeline *timeline);
+void             clutter_timeline_pause                 (ClutterTimeline *timeline);
+void             clutter_timeline_stop                  (ClutterTimeline *timeline);
+void             clutter_timeline_set_loop              (ClutterTimeline *timeline,
+                                                         gboolean         loop);
+gboolean         clutter_timeline_get_loop              (ClutterTimeline *timeline);
+void             clutter_timeline_rewind                (ClutterTimeline *timeline);
+void             clutter_timeline_skip                  (ClutterTimeline *timeline,
+                                                         guint            n_frames);
+void             clutter_timeline_advance               (ClutterTimeline *timeline,
+                                                         guint            frame_num);
+gint             clutter_timeline_get_current_frame     (ClutterTimeline *timeline);
+gdouble          clutter_timeline_get_progress          (ClutterTimeline *timeline);
+ClutterFixed     clutter_timeline_get_progressx         (ClutterTimeline *timeline);
+void             clutter_timeline_set_n_frames          (ClutterTimeline *timeline,
+                                                         guint            n_frames);
+guint            clutter_timeline_get_n_frames          (ClutterTimeline *timeline);
+gboolean         clutter_timeline_is_playing            (ClutterTimeline *timeline);
+void             clutter_timeline_set_delay             (ClutterTimeline *timeline,
+                                                         guint            msecs);
+guint            clutter_timeline_get_delay             (ClutterTimeline *timeline);
 
 G_END_DECLS
 
diff --git a/doc/reference/ChangeLog b/doc/reference/ChangeLog
index cc1d8d779..dfc687a56 100644
--- a/doc/reference/ChangeLog
+++ b/doc/reference/ChangeLog
@@ -1,3 +1,7 @@
+2007-11-15  Emmanuele Bassi  <ebassi@openedhand.com>
+
+	* clutter-sections.txt: Add new ClutterTimeline API
+
 2007-11-15  Emmanuele Bassi  <ebassi@openedhand.com>
 
 	* clutter-sections.txt: Add new ClutterEffectTemplate constructor.
diff --git a/doc/reference/clutter-sections.txt b/doc/reference/clutter-sections.txt
index 7d83700b7..dd9b9ce52 100644
--- a/doc/reference/clutter-sections.txt
+++ b/doc/reference/clutter-sections.txt
@@ -484,23 +484,36 @@ clutter_perspective_get_type
 ClutterTimeline
 ClutterTimelineClass
 clutter_timeline_new
+clutter_timeline_new_for_duration
 clutter_timeline_clone
+
+<SUBSECTION>
 clutter_timeline_set_speed
 clutter_timeline_get_speed
+clutter_timeline_set_duration
+clutter_timeline_get_duration
+clutter_timeline_set_loop
+clutter_timeline_get_loop
+clutter_timeline_set_n_frames
+clutter_timeline_get_n_frames
+clutter_timeline_set_delay
+clutter_timeline_get_delay
+ClutterTimelineDirection
+clutter_timeline_set_direction
+clutter_timeline_get_direction
+
+<SUBSECTION
 clutter_timeline_start
 clutter_timeline_pause
 clutter_timeline_stop
-clutter_timeline_set_loop
-clutter_timeline_get_loop
 clutter_timeline_rewind
 clutter_timeline_skip
 clutter_timeline_advance
 clutter_timeline_get_current_frame
-clutter_timeline_set_n_frames
-clutter_timeline_get_n_frames
+clutter_timeline_get_progress
+clutter_timeline_get_progressx
 clutter_timeline_is_playing
-clutter_timeline_set_delay
-clutter_timeline_get_delay
+
 <SUBSECTION Standard>
 CLUTTER_TIMELINE
 CLUTTER_IS_TIMELINE
diff --git a/tests/test-timeline.c b/tests/test-timeline.c
index 9b2b74caa..506b23cb9 100644
--- a/tests/test-timeline.c
+++ b/tests/test-timeline.c
@@ -48,8 +48,11 @@ main (int argc, char **argv)
   clutter_init (&argc, &argv);
 
   timeline_1 = clutter_timeline_new (10, 120);
+
   timeline_2 = clutter_timeline_clone (timeline_1);
+
   timeline_3 = clutter_timeline_clone (timeline_1);
+  clutter_timeline_set_direction (timeline_3, CLUTTER_TIMELINE_BACKWARD);
 
   g_signal_connect (timeline_1,
                     "new-frame", G_CALLBACK (timeline_1_new_frame_cb),