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