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