ges: Add a timeline edition mode API
[platform/upstream/gstreamer.git] / ges / ges-timeline.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
3  *               2009 Nokia Corporation
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:ges-timeline
23  * @short_description: Multimedia timeline
24  *
25  * #GESTimeline is the central object for any multimedia timeline.
26  *
27  * Contains a list of #GESTimelineLayer which users should use to arrange the
28  * various timeline objects through time.
29  *
30  * The output type is determined by the #GESTrack that are set on
31  * the #GESTimeline.
32  *
33  * To save/load a timeline, you can use the ges_timeline_load_from_uri() and
34  * ges_timeline_save_to_uri() methods to use the default format. If you wish
35  * to specify the format to save/load the timeline from, please consult the
36  * documentation about #GESFormatter.
37  */
38
39 #include "gesmarshal.h"
40 #include "ges-internal.h"
41 #include "ges-timeline.h"
42 #include "ges-track.h"
43 #include "ges-timeline-layer.h"
44 #include "ges.h"
45
46 typedef struct _MoveContext MoveContext;
47
48 static inline void init_movecontext (MoveContext * mv_ctx);
49
50 G_DEFINE_TYPE (GESTimeline, ges_timeline, GST_TYPE_BIN);
51
52 #define GES_TIMELINE_PENDINGOBJS_GET_LOCK(timeline) \
53   (GES_TIMELINE(timeline)->priv->pendingobjects_lock)
54 #define GES_TIMELINE_PENDINGOBJS_LOCK(timeline) \
55   (g_mutex_lock(GES_TIMELINE_PENDINGOBJS_GET_LOCK (timeline)))
56 #define GES_TIMELINE_PENDINGOBJS_UNLOCK(timeline) \
57   (g_mutex_unlock(GES_TIMELINE_PENDINGOBJS_GET_LOCK (timeline)))
58
59 /**
60  *  The move context is used for the timeline editing modes functions in order to
61  *  + Ripple / Roll /  Slide / Move / Trim
62  *
63  * The context aims at avoiding to recalculate values/objects on each call of the
64  * editing functions.
65  */
66 struct _MoveContext
67 {
68   GESTimelineObject *obj;
69   GESEdge edge;
70   GESEditMode mode;
71
72   /* Ripple and Roll Objects */
73   GList *moving_tckobjs;
74
75   /* We use it as a set of TimelineObject to move between layers */
76   GHashTable *moving_tlobjs;
77   /* Min priority of the objects currently in moving_tlobjs */
78   guint min_move_layer;
79   /* Max priority of the objects currently in moving_tlobjs */
80   guint max_layer_prio;
81
82   /* Never trim so duration would becomes < 0 */
83   guint64 max_trim_pos;
84
85   /* fields to force/avoid new context */
86   /* Set to %TRUE when the track is doing updates of track objects
87    * properties so we don't end up always needing new move context */
88   gboolean ignore_needs_ctx;
89   gboolean needs_move_ctx;
90 };
91
92 struct _GESTimelinePrivate
93 {
94   GList *layers;                /* A list of GESTimelineLayer sorted by priority */
95   GList *tracks;                /* A list of private track data */
96
97   /* The duration of the timeline */
98   gint64 duration;
99
100   /* discoverer used for virgin sources */
101   GstDiscoverer *discoverer;
102   GList *pendingobjects;
103   /* lock to avoid discovery of objects that will be removed */
104   GMutex *pendingobjects_lock;
105
106   /* Whether we are changing state asynchronously or not */
107   gboolean async_pending;
108
109   /* Timeline edition modes and snapping management */
110   guint64 snapping_distance;
111
112   /* FIXME: Should we offer an API over those fields ?
113    * FIXME: Should other classes than subclasses of TrackSource also
114    * be tracked? */
115
116   /* Snapping fields */
117   GHashTable *by_start;         /* {TrackSource: start} */
118   GHashTable *by_end;           /* {TrackSource: end} */
119   GHashTable *by_object;        /* {timecode: TrackSource} */
120   GSequence *starts_ends;       /* Sorted list of starts/ends */
121   /* We keep 1 reference to our trackobject here */
122   GSequence *tracksources;      /* TrackSource-s sorted by start/priorities */
123
124   MoveContext movecontext;
125 };
126
127 /* private structure to contain our track-related information */
128
129 typedef struct
130 {
131   GESTimeline *timeline;
132   GESTrack *track;
133   GstPad *pad;                  /* Pad from the track */
134   GstPad *ghostpad;
135 } TrackPrivate;
136
137 enum
138 {
139   PROP_0,
140   PROP_DURATION,
141   PROP_SNAPPING_DISTANCE,
142   PROP_LAST
143 };
144
145 static GParamSpec *properties[PROP_LAST];
146
147 enum
148 {
149   TRACK_ADDED,
150   TRACK_REMOVED,
151   LAYER_ADDED,
152   LAYER_REMOVED,
153   DISCOVERY_ERROR,
154   OBJECT_SNAPPING,
155   LAST_SIGNAL
156 };
157
158 static GstBinClass *parent_class;
159
160 static guint ges_timeline_signals[LAST_SIGNAL] = { 0 };
161
162 static gint custom_find_track (TrackPrivate * tr_priv, GESTrack * track);
163 static GstStateChangeReturn
164 ges_timeline_change_state (GstElement * element, GstStateChange transition);
165 static void
166 discoverer_finished_cb (GstDiscoverer * discoverer, GESTimeline * timeline);
167 static void
168 discoverer_discovered_cb (GstDiscoverer * discoverer,
169     GstDiscovererInfo * info, GError * err, GESTimeline * timeline);
170
171 /* GObject Standard vmethods*/
172 static void
173 ges_timeline_get_property (GObject * object, guint property_id,
174     GValue * value, GParamSpec * pspec)
175 {
176   GESTimeline *timeline = GES_TIMELINE (object);
177
178   switch (property_id) {
179     default:
180       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
181     case PROP_DURATION:
182       g_value_set_uint64 (value, timeline->priv->duration);
183       break;
184     case PROP_SNAPPING_DISTANCE:
185       g_value_set_uint64 (value, timeline->priv->snapping_distance);
186       break;
187   }
188 }
189
190 static void
191 ges_timeline_set_property (GObject * object, guint property_id,
192     const GValue * value, GParamSpec * pspec)
193 {
194   GESTimeline *timeline = GES_TIMELINE (object);
195
196   switch (property_id) {
197     case PROP_SNAPPING_DISTANCE:
198       timeline->priv->snapping_distance = g_value_get_uint64 (value);
199       break;
200     default:
201       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
202   }
203 }
204
205 static void
206 ges_timeline_dispose (GObject * object)
207 {
208   GESTimelinePrivate *priv = GES_TIMELINE (object)->priv;
209
210   if (priv->discoverer) {
211     gst_discoverer_stop (priv->discoverer);
212     g_object_unref (priv->discoverer);
213     priv->discoverer = NULL;
214   }
215
216   while (priv->layers) {
217     GESTimelineLayer *layer = (GESTimelineLayer *) priv->layers->data;
218     ges_timeline_remove_layer (GES_TIMELINE (object), layer);
219   }
220
221   /* FIXME: it should be possible to remove tracks before removing
222    * layers, but at the moment this creates a problem because the track
223    * objects aren't notified that their gnlobjects have been destroyed.
224    */
225
226   while (priv->tracks) {
227     TrackPrivate *tr_priv = (TrackPrivate *) priv->tracks->data;
228     ges_timeline_remove_track (GES_TIMELINE (object), tr_priv->track);
229   }
230
231   g_hash_table_unref (priv->by_start);
232   g_hash_table_unref (priv->by_end);
233   g_hash_table_unref (priv->by_object);
234   g_sequence_free (priv->starts_ends);
235   g_sequence_free (priv->tracksources);
236
237   G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
238 }
239
240 static void
241 ges_timeline_finalize (GObject * object)
242 {
243   GESTimeline *timeline = GES_TIMELINE (object);
244
245   g_mutex_free (timeline->priv->pendingobjects_lock);
246
247   G_OBJECT_CLASS (ges_timeline_parent_class)->finalize (object);
248 }
249
250 static void
251 ges_timeline_class_init (GESTimelineClass * klass)
252 {
253   GObjectClass *object_class = G_OBJECT_CLASS (klass);
254   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
255
256   g_type_class_add_private (klass, sizeof (GESTimelinePrivate));
257
258   parent_class = g_type_class_peek_parent (klass);
259
260   element_class->change_state = ges_timeline_change_state;
261
262   object_class->get_property = ges_timeline_get_property;
263   object_class->set_property = ges_timeline_set_property;
264   object_class->dispose = ges_timeline_dispose;
265   object_class->finalize = ges_timeline_finalize;
266
267   /**
268    * GESTimelineObject:duration
269    *
270    * Current duration (in nanoseconds) of the #GESTimeline
271    *
272    * Default value: 0
273    */
274   properties[PROP_DURATION] =
275       g_param_spec_uint64 ("duration", "Duration",
276       "The duration of the timeline", 0, G_MAXUINT64,
277       GST_CLOCK_TIME_NONE, G_PARAM_READABLE);
278   g_object_class_install_property (object_class, PROP_DURATION,
279       properties[PROP_DURATION]);
280
281   /**
282    * GESTimeline:snapping-distance
283    *
284    * Distance (in nanoseconds) from which a moving object will snap
285    * with it neighboors. 0 means no snapping.
286    */
287   properties[PROP_SNAPPING_DISTANCE] =
288       g_param_spec_uint64 ("snapping-distance", "Snapping distance",
289       "Distance from which moving an object will snap with neighboors", 0,
290       G_MAXUINT64, 0, G_PARAM_READWRITE);
291   g_object_class_install_property (object_class, PROP_SNAPPING_DISTANCE,
292       properties[PROP_SNAPPING_DISTANCE]);
293
294   /**
295    * GESTimeline::track-added
296    * @timeline: the #GESTimeline
297    * @track: the #GESTrack that was added to the timeline
298    *
299    * Will be emitted after the track was added to the timeline.
300    */
301   ges_timeline_signals[TRACK_ADDED] =
302       g_signal_new ("track-added", G_TYPE_FROM_CLASS (klass),
303       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_added), NULL,
304       NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GES_TYPE_TRACK);
305
306   /**
307    * GESTimeline::track-removed
308    * @timeline: the #GESTimeline
309    * @track: the #GESTrack that was removed from the timeline
310    *
311    * Will be emitted after the track was removed from the timeline.
312    */
313   ges_timeline_signals[TRACK_REMOVED] =
314       g_signal_new ("track-removed", G_TYPE_FROM_CLASS (klass),
315       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_removed),
316       NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GES_TYPE_TRACK);
317
318   /**
319    * GESTimeline::layer-added
320    * @timeline: the #GESTimeline
321    * @layer: the #GESTimelineLayer that was added to the timeline
322    *
323    * Will be emitted after the layer was added to the timeline.
324    */
325   ges_timeline_signals[LAYER_ADDED] =
326       g_signal_new ("layer-added", G_TYPE_FROM_CLASS (klass),
327       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_added), NULL,
328       NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GES_TYPE_TIMELINE_LAYER);
329
330   /**
331    * GESTimeline::layer-removed
332    * @timeline: the #GESTimeline
333    * @layer: the #GESTimelineLayer that was removed from the timeline
334    *
335    * Will be emitted after the layer was removed from the timeline.
336    */
337   ges_timeline_signals[LAYER_REMOVED] =
338       g_signal_new ("layer-removed", G_TYPE_FROM_CLASS (klass),
339       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_removed),
340       NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
341       GES_TYPE_TIMELINE_LAYER);
342
343   /**
344    * GESTimeline::discovery-error:
345    * @timeline: the #GESTimeline
346    * @formatter: the #GESFormatter
347    * @source: The #GESTimelineFileSource that could not be discovered properly
348    * @error: (type GLib.Error): #GError, which will be non-NULL if an error
349    *                            occurred during discovery
350    */
351   ges_timeline_signals[DISCOVERY_ERROR] =
352       g_signal_new ("discovery-error", G_TYPE_FROM_CLASS (klass),
353       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, gst_marshal_VOID__OBJECT_BOXED,
354       G_TYPE_NONE, 2, GES_TYPE_TIMELINE_FILE_SOURCE, GST_TYPE_G_ERROR);
355
356   /**
357    * GESTimeline::track-objects-snapping:
358    * @timeline: the #GESTimeline
359    * @obj1: the first #GESTrackObject that was snapping.
360    * @obj2: the second #GESTrackObject that was snapping.
361    * @position: the position where the two objects finally snapping.
362    *
363    * Will be emitted after two track objects snapped together.
364    *
365    * Since: 0.10.XX
366    */
367   ges_timeline_signals[OBJECT_SNAPPING] =
368       g_signal_new ("track-objects-snapping", G_TYPE_FROM_CLASS (klass),
369       G_SIGNAL_RUN_LAST, 0, NULL, NULL, ges_marshal_VOID__OBJECT,
370       G_TYPE_NONE, 3, GES_TYPE_TRACK_OBJECT, GES_TYPE_TRACK_OBJECT,
371       G_TYPE_UINT64);
372 }
373
374 static void
375 ges_timeline_init (GESTimeline * self)
376 {
377   GESTimelinePrivate *priv;
378
379   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
380       GES_TYPE_TIMELINE, GESTimelinePrivate);
381
382   priv = self->priv;
383   priv->layers = NULL;
384   priv->tracks = NULL;
385   priv->duration = 0;
386   priv->snapping_distance = 0;
387
388   /* Move context initialization */
389   init_movecontext (&self->priv->movecontext);
390   priv->movecontext.ignore_needs_ctx = FALSE;
391
392   priv->by_start = g_hash_table_new (g_direct_hash, g_direct_equal);
393   priv->by_end = g_hash_table_new (g_direct_hash, g_direct_equal);
394   priv->by_object = g_hash_table_new (g_direct_hash, g_direct_equal);
395   priv->starts_ends = g_sequence_new (g_free);
396   priv->tracksources = g_sequence_new (g_object_unref);
397
398   priv->pendingobjects_lock = g_mutex_new ();
399   /* New discoverer with a 15s timeout */
400   priv->discoverer = gst_discoverer_new (15 * GST_SECOND, NULL);
401   g_signal_connect (priv->discoverer, "finished",
402       G_CALLBACK (discoverer_finished_cb), self);
403   g_signal_connect (priv->discoverer, "discovered",
404       G_CALLBACK (discoverer_discovered_cb), self);
405   gst_discoverer_start (priv->discoverer);
406 }
407
408 /* Private methods */
409
410 /* Sorting utils*/
411 static gint
412 sort_layers (gpointer a, gpointer b)
413 {
414   GESTimelineLayer *layer_a, *layer_b;
415   guint prio_a, prio_b;
416
417   layer_a = GES_TIMELINE_LAYER (a);
418   layer_b = GES_TIMELINE_LAYER (b);
419
420   prio_a = ges_timeline_layer_get_priority (layer_a);
421   prio_b = ges_timeline_layer_get_priority (layer_b);
422
423   if ((gint) prio_a > (guint) prio_b)
424     return 1;
425   if ((guint) prio_a < (guint) prio_b)
426     return -1;
427
428   return 0;
429 }
430
431 static gint
432 objects_start_compare (GESTrackObject * a, GESTrackObject * b)
433 {
434   if (a->start == b->start) {
435     if (a->priority < b->priority)
436       return -1;
437     if (a->priority > b->priority)
438       return 1;
439     return 0;
440   }
441   if (a->start < b->start)
442     return -1;
443   if (a->start > b->start)
444     return 1;
445   return 0;
446 }
447
448 static inline void
449 sort_track_objects (GESTimeline * timeline)
450 {
451   g_sequence_sort (timeline->priv->tracksources,
452       (GCompareDataFunc) objects_start_compare, NULL);
453 }
454
455 static gint
456 compare_uint64 (guint64 * a, guint64 * b, gpointer user_data)
457 {
458   if (*a > *b)
459     return 1;
460   else if (*a == *b)
461     return 0;
462   else
463     return -1;
464 }
465
466 static gint
467 custom_find_track (TrackPrivate * tr_priv, GESTrack * track)
468 {
469   if (tr_priv->track == track)
470     return 0;
471   return -1;
472 }
473
474 /* Look for the pointer passed as @value */
475 static GSequenceIter *
476 lookup_pointer_uint (GSequence * seq, guint64 * value)
477 {
478   GSequenceIter *iter, *tmpiter;
479   guint64 *found, *tmpval;
480
481   iter = g_sequence_lookup (seq, value,
482       (GCompareDataFunc) compare_uint64, NULL);
483
484   found = g_sequence_get (iter);
485
486   /* We have the same pointer so we are all fine */
487   if (found == value)
488     return iter;
489
490   if (!g_sequence_iter_is_end (iter)) {
491     /* Looking frontward for the good pointer */
492     tmpiter = iter;
493     while ((tmpiter = g_sequence_iter_next (tmpiter))) {
494       if (g_sequence_iter_is_end (tmpiter))
495         break;
496
497       tmpval = g_sequence_get (tmpiter);
498       if (tmpval == value)
499         return tmpiter;
500       else if (*tmpval != *value)
501         break;
502     }
503   }
504
505   if (!g_sequence_iter_is_begin (iter)) {
506     /* Looking backward for the good pointer */
507     tmpiter = iter;
508     while ((tmpiter = g_sequence_iter_prev (tmpiter))) {
509       tmpval = g_sequence_get (tmpiter);
510       if (tmpval == value)
511         return tmpiter;
512       else if (*tmpval != *value || g_sequence_iter_is_begin (tmpiter))
513         break;
514     }
515   }
516
517   GST_ERROR ("Missing timecode %p %" GST_TIME_FORMAT
518       " this should never happen", value, GST_TIME_ARGS (*value));
519
520   return NULL;
521 }
522
523 static inline void
524 sort_starts_ends_end (GESTimeline * timeline, GESTrackObject * obj)
525 {
526   GSequenceIter *iter;
527
528   GESTimelinePrivate *priv = timeline->priv;
529   guint64 *end = g_hash_table_lookup (priv->by_end, obj);
530
531   iter = lookup_pointer_uint (priv->starts_ends, end);
532   *end = obj->start + obj->duration;
533
534   g_sequence_sort_changed (iter, (GCompareDataFunc) compare_uint64, NULL);
535 }
536
537 static inline void
538 sort_starts_ends_start (GESTimeline * timeline, GESTrackObject * obj)
539 {
540   GSequenceIter *iter;
541
542   GESTimelinePrivate *priv = timeline->priv;
543   guint64 *start = g_hash_table_lookup (priv->by_start, obj);
544
545   iter = lookup_pointer_uint (priv->starts_ends, start);
546   *start = obj->start;
547
548   g_sequence_sort_changed (iter, (GCompareDataFunc) compare_uint64, NULL);
549 }
550
551 static inline void
552 resort_all_starts_ends (GESTimeline * timeline)
553 {
554   GSequenceIter *iter;
555   GESTrackObject *tckobj;
556   guint64 *start, *end;
557
558   GESTimelinePrivate *priv = timeline->priv;
559
560   for (iter = g_sequence_get_begin_iter (priv->tracksources);
561       !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
562     tckobj = GES_TRACK_OBJECT (g_sequence_get (iter));
563
564     start = g_hash_table_lookup (priv->by_start, tckobj);
565     end = g_hash_table_lookup (priv->by_end, tckobj);
566
567     *start = tckobj->start;
568     *end = tckobj->start + tckobj->duration;
569   }
570
571   g_sequence_sort (priv->starts_ends, (GCompareDataFunc) compare_uint64, NULL);
572 }
573
574 static inline void
575 sort_all (GESTimeline * timeline)
576 {
577   sort_track_objects (timeline);
578   resort_all_starts_ends (timeline);
579 }
580
581 /* Timeline edition functions */
582 static inline void
583 init_movecontext (MoveContext * mv_ctx)
584 {
585   mv_ctx->moving_tckobjs = NULL;
586   mv_ctx->moving_tlobjs = g_hash_table_new (g_direct_hash, g_direct_equal);
587   mv_ctx->max_trim_pos = G_MAXUINT64;
588   mv_ctx->min_move_layer = G_MAXUINT;
589   mv_ctx->max_layer_prio = 0;
590 }
591
592 static inline void
593 clean_movecontext (MoveContext * mv_ctx)
594 {
595   g_list_free (mv_ctx->moving_tckobjs);
596   g_hash_table_unref (mv_ctx->moving_tlobjs);
597   init_movecontext (mv_ctx);
598 }
599
600 static void
601 stop_tracking_for_snapping (GESTimeline * timeline, GESTrackObject * tckobj)
602 {
603   guint64 *start, *end;
604   GESTimelinePrivate *priv = timeline->priv;
605   GSequenceIter *iter_start, *iter_end, *tckobj_iter;
606
607
608   start = g_hash_table_lookup (priv->by_start, tckobj);
609   end = g_hash_table_lookup (priv->by_end, tckobj);
610
611   iter_start = lookup_pointer_uint (priv->starts_ends, start);
612   iter_end = lookup_pointer_uint (priv->starts_ends, end);
613   tckobj_iter = g_sequence_lookup (timeline->priv->tracksources, tckobj,
614       (GCompareDataFunc) objects_start_compare, NULL);
615
616   g_hash_table_remove (priv->by_start, tckobj);
617   g_hash_table_remove (priv->by_end, tckobj);
618   g_hash_table_remove (priv->by_object, end);
619   g_hash_table_remove (priv->by_object, start);
620
621   g_sequence_remove (iter_start);
622   g_sequence_remove (iter_end);
623   g_sequence_remove (tckobj_iter);
624
625 }
626
627 static void
628 start_tracking_track_obj (GESTimeline * timeline, GESTrackObject * tckobj)
629 {
630   guint64 *pstart, *pend;
631   GESTimelinePrivate *priv = timeline->priv;
632
633   pstart = g_malloc (sizeof (guint64));
634   pend = g_malloc (sizeof (guint64));
635   *pstart = tckobj->start;
636   *pend = *pstart + tckobj->duration;
637
638   g_sequence_insert_sorted (priv->starts_ends, pstart,
639       (GCompareDataFunc) compare_uint64, NULL);
640   g_sequence_insert_sorted (priv->starts_ends, pend,
641       (GCompareDataFunc) compare_uint64, NULL);
642   g_sequence_insert_sorted (priv->tracksources, g_object_ref (tckobj),
643       (GCompareDataFunc) objects_start_compare, NULL);
644
645   g_hash_table_insert (priv->by_start, tckobj, pstart);
646   g_hash_table_insert (priv->by_object, pstart, tckobj);
647   g_hash_table_insert (priv->by_end, tckobj, pend);
648   g_hash_table_insert (priv->by_object, pend, tckobj);
649
650   timeline->priv->movecontext.needs_move_ctx = TRUE;
651 }
652
653 static inline void
654 ges_timeline_emit_snappig (GESTimeline * timeline, GESTrackObject * obj1,
655     guint64 * timecode)
656 {
657   GESTrackObject *obj2;
658
659   obj2 = g_hash_table_lookup (timeline->priv->by_object, timecode);
660
661   g_signal_emit (timeline, ges_timeline_signals[OBJECT_SNAPPING], 0,
662       obj1, obj2, *timecode);
663
664   /* We then need to recalculate the moving context */
665   timeline->priv->movecontext.needs_move_ctx = TRUE;
666 }
667
668 /* If passing @trackobj will emit the snapping signal, otherwize it won't */
669 static guint64 *
670 ges_timeline_snap_position (GESTimeline * timeline, GESTrackObject * trackobj,
671     guint64 * current, guint64 timecode)
672 {
673   GESTimelinePrivate *priv = timeline->priv;
674   GSequenceIter *iter, *prev_iter, *nxt_iter;
675   GESTrackObject *tmp_tckobj;
676   GESTimelineObject *tmp_tlobj, *tlobj;
677
678   guint64 snap_distance = timeline->priv->snapping_distance;
679
680   guint64 *prev_tc, *next_tc, *ret = NULL, off = G_MAXUINT64, off1 =
681       G_MAXUINT64;
682
683   /* Avoid useless calculations */
684   if (snap_distance == 0)
685     return NULL;
686
687   tlobj = ges_track_object_get_timeline_object (trackobj);
688
689   iter = g_sequence_search (priv->starts_ends, &timecode,
690       (GCompareDataFunc) compare_uint64, NULL);
691
692   /* Getting the next/previous  values, and use the closest one if any "respects"
693    * the snap_distance value */
694   nxt_iter = iter;
695   while (!g_sequence_iter_is_end (nxt_iter)) {
696     next_tc = g_sequence_get (iter);
697     tmp_tckobj = g_hash_table_lookup (timeline->priv->by_object, next_tc);
698     tmp_tlobj = ges_track_object_get_timeline_object (tmp_tckobj);
699
700     off = timecode > *next_tc ? timecode - *next_tc : *next_tc - timecode;
701     if (next_tc != current && off <= snap_distance && tlobj != tmp_tlobj) {
702
703       ret = next_tc;
704       break;
705     }
706
707     nxt_iter = g_sequence_iter_next (nxt_iter);
708   }
709
710   prev_iter = g_sequence_iter_prev (iter);
711   while (!g_sequence_iter_is_begin (prev_iter)) {
712     prev_tc = g_sequence_get (prev_iter);
713     tmp_tckobj = g_hash_table_lookup (timeline->priv->by_object, prev_tc);
714     tmp_tlobj = ges_track_object_get_timeline_object (tmp_tckobj);
715
716     off1 = timecode > *prev_tc ? timecode - *prev_tc : *prev_tc - timecode;
717     if (prev_tc != current && off1 < off && off1 <= snap_distance &&
718         tlobj != tmp_tlobj) {
719       ret = prev_tc;
720
721       break;
722     }
723
724     prev_iter = g_sequence_iter_prev (prev_iter);
725   }
726
727   /* We emit the snapping signal only if we snapped with a different value
728    * than the current one */
729   if (trackobj && ret)
730     ges_timeline_emit_snappig (timeline, trackobj, ret);
731
732   return ret;
733 }
734
735 static inline GESTimelineObject *
736 add_moving_timeline_object (MoveContext * mv_ctx, GESTrackObject * tckobj)
737 {
738   GESTimelineObject *tlobj;
739   GESTimelineLayer *layer;
740   guint layer_prio;
741
742   tlobj = ges_track_object_get_timeline_object (tckobj);
743
744   /* Avoid recalculating */
745   if (!g_hash_table_lookup (mv_ctx->moving_tlobjs, tlobj)) {
746     layer = ges_timeline_object_get_layer (tlobj);
747     if (layer == NULL) {
748       GST_WARNING_OBJECT (tlobj, "Not in any layer, can not move"
749           " between layers");
750
751     } else {
752
753       g_hash_table_insert (mv_ctx->moving_tlobjs, tlobj, tlobj);
754
755       layer_prio = ges_timeline_layer_get_priority (layer);
756       mv_ctx->min_move_layer = MIN (mv_ctx->min_move_layer, layer_prio);
757       mv_ctx->max_layer_prio = MAX (mv_ctx->max_layer_prio, layer_prio);
758
759       g_object_unref (layer);
760     }
761   }
762
763   return tlobj;
764 }
765
766 static gboolean
767 ges_move_context_set_objects (GESTimeline * timeline, GESTrackObject * obj,
768     GESEdge edge)
769 {
770   GSequenceIter *iter, *tckobj_iter;
771   guint64 start, end, tmpend;
772   GESTrackObject *tmptckobj;
773
774   MoveContext *mv_ctx = &timeline->priv->movecontext;
775
776   tckobj_iter = g_sequence_lookup (timeline->priv->tracksources, obj,
777       (GCompareDataFunc) objects_start_compare, NULL);
778
779   switch (edge) {
780     case GES_EDGE_START:
781       /* set it properly int the context of "trimming" */
782       mv_ctx->max_trim_pos = 0;
783       start = obj->start;
784
785       if (g_sequence_iter_is_begin (tckobj_iter))
786         break;
787
788       /* Look for the objects */
789       for (iter = g_sequence_iter_prev (tckobj_iter);
790           iter && !g_sequence_iter_is_end (iter);
791           iter = g_sequence_iter_prev (iter)) {
792
793         tmptckobj = GES_TRACK_OBJECT (g_sequence_get (iter));
794         tmpend = tmptckobj->start + tmptckobj->duration;
795
796         if (tmpend <= start) {
797           mv_ctx->max_trim_pos = MAX (mv_ctx->max_trim_pos, tmptckobj->start);
798           mv_ctx->moving_tckobjs =
799               g_list_prepend (mv_ctx->moving_tckobjs, tmptckobj);
800         }
801
802         if (g_sequence_iter_is_begin (iter))
803           break;
804       }
805       break;
806
807     case GES_EDGE_END:
808     case GES_EDGE_NONE:        /* In this case only works for ripple */
809       end = ges_track_object_get_start (obj) +
810           ges_track_object_get_duration (obj);
811
812       mv_ctx->max_trim_pos = G_MAXUINT64;
813
814       /* Look for folowing objects */
815       for (iter = g_sequence_iter_next (tckobj_iter);
816           iter && !g_sequence_iter_is_end (iter);
817           iter = g_sequence_iter_next (iter)) {
818         tmptckobj = GES_TRACK_OBJECT (g_sequence_get (iter));
819
820         if (tmptckobj->start >= end) {
821           tmpend = tmptckobj->start + tmptckobj->duration;
822           mv_ctx->max_trim_pos = MIN (mv_ctx->max_trim_pos, tmpend);
823           mv_ctx->moving_tckobjs =
824               g_list_prepend (mv_ctx->moving_tckobjs, tmptckobj);
825         }
826       }
827       break;
828     default:
829       GST_DEBUG ("Edge type %d no supported", edge);
830       return FALSE;
831   }
832
833   return TRUE;
834 }
835
836 static gboolean
837 ges_timeline_set_moving_context (GESTimeline * timeline, GESTrackObject * obj,
838     GESEditMode mode, GESEdge edge, GList * layers)
839 {
840   MoveContext *mv_ctx = &timeline->priv->movecontext;
841   GESTimelineObject *tlobj = ges_track_object_get_timeline_object (obj);
842
843   /* Still in the same mv_ctx */
844   if ((mv_ctx->obj == tlobj && mv_ctx->mode == mode &&
845           mv_ctx->edge == edge && !mv_ctx->needs_move_ctx)) {
846
847     GST_DEBUG ("Keeping the same moving mv_ctx");
848     return TRUE;
849   }
850
851   GST_DEBUG_OBJECT (tlobj,
852       "Changing context:\nold: obj: %p, mode: %d, edge: %d \n"
853       "new: obj: %p, mode: %d, edge: %d ! Has changed %i", mv_ctx->obj,
854       mv_ctx->mode, mv_ctx->edge, tlobj, mode, edge, mv_ctx->needs_move_ctx);
855
856   clean_movecontext (mv_ctx);
857   mv_ctx->edge = edge;
858   mv_ctx->mode = mode;
859   mv_ctx->obj = tlobj;
860   mv_ctx->needs_move_ctx = FALSE;
861
862   switch (mode) {
863     case GES_EDIT_MODE_RIPPLE:
864     case GES_EDIT_MODE_ROLL:
865       if (!(ges_move_context_set_objects (timeline, obj, edge)))
866         return FALSE;
867     default:
868       break;
869   }
870
871   /* And we add the main object to the moving_tlobjs set */
872   add_moving_timeline_object (&timeline->priv->movecontext, obj);
873
874
875   return TRUE;
876 }
877
878 gboolean
879 ges_timeline_trim_object_simple (GESTimeline * timeline, GESTrackObject * obj,
880     GList * layers, GESEdge edge, guint64 position, gboolean snapping)
881 {
882   guint64 nstart, start, inpoint, duration, max_duration, *snapped, *cur;
883   gboolean ret = TRUE;
884   gint64 real_dur;
885
886   GST_DEBUG_OBJECT (obj, "Trimming to %" GST_TIME_FORMAT " %s snaping, edge %i",
887       GST_TIME_ARGS (position), snapping ? "Is" : "Not", edge);
888
889   start = ges_track_object_get_start (obj);
890   g_object_get (obj, "max-duration", &max_duration, NULL);
891
892   switch (edge) {
893     case GES_EDGE_START:
894       inpoint = obj->inpoint;
895       duration = obj->duration;
896
897       if (snapping) {
898         cur = g_hash_table_lookup (timeline->priv->by_start, obj);
899
900         snapped = ges_timeline_snap_position (timeline, obj, cur, position);
901         if (snapped)
902           position = *snapped;
903       }
904
905       nstart = position;
906
907       /* Calculate new values */
908       position = MAX (start > inpoint ? start - inpoint : 0, position);
909       position = MIN (position, start + duration);
910       inpoint = MAX (0, inpoint + position - start);
911
912       real_dur = start + duration - nstart;
913       /* FIXME: Why CLAMP (0, real_dur, max_duration) doesn't work? */
914       duration = MAX (0, real_dur);
915       duration = MIN (duration, max_duration - obj->inpoint);
916
917       ges_track_object_set_start (obj, nstart);
918       ges_track_object_set_duration (obj, duration);
919       ges_track_object_set_inpoint (obj, inpoint);
920       break;
921     case GES_EDGE_END:
922     {
923       cur = g_hash_table_lookup (timeline->priv->by_end, obj);
924       snapped = ges_timeline_snap_position (timeline, obj, cur, position);
925       if (snapped)
926         position = *snapped;
927
928       /* Calculate new values */
929       real_dur = position - start;
930       duration = MAX (0, real_dur);
931       duration = MIN (duration, max_duration - obj->inpoint);
932
933       ges_track_object_set_duration (obj, duration);
934       break;
935     }
936     default:
937       GST_WARNING ("Can not trim with %i GESEdge", edge);
938       return FALSE;
939   }
940
941   return ret;
942 }
943
944 gboolean
945 timeline_ripple_object (GESTimeline * timeline, GESTrackObject * obj,
946     GList * layers, GESEdge edge, guint64 position)
947 {
948   GList *tmp, *moved_tlobjs = NULL;
949   GESTrackObject *tckobj;
950   GESTimelineObject *tlobj;
951   guint64 duration, new_start, *snapped, *cur;
952   gint64 offset;
953
954   MoveContext *mv_ctx = &timeline->priv->movecontext;
955
956   mv_ctx->ignore_needs_ctx = TRUE;
957
958   if (!ges_timeline_set_moving_context (timeline, obj, GES_EDIT_MODE_RIPPLE,
959           edge, layers))
960     goto error;
961
962   switch (edge) {
963     case GES_EDGE_NONE:
964       GST_DEBUG ("Simply rippling");
965
966       cur = g_hash_table_lookup (timeline->priv->by_end, obj);
967       snapped = ges_timeline_snap_position (timeline, obj, cur, position);
968       if (snapped)
969         position = *snapped;
970
971       offset = position - obj->start;
972
973       for (tmp = mv_ctx->moving_tckobjs; tmp; tmp = tmp->next) {
974         tckobj = GES_TRACK_OBJECT (tmp->data);
975         new_start = tckobj->start + offset;
976
977         tlobj = add_moving_timeline_object (mv_ctx, tckobj);
978
979         if (ges_track_object_is_locked (tckobj) == TRUE) {
980
981           /* Make sure not to move 2 times the same TimelineObject */
982           if (g_list_find (moved_tlobjs, tlobj) == NULL) {
983             ges_track_object_set_start (tckobj, new_start);
984             moved_tlobjs = g_list_prepend (moved_tlobjs, tlobj);
985           }
986
987         } else {
988           ges_track_object_set_start (tckobj, new_start);
989         }
990       }
991       g_list_free (moved_tlobjs);
992       ges_track_object_set_start (obj, position);
993
994       break;
995     case GES_EDGE_END:
996       GST_DEBUG ("Rippling end");
997
998       cur = g_hash_table_lookup (timeline->priv->by_end, obj);
999       snapped = ges_timeline_snap_position (timeline, obj, cur, position);
1000       if (snapped)
1001         position = *snapped;
1002
1003       duration = obj->duration;
1004
1005       ges_track_object_set_duration (obj, position - obj->start);
1006
1007       offset = obj->duration - duration;
1008       for (tmp = mv_ctx->moving_tckobjs; tmp; tmp = tmp->next) {
1009         tckobj = GES_TRACK_OBJECT (tmp->data);
1010         new_start = tckobj->start + offset;
1011
1012         tlobj = add_moving_timeline_object (mv_ctx, tckobj);
1013
1014         if (ges_track_object_is_locked (tckobj) == TRUE) {
1015
1016           /* Make sure not to move 2 times the same TimelineObject */
1017           if (g_list_find (moved_tlobjs, tlobj) == NULL) {
1018             ges_track_object_set_start (tckobj, new_start);
1019             moved_tlobjs = g_list_prepend (moved_tlobjs, tlobj);
1020           }
1021
1022         } else {
1023           ges_track_object_set_start (tckobj, new_start);
1024         }
1025       }
1026
1027       g_list_free (moved_tlobjs);
1028       GST_DEBUG ("Done Rippling end");
1029       break;
1030     case GES_EDGE_START:
1031       GST_WARNING ("Ripple start doesn't exist!");
1032
1033       break;
1034     default:
1035       GST_DEBUG ("Can not ripple edge: %i", edge);
1036
1037       break;
1038   }
1039
1040   mv_ctx->ignore_needs_ctx = FALSE;
1041
1042   return TRUE;
1043
1044 error:
1045   mv_ctx->ignore_needs_ctx = FALSE;
1046
1047   return FALSE;
1048 }
1049
1050 gboolean
1051 timeline_slide_object (GESTimeline * timeline, GESTrackObject * obj,
1052     GList * layers, GESEdge edge, guint64 position)
1053 {
1054
1055   /* FIXME implement me! */
1056   GST_WARNING ("Slide mode editing not implemented yet");
1057
1058   return FALSE;
1059 }
1060
1061 gboolean
1062 timeline_trim_object (GESTimeline * timeline, GESTrackObject * object,
1063     GList * layers, GESEdge edge, guint64 position)
1064 {
1065   gboolean ret = FALSE;
1066   MoveContext *mv_ctx = &timeline->priv->movecontext;
1067
1068   mv_ctx->ignore_needs_ctx = TRUE;
1069
1070   if (!ges_timeline_set_moving_context (timeline, object, GES_EDIT_MODE_TRIM,
1071           edge, layers))
1072     goto end;
1073
1074   ret =
1075       ges_timeline_trim_object_simple (timeline, object, layers, edge, position,
1076       TRUE);
1077
1078 end:
1079   mv_ctx->ignore_needs_ctx = FALSE;
1080
1081   return ret;
1082 }
1083
1084 gboolean
1085 timeline_roll_object (GESTimeline * timeline, GESTrackObject * obj,
1086     GList * layers, GESEdge edge, guint64 position)
1087 {
1088   MoveContext *mv_ctx = &timeline->priv->movecontext;
1089   guint64 start, duration, end, tmpstart, tmpduration, tmpend, *snapped, *cur;
1090   gboolean ret = TRUE;
1091   GList *tmp;
1092
1093   mv_ctx->ignore_needs_ctx = TRUE;
1094
1095   GST_DEBUG_OBJECT (obj, "Rolling object to %" GST_TIME_FORMAT,
1096       GST_TIME_ARGS (position));
1097
1098   if (!ges_timeline_set_moving_context (timeline, obj, GES_EDIT_MODE_ROLL,
1099           edge, layers))
1100     goto error;
1101
1102   start = ges_track_object_get_start (obj);
1103   duration = ges_track_object_get_duration (obj);
1104   end = start + duration;
1105
1106   switch (edge) {
1107     case GES_EDGE_START:
1108
1109       /* Avoid negative durations */
1110       if (position < mv_ctx->max_trim_pos || position > end)
1111         goto error;
1112
1113       cur = g_hash_table_lookup (timeline->priv->by_start, obj);
1114       snapped = ges_timeline_snap_position (timeline, obj, cur, position);
1115       if (snapped)
1116         position = *snapped;
1117
1118       ret &=
1119           ges_timeline_trim_object_simple (timeline, obj, layers,
1120           GES_EDGE_START, position, FALSE);
1121
1122       /* In the case we reached max_duration we just make sure to roll
1123        * everything to the real new position */
1124       position = obj->start;
1125
1126       /* Send back changes to the neighbourhood */
1127       for (tmp = mv_ctx->moving_tckobjs; tmp; tmp = tmp->next) {
1128         GESTrackObject *tmptckobj = GES_TRACK_OBJECT (tmp->data);
1129
1130         tmpstart = ges_track_object_get_start (tmptckobj);
1131         tmpduration = ges_track_object_get_duration (tmptckobj);
1132         tmpend = tmpstart + tmpduration;
1133
1134         /* Check that the object should be resized at this position
1135          * even if an error accurs, we keep doing our job */
1136         if (tmpend == start) {
1137           ret &= ges_timeline_trim_object_simple (timeline, tmptckobj, NULL,
1138               GES_EDGE_END, position, FALSE);
1139           break;
1140         }
1141       }
1142       break;
1143     case GES_EDGE_END:
1144
1145       /* Avoid negative durations */
1146       if (position > mv_ctx->max_trim_pos || position < start)
1147         goto error;
1148
1149       end = obj->start + obj->duration;
1150
1151       cur = g_hash_table_lookup (timeline->priv->by_end, obj);
1152       snapped = ges_timeline_snap_position (timeline, obj, cur, position);
1153       if (snapped)
1154         position = *snapped;
1155
1156       ret &= ges_timeline_trim_object_simple (timeline, obj, NULL, GES_EDGE_END,
1157           position, FALSE);
1158
1159       /* In the case we reached max_duration we just make sure to roll
1160        * everything to the real new position */
1161       position = obj->start + obj->duration;
1162
1163       /* Send back changes to the neighbourhood */
1164       for (tmp = mv_ctx->moving_tckobjs; tmp; tmp = tmp->next) {
1165         GESTrackObject *tmptckobj = GES_TRACK_OBJECT (tmp->data);
1166
1167         tmpstart = ges_track_object_get_start (tmptckobj);
1168         tmpduration = ges_track_object_get_duration (tmptckobj);
1169         tmpend = tmpstart + tmpduration;
1170
1171         /* Check that the object should be resized at this position
1172          * even if an error accure, we keep doing our job */
1173         if (end == tmpstart) {
1174           ret &= ges_timeline_trim_object_simple (timeline, tmptckobj, NULL,
1175               GES_EDGE_START, position, FALSE);
1176         }
1177       }
1178       break;
1179     default:
1180       GST_DEBUG ("Edge type %i not handled here", edge);
1181       break;
1182   }
1183
1184   mv_ctx->ignore_needs_ctx = FALSE;
1185
1186   return ret;
1187
1188 error:
1189   mv_ctx->ignore_needs_ctx = FALSE;
1190
1191   GST_DEBUG_OBJECT (obj, "Could not roll edge %d to %" GST_TIME_FORMAT,
1192       edge, GST_TIME_ARGS (position));
1193
1194   return FALSE;
1195 }
1196
1197 gboolean
1198 timeline_move_object (GESTimeline * timeline, GESTrackObject * object,
1199     GList * layers, GESEdge edge, guint64 position)
1200 {
1201   if (!ges_timeline_set_moving_context (timeline, object, GES_EDIT_MODE_RIPPLE,
1202           edge, layers)) {
1203     GST_DEBUG_OBJECT (object, "Could not move to %" GST_TIME_FORMAT,
1204         GST_TIME_ARGS (position));
1205
1206     return FALSE;
1207   }
1208
1209   return ges_timeline_move_object_simple (timeline, object, layers, edge,
1210       position);
1211 }
1212
1213 gboolean
1214 ges_timeline_move_object_simple (GESTimeline * timeline,
1215     GESTrackObject * object, GList * layers, GESEdge edge, guint64 position)
1216 {
1217   guint64 *snap_end, *snap_st, *cur, off1, off2, end;
1218
1219   end = position + object->duration;
1220   cur = g_hash_table_lookup (timeline->priv->by_end, object);
1221   snap_end = ges_timeline_snap_position (timeline, object, cur, end);
1222   if (snap_end)
1223     off1 = end > *snap_end ? end - *snap_end : *snap_end - end;
1224   else
1225     off1 = G_MAXUINT64;
1226
1227   cur = g_hash_table_lookup (timeline->priv->by_start, object);
1228   snap_st = ges_timeline_snap_position (timeline, object, cur, position);
1229   if (snap_st)
1230     off2 = position > *snap_st ? position - *snap_st : *snap_st - position;
1231   else
1232     off2 = G_MAXUINT64;
1233
1234   if (snap_end && off1 <= off2) {
1235     position = position + *snap_end - end;
1236     ges_timeline_emit_snappig (timeline, object, snap_end);
1237   } else if (snap_st) {
1238     position = position + *snap_st - position;
1239     ges_timeline_emit_snappig (timeline, object, snap_st);
1240   }
1241
1242
1243   ges_track_object_set_start (object, position);
1244
1245   return TRUE;
1246 }
1247
1248 gboolean
1249 timeline_context_to_layer (GESTimeline * timeline, gint offset)
1250 {
1251   gboolean ret = TRUE;
1252   MoveContext *mv_ctx = &timeline->priv->movecontext;
1253
1254
1255
1256   /* Layer's priority is always positive */
1257   if (offset != 0 && (offset > 0 || mv_ctx->min_move_layer >= -offset)) {
1258     GHashTableIter iter;
1259     GESTimelineObject *key, *value;
1260     GESTimelineLayer *new_layer, *layer;
1261     guint prio;
1262
1263     mv_ctx->ignore_needs_ctx = TRUE;
1264
1265     GST_DEBUG ("Moving %d object, offset %d",
1266         g_hash_table_size (mv_ctx->moving_tlobjs), offset);
1267
1268     g_hash_table_iter_init (&iter, mv_ctx->moving_tlobjs);
1269     while (g_hash_table_iter_next (&iter, (gpointer *) & key,
1270             (gpointer *) & value)) {
1271       layer = ges_timeline_object_get_layer (value);
1272       prio = ges_timeline_layer_get_priority (layer);
1273
1274       /* We know that the layer exists as we created it */
1275       new_layer = GES_TIMELINE_LAYER (g_list_nth_data (timeline->priv->layers,
1276               prio + offset));
1277
1278       if (new_layer == NULL) {
1279         do {
1280           new_layer = ges_timeline_append_layer (timeline);
1281         } while (ges_timeline_layer_get_priority (new_layer) < prio + offset);
1282       }
1283
1284       ret &= ges_timeline_object_move_to_layer (key, new_layer);
1285
1286       g_object_unref (layer);
1287     }
1288
1289     /* Readjust min_move_layer */
1290     mv_ctx->min_move_layer = mv_ctx->min_move_layer + offset;
1291
1292     mv_ctx->ignore_needs_ctx = FALSE;
1293   }
1294
1295   return ret;
1296 }
1297
1298 static void
1299 add_object_to_track (GESTimelineObject * object, GESTrack * track)
1300 {
1301   if (!ges_timeline_object_create_track_objects (object, track)) {
1302     if ((track->type & ges_timeline_object_get_supported_formats (object))) {
1303       GST_WARNING ("Error creating track objects");
1304     }
1305   }
1306 }
1307
1308 static void
1309 add_object_to_tracks (GESTimeline * timeline, GESTimelineObject * object)
1310 {
1311   GList *tmp;
1312
1313   for (tmp = timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
1314     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
1315     GESTrack *track = tr_priv->track;
1316
1317     GST_LOG ("Trying with track %p", track);
1318     add_object_to_track (object, track);
1319   }
1320 }
1321
1322 static void
1323 do_async_start (GESTimeline * timeline)
1324 {
1325   GstMessage *message;
1326   GList *tmp;
1327
1328   timeline->priv->async_pending = TRUE;
1329
1330   /* Freeze state of tracks */
1331   for (tmp = timeline->priv->tracks; tmp; tmp = tmp->next) {
1332     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
1333     gst_element_set_locked_state ((GstElement *) tr_priv->track, TRUE);
1334   }
1335
1336   message = gst_message_new_async_start (GST_OBJECT_CAST (timeline), FALSE);
1337   parent_class->handle_message (GST_BIN_CAST (timeline), message);
1338 }
1339
1340 static void
1341 do_async_done (GESTimeline * timeline)
1342 {
1343   GstMessage *message;
1344
1345   if (timeline->priv->async_pending) {
1346     GList *tmp;
1347     /* Unfreeze state of tracks */
1348     for (tmp = timeline->priv->tracks; tmp; tmp = tmp->next) {
1349       TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
1350       gst_element_set_locked_state ((GstElement *) tr_priv->track, FALSE);
1351       gst_element_sync_state_with_parent ((GstElement *) tr_priv->track);
1352     }
1353
1354     GST_DEBUG_OBJECT (timeline, "Emitting async-done");
1355     message = gst_message_new_async_done (GST_OBJECT_CAST (timeline));
1356     parent_class->handle_message (GST_BIN_CAST (timeline), message);
1357
1358     timeline->priv->async_pending = FALSE;
1359   }
1360 }
1361
1362 /* Callbacks  */
1363 static void
1364 discoverer_finished_cb (GstDiscoverer * discoverer, GESTimeline * timeline)
1365 {
1366   do_async_done (timeline);
1367 }
1368
1369 static void
1370 discoverer_discovered_cb (GstDiscoverer * discoverer,
1371     GstDiscovererInfo * info, GError * err, GESTimeline * timeline)
1372 {
1373   GList *tmp;
1374   GList *stream_list;
1375   GESTrackType tfs_supportedformats;
1376
1377   gboolean found = FALSE;
1378   gboolean is_image = FALSE;
1379   GESTimelineFileSource *tfs = NULL;
1380   GESTimelinePrivate *priv = timeline->priv;
1381   const gchar *uri = gst_discoverer_info_get_uri (info);
1382
1383   GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
1384
1385   /* Find corresponding TimelineFileSource in the sources */
1386   for (tmp = priv->pendingobjects; tmp; tmp = tmp->next) {
1387     tfs = (GESTimelineFileSource *) tmp->data;
1388
1389     if (!g_strcmp0 (ges_timeline_filesource_get_uri (tfs), uri)) {
1390       found = TRUE;
1391       break;
1392     }
1393   }
1394
1395   if (!found) {
1396     GST_WARNING ("Discovered %s, that seems not to be in the list of sources"
1397         "to discover", uri);
1398     GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
1399     return;
1400   }
1401
1402   if (err) {
1403     GError *propagate_error = NULL;
1404
1405     priv->pendingobjects = g_list_delete_link (priv->pendingobjects, tmp);
1406     GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
1407     GST_WARNING ("Error while discovering %s: %s", uri, err->message);
1408
1409     g_propagate_error (&propagate_error, err);
1410     g_signal_emit (timeline, ges_timeline_signals[DISCOVERY_ERROR], 0, tfs,
1411         propagate_error);
1412
1413     return;
1414   }
1415
1416   /* Everything went fine... let's do our job! */
1417   GST_DEBUG ("Discovered uri %s", uri);
1418
1419   /* The timeline file source will be updated with discovered information
1420    * so it needs to not be finalized during this process */
1421   g_object_ref (tfs);
1422
1423   /* Remove object from list */
1424   priv->pendingobjects = g_list_delete_link (priv->pendingobjects, tmp);
1425   GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
1426
1427   /* FIXME : Handle errors in discovery */
1428   stream_list = gst_discoverer_info_get_stream_list (info);
1429
1430   tfs_supportedformats = ges_timeline_filesource_get_supported_formats (tfs);
1431   if (tfs_supportedformats != GES_TRACK_TYPE_UNKNOWN)
1432     goto check_image;
1433
1434   /* Update timelinefilesource properties based on info */
1435   for (tmp = stream_list; tmp; tmp = tmp->next) {
1436     GstDiscovererStreamInfo *sinf = (GstDiscovererStreamInfo *) tmp->data;
1437
1438     if (GST_IS_DISCOVERER_AUDIO_INFO (sinf)) {
1439       tfs_supportedformats |= GES_TRACK_TYPE_AUDIO;
1440       ges_timeline_filesource_set_supported_formats (tfs, tfs_supportedformats);
1441     } else if (GST_IS_DISCOVERER_VIDEO_INFO (sinf)) {
1442       tfs_supportedformats |= GES_TRACK_TYPE_VIDEO;
1443       ges_timeline_filesource_set_supported_formats (tfs, tfs_supportedformats);
1444       if (gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
1445               sinf)) {
1446         tfs_supportedformats |= GES_TRACK_TYPE_AUDIO;
1447         ges_timeline_filesource_set_supported_formats (tfs,
1448             tfs_supportedformats);
1449         is_image = TRUE;
1450       }
1451     }
1452   }
1453
1454   if (stream_list)
1455     gst_discoverer_stream_info_list_free (stream_list);
1456
1457 check_image:
1458
1459   if (is_image) {
1460     /* don't set max-duration on still images */
1461     g_object_set (tfs, "is_image", (gboolean) TRUE, NULL);
1462   }
1463
1464   /* Continue the processing on tfs */
1465   add_object_to_tracks (timeline, GES_TIMELINE_OBJECT (tfs));
1466
1467   if (!is_image) {
1468     g_object_set (tfs, "max-duration",
1469         gst_discoverer_info_get_duration (info), NULL);
1470   }
1471
1472   /* Remove the ref as the timeline file source is no longer needed here */
1473   g_object_unref (tfs);
1474 }
1475
1476 static void
1477 layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
1478     GESTimeline * timeline)
1479 {
1480   if (ges_timeline_object_is_moving_from_layer (object)) {
1481     GST_DEBUG ("TimelineObject %p is moving from a layer to another, not doing"
1482         " anything on it", object);
1483     if (!timeline->priv->movecontext.ignore_needs_ctx)
1484       timeline->priv->movecontext.needs_move_ctx = TRUE;
1485     return;
1486   }
1487
1488   GST_DEBUG ("New TimelineObject %p added to layer %p", object, layer);
1489
1490   if (GES_IS_TIMELINE_FILE_SOURCE (object)) {
1491     GESTimelineFileSource *tfs = GES_TIMELINE_FILE_SOURCE (object);
1492     GESTrackType tfs_supportedformats =
1493         ges_timeline_filesource_get_supported_formats (tfs);
1494     guint64 tfs_maxdur = ges_timeline_filesource_get_max_duration (tfs);
1495     const gchar *tfs_uri;
1496
1497     /* Send the filesource to the discoverer if:
1498      * * it doesn't have specified supported formats
1499      * * OR it doesn't have a specified max-duration
1500      * * OR it doesn't have a valid duration  */
1501
1502     if (tfs_supportedformats == GES_TRACK_TYPE_UNKNOWN ||
1503         tfs_maxdur == GST_CLOCK_TIME_NONE || object->duration == 0) {
1504       GST_LOG ("Incomplete TimelineFileSource, discovering it");
1505       tfs_uri = ges_timeline_filesource_get_uri (tfs);
1506
1507       GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
1508       timeline->priv->pendingobjects =
1509           g_list_append (timeline->priv->pendingobjects, object);
1510       GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
1511
1512       gst_discoverer_discover_uri_async (timeline->priv->discoverer, tfs_uri);
1513     } else
1514       add_object_to_tracks (timeline, object);
1515   } else {
1516     add_object_to_tracks (timeline, object);
1517   }
1518
1519   GST_DEBUG ("done");
1520 }
1521
1522 static void
1523 layer_priority_changed_cb (GESTimelineLayer * layer,
1524     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
1525 {
1526   timeline->priv->layers = g_list_sort (timeline->priv->layers, (GCompareFunc)
1527       sort_layers);
1528 }
1529
1530 static void
1531 layer_object_removed_cb (GESTimelineLayer * layer, GESTimelineObject * object,
1532     GESTimeline * timeline)
1533 {
1534   GList *trackobjects, *tmp;
1535
1536   if (ges_timeline_object_is_moving_from_layer (object)) {
1537     GST_DEBUG ("TimelineObject %p is moving from a layer to another, not doing"
1538         " anything on it", object);
1539     return;
1540   }
1541
1542   GST_DEBUG ("TimelineObject %p removed from layer %p", object, layer);
1543
1544   /* Go over the object's track objects and figure out which one belongs to
1545    * the list of tracks we control */
1546
1547   trackobjects = ges_timeline_object_get_track_objects (object);
1548   for (tmp = trackobjects; tmp; tmp = tmp->next) {
1549     GESTrackObject *trobj = (GESTrackObject *) tmp->data;
1550
1551     GST_DEBUG ("Trying to remove TrackObject %p", trobj);
1552     if (G_LIKELY (g_list_find_custom (timeline->priv->tracks,
1553                 ges_track_object_get_track (trobj),
1554                 (GCompareFunc) custom_find_track))) {
1555       GST_DEBUG ("Belongs to one of the tracks we control");
1556       ges_track_remove_object (ges_track_object_get_track (trobj), trobj);
1557
1558       ges_timeline_object_release_track_object (object, trobj);
1559     }
1560     /* removing the reference added by _get_track_objects() */
1561     g_object_unref (trobj);
1562   }
1563
1564   g_list_free (trackobjects);
1565
1566   /* if the object is a timeline file source that has not yet been discovered,
1567    * it no longer needs to be discovered so remove it from the pendingobjects
1568    * list if it belongs to this layer */
1569   if (GES_IS_TIMELINE_FILE_SOURCE (object)) {
1570     GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
1571     timeline->priv->pendingobjects =
1572         g_list_remove_all (timeline->priv->pendingobjects, object);
1573     GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
1574   }
1575
1576   GST_DEBUG ("Done");
1577 }
1578
1579 static void
1580 trackobj_start_changed_cb (GESTrackObject * child,
1581     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
1582 {
1583   sort_track_objects (timeline);
1584   sort_starts_ends_start (timeline, child);
1585   sort_starts_ends_end (timeline, child);
1586
1587   if (!timeline->priv->movecontext.ignore_needs_ctx)
1588     timeline->priv->movecontext.needs_move_ctx = TRUE;
1589 }
1590
1591 static void
1592 trackobj_duration_changed_cb (GESTrackObject * child,
1593     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
1594 {
1595   sort_starts_ends_end (timeline, child);
1596
1597   if (!timeline->priv->movecontext.ignore_needs_ctx)
1598     timeline->priv->movecontext.needs_move_ctx = TRUE;
1599 }
1600
1601 static void
1602 trackobj_inpoint_changed_cb (GESTrackObject * child,
1603     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
1604 {
1605   if (!timeline->priv->movecontext.ignore_needs_ctx)
1606     timeline->priv->movecontext.needs_move_ctx = TRUE;
1607 }
1608
1609 static void
1610 track_object_added_cb (GESTrack * track, GESTrackObject * object,
1611     GESTimeline * timeline)
1612 {
1613   /* We only work with sources */
1614   if (GES_IS_TRACK_SOURCE (object)) {
1615     start_tracking_track_obj (timeline, object);
1616
1617     g_signal_connect (GES_TRACK_OBJECT (object), "notify::start",
1618         G_CALLBACK (trackobj_start_changed_cb), timeline);
1619     g_signal_connect (GES_TRACK_OBJECT (object), "notify::duration",
1620         G_CALLBACK (trackobj_duration_changed_cb), timeline);
1621     g_signal_connect (GES_TRACK_OBJECT (object), "notify::in-point",
1622         G_CALLBACK (trackobj_inpoint_changed_cb), timeline);
1623   }
1624 }
1625
1626 static void
1627 track_object_removed_cb (GESTrack * track, GESTrackObject * object,
1628     GESTimeline * timeline)
1629 {
1630   /* We only work with sources */
1631   if (GES_IS_TRACK_SOURCE (object)) {
1632     g_signal_handlers_disconnect_by_func (object, trackobj_start_changed_cb,
1633         NULL);
1634     g_signal_handlers_disconnect_by_func (object, trackobj_duration_changed_cb,
1635         NULL);
1636     g_signal_handlers_disconnect_by_func (object, trackobj_inpoint_changed_cb,
1637         NULL);
1638
1639     /* Make sure to reinitialise the moving context next time */
1640     timeline->priv->movecontext.needs_move_ctx = TRUE;
1641     stop_tracking_for_snapping (timeline, object);
1642   }
1643 }
1644
1645 static void
1646 track_duration_cb (GstElement * track,
1647     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
1648 {
1649   guint64 duration, max_duration = 0;
1650   GList *tmp;
1651
1652   for (tmp = timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
1653     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
1654     g_object_get (tr_priv->track, "duration", &duration, NULL);
1655
1656     GST_DEBUG_OBJECT (track, "track duration : %" GST_TIME_FORMAT,
1657         GST_TIME_ARGS (duration));
1658     max_duration = MAX (duration, max_duration);
1659   }
1660
1661   if (timeline->priv->duration != max_duration) {
1662     GST_DEBUG ("track duration : %" GST_TIME_FORMAT " current : %"
1663         GST_TIME_FORMAT, GST_TIME_ARGS (max_duration),
1664         GST_TIME_ARGS (timeline->priv->duration));
1665
1666     timeline->priv->duration = max_duration;
1667
1668 #if GLIB_CHECK_VERSION(2,26,0)
1669     g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_DURATION]);
1670 #else
1671     g_object_notify (G_OBJECT (timeline), "duration");
1672 #endif
1673   }
1674 }
1675
1676 static GstStateChangeReturn
1677 ges_timeline_change_state (GstElement * element, GstStateChange transition)
1678 {
1679   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1680   GESTimeline *timeline = GES_TIMELINE (element);
1681
1682   switch (transition) {
1683     case GST_STATE_CHANGE_READY_TO_PAUSED:
1684       GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
1685       if (timeline->priv->pendingobjects) {
1686         GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
1687         do_async_start (timeline);
1688         ret = GST_STATE_CHANGE_ASYNC;
1689       } else {
1690         GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
1691       }
1692       break;
1693     default:
1694       break;
1695   }
1696
1697   {
1698     GstStateChangeReturn bret;
1699
1700     bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1701     if (G_UNLIKELY (bret == GST_STATE_CHANGE_NO_PREROLL)) {
1702       do_async_done (timeline);
1703       ret = bret;
1704     }
1705   }
1706
1707   switch (transition) {
1708     case GST_STATE_CHANGE_PAUSED_TO_READY:
1709       do_async_done (timeline);
1710       break;
1711     default:
1712       break;
1713   }
1714
1715   return ret;
1716
1717 }
1718
1719 /**
1720  * ges_timeline_new:
1721  *
1722  * Creates a new empty #GESTimeline.
1723  *
1724  * Returns: The new timeline.
1725  */
1726
1727 GESTimeline *
1728 ges_timeline_new (void)
1729 {
1730   return g_object_new (GES_TYPE_TIMELINE, NULL);
1731 }
1732
1733 /**
1734  * ges_timeline_new_from_uri:
1735  * @uri: the URI to load from
1736  *
1737  * Creates a timeline from the given URI.
1738  *
1739  * Returns: A new timeline if the uri was loaded successfully, or NULL if the
1740  * uri could not be loaded
1741  */
1742
1743 GESTimeline *
1744 ges_timeline_new_from_uri (const gchar * uri)
1745 {
1746   GESTimeline *ret;
1747
1748   /* FIXME : we should have a GError** argument so the user can know why
1749    * it wasn't able to load the uri
1750    */
1751
1752   ret = ges_timeline_new ();
1753
1754   if (!ges_timeline_load_from_uri (ret, uri)) {
1755     g_object_unref (ret);
1756     return NULL;
1757   }
1758
1759   return ret;
1760 }
1761
1762
1763 /**
1764  * ges_timeline_load_from_uri:
1765  * @timeline: an empty #GESTimeline into which to load the formatter
1766  * @uri: The URI to load from
1767  *
1768  * Loads the contents of URI into the given timeline.
1769  *
1770  * Returns: TRUE if the timeline was loaded successfully, or FALSE if the uri
1771  * could not be loaded.
1772  */
1773
1774 gboolean
1775 ges_timeline_load_from_uri (GESTimeline * timeline, const gchar * uri)
1776 {
1777   GESFormatter *p = NULL;
1778   gboolean ret = FALSE;
1779
1780   /* FIXME : we should have a GError** argument so the user can know why
1781    * it wasn't able to load the uri
1782    */
1783
1784   if (!(p = ges_formatter_new_for_uri (uri))) {
1785     GST_ERROR ("unsupported uri '%s'", uri);
1786     goto fail;
1787   }
1788
1789   if (!ges_formatter_load_from_uri (p, timeline, uri)) {
1790     GST_ERROR ("error deserializing formatter");
1791     goto fail;
1792   }
1793
1794   ret = TRUE;
1795
1796 fail:
1797   if (p)
1798     g_object_unref (p);
1799   return ret;
1800 }
1801
1802 /**
1803  * ges_timeline_save_to_uri:
1804  * @timeline: a #GESTimeline
1805  * @uri: The location to save to
1806  *
1807  * Saves the timeline to the given location
1808  *
1809  * Returns: TRUE if the timeline was successfully saved to the given location,
1810  * else FALSE.
1811  */
1812
1813 gboolean
1814 ges_timeline_save_to_uri (GESTimeline * timeline, const gchar * uri)
1815 {
1816   GESFormatter *p = NULL;
1817   gboolean ret = FALSE;
1818
1819   /* FIXME : How will the user be able to chose the format he
1820    * wishes to store to ? */
1821
1822   /* FIXME : How will we ensure a timeline loaded with a certain format
1823    * will be saved with the same one by default ? We need to make this
1824    * easy from an API perspective */
1825
1826   /* FIXME : we should have a GError** argument so the user can know why
1827    * it wasn't able to save
1828    */
1829
1830   if (!(p = ges_formatter_new_for_uri (uri))) {
1831     GST_ERROR ("unsupported uri '%s'", uri);
1832     goto fail;
1833   }
1834
1835   if (!ges_formatter_save_to_uri (p, timeline, uri)) {
1836     GST_ERROR ("error serializing formatter");
1837     goto fail;
1838   }
1839
1840   ret = TRUE;
1841
1842 fail:
1843   if (p)
1844     g_object_unref (p);
1845   return ret;
1846 }
1847
1848 /**
1849  * ges_timeline_append_layer:
1850  * @timeline: a #GESTimeline
1851  *
1852  * Append a newly creater #GESTimelineLayer to @timeline
1853  * Note that you do not own any reference to the returned layer.
1854  *
1855  * Returns: (transfer none): The newly created #GESTimelineLayer, or the last (empty)
1856  * #GESTimelineLayer of @timeline.
1857  */
1858 GESTimelineLayer *
1859 ges_timeline_append_layer (GESTimeline * timeline)
1860 {
1861   guint32 priority;
1862   GESTimelinePrivate *priv = timeline->priv;
1863   GESTimelineLayer *layer;
1864
1865   layer = ges_timeline_layer_new ();
1866   priority = g_list_length (priv->layers);
1867   ges_timeline_layer_set_priority (layer, priority);
1868
1869   ges_timeline_add_layer (timeline, layer);
1870
1871   return layer;
1872 }
1873
1874 /**
1875  * ges_timeline_add_layer:
1876  * @timeline: a #GESTimeline
1877  * @layer: the #GESTimelineLayer to add
1878  *
1879  * Add the layer to the timeline. The reference to the @layer will be stolen
1880  * by the @timeline.
1881  *
1882  * Returns: TRUE if the layer was properly added, else FALSE.
1883  */
1884 gboolean
1885 ges_timeline_add_layer (GESTimeline * timeline, GESTimelineLayer * layer)
1886 {
1887   GList *objects, *tmp;
1888   GESTimelinePrivate *priv = timeline->priv;
1889
1890   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
1891
1892   /* We can only add a layer that doesn't already belong to another timeline */
1893   if (G_UNLIKELY (layer->timeline)) {
1894     GST_WARNING ("Layer belongs to another timeline, can't add it");
1895     return FALSE;
1896   }
1897
1898   /* Add to the list of layers, make sure we don't already control it */
1899   if (G_UNLIKELY (g_list_find (priv->layers, (gconstpointer) layer))) {
1900     GST_WARNING ("Layer is already controlled by this timeline");
1901     return FALSE;
1902   }
1903
1904   g_object_ref_sink (layer);
1905   priv->layers = g_list_insert_sorted (priv->layers, layer,
1906       (GCompareFunc) sort_layers);
1907
1908   /* Inform the layer that it belongs to a new timeline */
1909   ges_timeline_layer_set_timeline (layer, timeline);
1910
1911   /* Connect to 'object-added'/'object-removed' signal from the new layer */
1912   g_signal_connect (layer, "object-added", G_CALLBACK (layer_object_added_cb),
1913       timeline);
1914   g_signal_connect (layer, "object-removed",
1915       G_CALLBACK (layer_object_removed_cb), timeline);
1916   g_signal_connect (layer, "notify::priority",
1917       G_CALLBACK (layer_priority_changed_cb), timeline);
1918
1919   GST_DEBUG ("Done adding layer, emitting 'layer-added' signal");
1920   g_signal_emit (timeline, ges_timeline_signals[LAYER_ADDED], 0, layer);
1921
1922   /* add any existing timeline objects to the timeline */
1923   objects = ges_timeline_layer_get_objects (layer);
1924   for (tmp = objects; tmp; tmp = tmp->next) {
1925     layer_object_added_cb (layer, tmp->data, timeline);
1926     g_object_unref (tmp->data);
1927     tmp->data = NULL;
1928   }
1929   g_list_free (objects);
1930
1931   return TRUE;
1932 }
1933
1934 /**
1935  * ges_timeline_remove_layer:
1936  * @timeline: a #GESTimeline
1937  * @layer: the #GESTimelineLayer to remove
1938  *
1939  * Removes the layer from the timeline. The reference that the @timeline holds on
1940  * the layer will be dropped. If you wish to use the @layer after calling this
1941  * method, you need to take a reference before calling.
1942  *
1943  * Returns: TRUE if the layer was properly removed, else FALSE.
1944  */
1945
1946 gboolean
1947 ges_timeline_remove_layer (GESTimeline * timeline, GESTimelineLayer * layer)
1948 {
1949   GList *layer_objects, *tmp;
1950   GESTimelinePrivate *priv = timeline->priv;
1951
1952   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
1953
1954   if (G_UNLIKELY (!g_list_find (priv->layers, layer))) {
1955     GST_WARNING ("Layer doesn't belong to this timeline");
1956     return FALSE;
1957   }
1958
1959   /* remove objects from any private data structures */
1960
1961   layer_objects = ges_timeline_layer_get_objects (layer);
1962   for (tmp = layer_objects; tmp; tmp = tmp->next) {
1963     layer_object_removed_cb (layer, GES_TIMELINE_OBJECT (tmp->data), timeline);
1964     g_object_unref (G_OBJECT (tmp->data));
1965     tmp->data = NULL;
1966   }
1967   g_list_free (layer_objects);
1968
1969   /* Disconnect signals */
1970   GST_DEBUG ("Disconnecting signal callbacks");
1971   g_signal_handlers_disconnect_by_func (layer, layer_object_added_cb, timeline);
1972   g_signal_handlers_disconnect_by_func (layer, layer_object_removed_cb,
1973       timeline);
1974
1975   priv->layers = g_list_remove (priv->layers, layer);
1976
1977   ges_timeline_layer_set_timeline (layer, NULL);
1978
1979   g_signal_emit (timeline, ges_timeline_signals[LAYER_REMOVED], 0, layer);
1980
1981   g_object_unref (layer);
1982
1983   return TRUE;
1984 }
1985
1986 static void
1987 pad_added_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv)
1988 {
1989   gchar *padname;
1990   gboolean no_more;
1991   GList *tmp;
1992
1993   GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
1994
1995   if (G_UNLIKELY (tr_priv->pad)) {
1996     GST_WARNING ("We are already controlling a pad for this track");
1997     return;
1998   }
1999
2000   /* Remember the pad */
2001   GST_OBJECT_LOCK (track);
2002   tr_priv->pad = pad;
2003
2004   no_more = TRUE;
2005   for (tmp = tr_priv->timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
2006     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
2007
2008     if (!tr_priv->pad) {
2009       GST_LOG ("Found track without pad %p", tr_priv->track);
2010       no_more = FALSE;
2011     }
2012   }
2013   GST_OBJECT_UNLOCK (track);
2014
2015   /* ghost it ! */
2016   GST_DEBUG ("Ghosting pad and adding it to ourself");
2017   padname = g_strdup_printf ("track_%p_src", track);
2018   tr_priv->ghostpad = gst_ghost_pad_new (padname, pad);
2019   g_free (padname);
2020   gst_pad_set_active (tr_priv->ghostpad, TRUE);
2021   gst_element_add_pad (GST_ELEMENT (tr_priv->timeline), tr_priv->ghostpad);
2022
2023   if (no_more) {
2024     GST_DEBUG ("Signaling no-more-pads");
2025     gst_element_no_more_pads (GST_ELEMENT (tr_priv->timeline));
2026   }
2027 }
2028
2029 static void
2030 pad_removed_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv)
2031 {
2032   GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
2033
2034   if (G_UNLIKELY (tr_priv->pad != pad)) {
2035     GST_WARNING ("Not the pad we're controlling");
2036     return;
2037   }
2038
2039   if (G_UNLIKELY (tr_priv->ghostpad == NULL)) {
2040     GST_WARNING ("We don't have a ghostpad for this pad !");
2041     return;
2042   }
2043
2044   GST_DEBUG ("Removing ghostpad");
2045   gst_pad_set_active (tr_priv->ghostpad, FALSE);
2046   gst_element_remove_pad (GST_ELEMENT (tr_priv->timeline), tr_priv->ghostpad);
2047   tr_priv->ghostpad = NULL;
2048   tr_priv->pad = NULL;
2049 }
2050
2051 /**
2052  * ges_timeline_add_track:
2053  * @timeline: a #GESTimeline
2054  * @track: the #GESTrack to add
2055  *
2056  * Add a track to the timeline. The reference to the track will be stolen by the
2057  * pipeline.
2058  *
2059  * Returns: TRUE if the track was properly added, else FALSE.
2060  */
2061
2062 /* FIXME: create track objects for timeline objects which have already been
2063  * added to existing layers.
2064  */
2065
2066 gboolean
2067 ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
2068 {
2069   TrackPrivate *tr_priv;
2070   GESTimelinePrivate *priv = timeline->priv;
2071   GList *tmp;
2072
2073   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
2074
2075   /* make sure we don't already control it */
2076   if (G_UNLIKELY (g_list_find_custom (priv->tracks, (gconstpointer) track,
2077               (GCompareFunc) custom_find_track))) {
2078     GST_WARNING ("Track is already controlled by this timeline");
2079     return FALSE;
2080   }
2081
2082   /* Add the track to ourself (as a GstBin)
2083    * Reference is stolen ! */
2084   if (G_UNLIKELY (!gst_bin_add (GST_BIN (timeline), GST_ELEMENT (track)))) {
2085     GST_WARNING ("Couldn't add track to ourself (GST)");
2086     return FALSE;
2087   }
2088
2089   tr_priv = g_new0 (TrackPrivate, 1);
2090   tr_priv->timeline = timeline;
2091   tr_priv->track = track;
2092
2093   /* Add the track to the list of tracks we track */
2094   priv->tracks = g_list_append (priv->tracks, tr_priv);
2095
2096   /* Listen to pad-added/-removed */
2097   g_signal_connect (track, "pad-added", (GCallback) pad_added_cb, tr_priv);
2098   g_signal_connect (track, "pad-removed", (GCallback) pad_removed_cb, tr_priv);
2099
2100   /* Inform the track that it's currently being used by ourself */
2101   ges_track_set_timeline (track, timeline);
2102
2103   GST_DEBUG ("Done adding track, emitting 'track-added' signal");
2104
2105   /* emit 'track-added' */
2106   g_signal_emit (timeline, ges_timeline_signals[TRACK_ADDED], 0, track);
2107
2108   /* ensure that each existing timeline object has the opportunity to create a
2109    * track object for this track*/
2110
2111   /* We connect to the duration change notify, so we can update
2112    * our duration accordingly */
2113   g_signal_connect (G_OBJECT (track), "notify::duration",
2114       G_CALLBACK (track_duration_cb), timeline);
2115
2116   /* We connect to the object for the timeline editing mode management */
2117   g_signal_connect (G_OBJECT (track), "track-object-added",
2118       G_CALLBACK (track_object_added_cb), timeline);
2119   g_signal_connect (G_OBJECT (track), "track-object-removed",
2120       G_CALLBACK (track_object_removed_cb), timeline);
2121
2122   for (tmp = priv->layers; tmp; tmp = tmp->next) {
2123     GList *objects, *obj;
2124     objects = ges_timeline_layer_get_objects (tmp->data);
2125
2126     for (obj = objects; obj; obj = obj->next) {
2127       add_object_to_track (obj->data, track);
2128       g_object_unref (obj->data);
2129       obj->data = NULL;
2130     }
2131     g_list_free (objects);
2132   }
2133
2134   track_duration_cb (GST_ELEMENT (track), NULL, timeline);
2135
2136   return TRUE;
2137 }
2138
2139 /**
2140  * ges_timeline_remove_track:
2141  * @timeline: a #GESTimeline
2142  * @track: the #GESTrack to remove
2143  *
2144  * Remove the @track from the @timeline. The reference stolen when adding the
2145  * @track will be removed. If you wish to use the @track after calling this
2146  * function you must ensure that you have a reference to it.
2147  *
2148  * Returns: TRUE if the @track was properly removed, else FALSE.
2149  */
2150
2151 /* FIXME: release any track objects associated with this layer. currenly this
2152  * will not happen if you remove the track before removing *all*
2153  * timelineobjects which have a track object in this track.
2154  */
2155
2156 gboolean
2157 ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track)
2158 {
2159   GList *tmp;
2160   TrackPrivate *tr_priv;
2161   GESTimelinePrivate *priv = timeline->priv;
2162
2163   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
2164
2165   if (G_UNLIKELY (!(tmp =
2166               g_list_find_custom (priv->tracks, (gconstpointer) track,
2167                   (GCompareFunc) custom_find_track)))) {
2168     GST_WARNING ("Track doesn't belong to this timeline");
2169     return FALSE;
2170   }
2171
2172   tr_priv = tmp->data;
2173   priv->tracks = g_list_remove (priv->tracks, tr_priv);
2174
2175   ges_track_set_timeline (track, NULL);
2176
2177   /* Remove ghost pad */
2178   if (tr_priv->ghostpad) {
2179     GST_DEBUG ("Removing ghostpad");
2180     gst_pad_set_active (tr_priv->ghostpad, FALSE);
2181     gst_ghost_pad_set_target ((GstGhostPad *) tr_priv->ghostpad, NULL);
2182     gst_element_remove_pad (GST_ELEMENT (timeline), tr_priv->ghostpad);
2183   }
2184
2185   /* Remove pad-added/-removed handlers */
2186   g_signal_handlers_disconnect_by_func (track, pad_added_cb, tr_priv);
2187   g_signal_handlers_disconnect_by_func (track, pad_removed_cb, tr_priv);
2188   g_signal_handlers_disconnect_by_func (track, track_duration_cb,
2189       tr_priv->track);
2190   g_signal_handlers_disconnect_by_func (track, track_object_added_cb, timeline);
2191   g_signal_handlers_disconnect_by_func (track, track_object_removed_cb,
2192       timeline);
2193
2194   /* Signal track removal to all layers/objects */
2195   g_signal_emit (timeline, ges_timeline_signals[TRACK_REMOVED], 0, track);
2196
2197   /* remove track from our bin */
2198   gst_object_ref (track);
2199   if (G_UNLIKELY (!gst_bin_remove (GST_BIN (timeline), GST_ELEMENT (track)))) {
2200     GST_WARNING ("Couldn't remove track to ourself (GST)");
2201     gst_object_unref (track);
2202     return FALSE;
2203   }
2204
2205   /* set track state to NULL */
2206
2207   gst_element_set_state (GST_ELEMENT (track), GST_STATE_NULL);
2208
2209   gst_object_unref (track);
2210
2211   g_free (tr_priv);
2212
2213   return TRUE;
2214 }
2215
2216 /**
2217  * ges_timeline_get_track_for_pad:
2218  * @timeline: The #GESTimeline
2219  * @pad: The #GstPad
2220  *
2221  * Search the #GESTrack corresponding to the given @timeline's @pad.
2222  *
2223  * Returns: (transfer none): The corresponding #GESTrack if it is found,
2224  * or %NULL if there is an error.
2225  */
2226
2227 GESTrack *
2228 ges_timeline_get_track_for_pad (GESTimeline * timeline, GstPad * pad)
2229 {
2230   GList *tmp;
2231
2232   for (tmp = timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
2233     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
2234     if (pad == tr_priv->ghostpad)
2235       return tr_priv->track;
2236   }
2237
2238   return NULL;
2239 }
2240
2241 /**
2242  * ges_timeline_get_tracks:
2243  * @timeline: a #GESTimeline
2244  *
2245  * Returns the list of #GESTrack used by the Timeline.
2246  *
2247  * Returns: (transfer full) (element-type GESTrack): A list of #GESTrack.
2248  * The caller should unref each track once he is done with them.
2249  */
2250 GList *
2251 ges_timeline_get_tracks (GESTimeline * timeline)
2252 {
2253   GList *tmp, *res = NULL;
2254
2255   for (tmp = timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
2256     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
2257     res = g_list_append (res, g_object_ref (tr_priv->track));
2258   }
2259
2260   return res;
2261 }
2262
2263 /**
2264  * ges_timeline_get_layers:
2265  * @timeline: a #GESTimeline
2266  *
2267  * Get the list of #GESTimelineLayer present in the Timeline.
2268  *
2269  * Returns: (transfer full) (element-type GESTimelineLayer): the list of
2270  * #GESTimelineLayer present in the Timeline sorted by priority.
2271  * The caller should unref each Layer once he is done with them.
2272  */
2273 GList *
2274 ges_timeline_get_layers (GESTimeline * timeline)
2275 {
2276   GList *tmp, *res = NULL;
2277
2278   for (tmp = timeline->priv->layers; tmp; tmp = g_list_next (tmp)) {
2279     res = g_list_insert_sorted (res, g_object_ref (tmp->data),
2280         (GCompareFunc) sort_layers);
2281   }
2282
2283   return res;
2284 }
2285
2286 /**
2287  * ges_timeline_is_updating:
2288  * @timeline: a #GESTimeline
2289  *
2290  * Get whether the timeline is updated for every change happening within or not.
2291  *
2292  * Returns: %TRUE if @timeline is updating on every changes, else %FALSE.
2293  */
2294 gboolean
2295 ges_timeline_is_updating (GESTimeline * timeline)
2296 {
2297   GList *tmp;
2298
2299   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
2300
2301   for (tmp = timeline->priv->tracks; tmp; tmp = tmp->next) {
2302     if (!ges_track_is_updating (((TrackPrivate *) tmp->data)->track))
2303       return FALSE;
2304   }
2305
2306   return TRUE;
2307 }
2308
2309 /**
2310  * ges_timeline_enable_update:
2311  * @timeline: a #GESTimeline
2312  * @enabled: Whether the timeline should update on every change or not.
2313  *
2314  * Control whether the timeline is updated for every change happening within.
2315  *
2316  * Users will want to use this method with %FALSE before doing lots of changes,
2317  * and then call again with %TRUE for the changes to take effect in one go.
2318  *
2319  * Returns: %TRUE if the update status could be changed, else %FALSE.
2320  */
2321 gboolean
2322 ges_timeline_enable_update (GESTimeline * timeline, gboolean enabled)
2323 {
2324   GList *tmp;
2325   gboolean res = TRUE;
2326
2327   GST_DEBUG_OBJECT (timeline, "%s updates", enabled ? "Enabling" : "Disabling");
2328
2329   for (tmp = timeline->priv->tracks; tmp; tmp = tmp->next) {
2330     if (!ges_track_enable_update (((TrackPrivate *) tmp->data)->track, enabled))
2331       res = FALSE;
2332   }
2333
2334   /* Make sure we reset the context */
2335   timeline->priv->movecontext.needs_move_ctx = TRUE;
2336
2337   return res;
2338 }