timeline: Do not ripple if resulting duration would be 0
[platform/upstream/gst-editing-services.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  *               2012 Thibault Saunier <tsaunier@gnome.org>
5  *               2012 Collabora Ltd.
6  *                 Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 /**
25  * SECTION:gestimeline
26  * @title: GESTimeline
27  * @short_description: Multimedia timeline
28  *
29  * #GESTimeline is the central object for any multimedia timeline.
30  *
31  * Contains a list of #GESLayer which users should use to arrange the
32  * various clips through time.
33  *
34  * The output type is determined by the #GESTrack that are set on
35  * the #GESTimeline.
36  *
37  * To save/load a timeline, you can use the ges_timeline_load_from_uri() and
38  * ges_timeline_save_to_uri() methods to use the default format. If you wish
39  *
40  * Note that any change you make in the timeline will not actually be taken
41  * into account until you call the #ges_timeline_commit method.
42  */
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46
47 #include "ges-internal.h"
48 #include "ges-project.h"
49 #include "ges-container.h"
50 #include "ges-timeline.h"
51 #include "ges-track.h"
52 #include "ges-layer.h"
53 #include "ges-auto-transition.h"
54 #include "ges.h"
55
56 typedef struct _MoveContext MoveContext;
57
58 static GPtrArray *select_tracks_for_object_default (GESTimeline * timeline,
59     GESClip * clip, GESTrackElement * tr_obj, gpointer user_data);
60 static inline void init_movecontext (MoveContext * mv_ctx, gboolean first_init);
61 static void ges_extractable_interface_init (GESExtractableInterface * iface);
62 static void ges_meta_container_interface_init
63     (GESMetaContainerInterface * iface);
64
65 GST_DEBUG_CATEGORY_STATIC (ges_timeline_debug);
66 #undef GST_CAT_DEFAULT
67 #define GST_CAT_DEFAULT ges_timeline_debug
68
69 /* lock to protect dynamic callbacks, like pad-added */
70 #define DYN_LOCK(timeline) (&GES_TIMELINE (timeline)->priv->dyn_mutex)
71 #define LOCK_DYN(timeline) G_STMT_START {                       \
72     GST_LOG_OBJECT (timeline, "Getting dynamic lock from %p", \
73         g_thread_self());                                       \
74     g_rec_mutex_lock (DYN_LOCK (timeline));                     \
75     GST_LOG_OBJECT (timeline, "Got Dynamic lock from %p",     \
76         g_thread_self());         \
77   } G_STMT_END
78
79 #define UNLOCK_DYN(timeline) G_STMT_START {                         \
80     GST_LOG_OBJECT (timeline, "Unlocking dynamic lock from %p", \
81         g_thread_self());                                         \
82     g_rec_mutex_unlock (DYN_LOCK (timeline));                     \
83     GST_LOG_OBJECT (timeline, "Unlocked Dynamic lock from %p",  \
84         g_thread_self());         \
85   } G_STMT_END
86
87 #define CHECK_THREAD(timeline) g_assert(timeline->priv->valid_thread == g_thread_self())
88
89 typedef struct TrackObjIters
90 {
91   GSequenceIter *iter_start;
92   GSequenceIter *iter_end;
93   GSequenceIter *iter_obj;
94   GSequenceIter *iter_by_layer;
95
96   GESLayer *layer;
97   GESTrackElement *trackelement;
98 } TrackObjIters;
99
100 static void
101 _destroy_obj_iters (TrackObjIters * iters)
102 {
103   g_slice_free (TrackObjIters, iters);
104 }
105
106 /*  The move context is used for the timeline editing modes functions in order to
107  *  + Ripple / Roll /  Slide / Move / Trim
108  *
109  * The context aims at avoiding to recalculate values/objects on each call of the
110  * editing functions.
111  */
112 struct _MoveContext
113 {
114   GESClip *clip;
115   GESEdge edge;
116   GESEditMode mode;
117
118   /* The  start of the moving context */
119   GstClockTime start;
120
121   /* Ripple and Roll Objects */
122   GList *moving_trackelements;
123
124   /* We use it as a set of Clip to move between layers */
125   GHashTable *toplevel_containers;
126   /* Min priority of the objects currently in toplevel_containers */
127   guint min_move_layer;
128   /* Max priority of the objects currently in toplevel_containers */
129   guint max_layer_prio;
130
131   /* Never trim so duration would become < 0 */
132   guint64 max_trim_pos;
133
134   /* Never trim so inpoint + duration would change */
135   guint64 min_trim_pos;
136
137   /* fields to force/avoid new context */
138   /* Set to %TRUE when the track is doing updates of track element
139    * properties so we don't end up always needing new move context */
140   gboolean ignore_needs_ctx;
141   gboolean needs_move_ctx;
142
143   /* Last snapping  properties */
144   GESTrackElement *last_snaped1;
145   GESTrackElement *last_snaped2;
146   GstClockTime *last_snap_ts;
147
148   /* Priority of the layer where we are moving current clip
149    * -1 if not moving any clip to a new layer. */
150   GESLayer *moving_to_layer;
151 };
152
153 struct _GESTimelinePrivate
154 {
155   /* The duration of the timeline */
156   gint64 duration;
157
158   /* The auto-transition of the timeline */
159   gboolean auto_transition;
160   /* Use to determine that a edit action should be rolled
161    * back because it leads to a wrong state of the element
162    * position (currently only happens if 3 clips overlap) */
163   gboolean needs_rollback;
164   gboolean rolling_back;
165
166   /* Timeline edition modes and snapping management */
167   guint64 snapping_distance;
168
169   /* FIXME: Should we offer an API over those fields ?
170    * FIXME: Should other classes than subclasses of Source also
171    * be tracked? */
172
173   /* Snapping fields */
174   GHashTable *by_start;         /* {Source: start} */
175   GHashTable *by_end;           /* {Source: end} */
176   GHashTable *by_object;        /* {timecode: Source} */
177   GHashTable *obj_iters;        /* {Source: TrackObjIters} */
178   GSequence *starts_ends;       /* Sorted list of starts/ends */
179   /* We keep 1 reference to our trackelement here */
180   GSequence *tracksources;      /* Source-s sorted by start/priorities */
181
182   GRecMutex dyn_mutex;
183   GList *priv_tracks;
184   /* FIXME: We should definitly offer an API over this,
185    * probably through a ges_layer_get_track_elements () method */
186   GHashTable *by_layer;         /* {layer: GSequence of TrackElement by start/priorities} */
187
188   /* Avoid sorting layers when we are actually resyncing them ourself */
189   gboolean resyncing_layers;
190   GList *auto_transitions;
191
192   MoveContext movecontext;
193
194   /* This variable is set to %TRUE when it makes sense to update the transitions,
195    * and %FALSE otherwize */
196   gboolean needs_transitions_update;
197
198   /* While we are creating and adding the TrackElements for a clip, we need to
199    * ignore the child-added signal */
200   GESClip *ignore_track_element_added;
201   GList *groups;
202
203   guint group_id;
204
205   GHashTable *all_elements;
206
207   /* With GST_OBJECT_LOCK */
208   guint expected_async_done;
209   /* With GST_OBJECT_LOCK */
210   guint expected_commited;
211
212   /* For ges_timeline_commit_sync */
213   GMutex commited_lock;
214   GCond commited_cond;
215
216   GThread *valid_thread;
217 };
218
219 /* private structure to contain our track-related information */
220
221 typedef struct
222 {
223   GESTimeline *timeline;
224   GESTrack *track;
225   GstPad *pad;                  /* Pad from the track */
226   GstPad *ghostpad;
227
228   gulong probe_id;
229 } TrackPrivate;
230
231 enum
232 {
233   PROP_0,
234   PROP_DURATION,
235   PROP_AUTO_TRANSITION,
236   PROP_SNAPPING_DISTANCE,
237   PROP_UPDATE,
238   PROP_LAST
239 };
240
241 static GParamSpec *properties[PROP_LAST];
242
243 enum
244 {
245   TRACK_ADDED,
246   TRACK_REMOVED,
247   LAYER_ADDED,
248   LAYER_REMOVED,
249   GROUP_ADDED,
250   GROUP_REMOVED,
251   SNAPING_STARTED,
252   SNAPING_ENDED,
253   SELECT_TRACKS_FOR_OBJECT,
254   COMMITED,
255   LAST_SIGNAL
256 };
257
258 G_DEFINE_TYPE_WITH_CODE (GESTimeline, ges_timeline, GST_TYPE_BIN,
259     G_ADD_PRIVATE (GESTimeline)
260     G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE, ges_extractable_interface_init)
261     G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER,
262         ges_meta_container_interface_init));
263
264 static GstBinClass *parent_class;
265
266 static guint ges_timeline_signals[LAST_SIGNAL] = { 0 };
267
268 static gint custom_find_track (TrackPrivate * tr_priv, GESTrack * track);
269
270 static guint nb_assets = 0;
271
272 /* GESExtractable implementation */
273 static gchar *
274 extractable_check_id (GType type, const gchar * id)
275 {
276   gchar *res;
277
278   if (id == NULL)
279     res = g_strdup_printf ("%s-%i", "project", nb_assets);
280   else
281     res = g_strdup (id);
282
283   nb_assets++;
284
285   return res;
286 }
287
288 static gchar *
289 extractable_get_id (GESExtractable * self)
290 {
291   GESAsset *asset;
292
293   if (!(asset = ges_extractable_get_asset (self)))
294     return NULL;
295
296   return g_strdup (ges_asset_get_id (asset));
297 }
298
299 static void
300 ges_extractable_interface_init (GESExtractableInterface * iface)
301 {
302   iface->asset_type = GES_TYPE_PROJECT;
303   iface->check_id = (GESExtractableCheckId) extractable_check_id;
304   iface->get_id = extractable_get_id;
305 }
306
307 static void
308 ges_meta_container_interface_init (GESMetaContainerInterface * iface)
309 {
310 }
311
312 /* GObject Standard vmethods*/
313 static void
314 ges_timeline_get_property (GObject * object, guint property_id,
315     GValue * value, GParamSpec * pspec)
316 {
317   GESTimeline *timeline = GES_TIMELINE (object);
318
319   switch (property_id) {
320     case PROP_DURATION:
321       g_value_set_uint64 (value, timeline->priv->duration);
322       break;
323     case PROP_AUTO_TRANSITION:
324       g_value_set_boolean (value, timeline->priv->auto_transition);
325       break;
326     case PROP_SNAPPING_DISTANCE:
327       g_value_set_uint64 (value, timeline->priv->snapping_distance);
328       break;
329     default:
330       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
331   }
332 }
333
334 static void
335 ges_timeline_set_property (GObject * object, guint property_id,
336     const GValue * value, GParamSpec * pspec)
337 {
338   GESTimeline *timeline = GES_TIMELINE (object);
339
340   switch (property_id) {
341     case PROP_AUTO_TRANSITION:
342       ges_timeline_set_auto_transition (timeline, g_value_get_boolean (value));
343       break;
344     case PROP_SNAPPING_DISTANCE:
345       timeline->priv->snapping_distance = g_value_get_uint64 (value);
346       break;
347     default:
348       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
349   }
350 }
351
352 static void
353 ges_timeline_dispose (GObject * object)
354 {
355   GESTimeline *tl = GES_TIMELINE (object);
356   GESTimelinePrivate *priv = tl->priv;
357   GList *tmp, *groups;
358
359   while (tl->layers) {
360     GESLayer *layer = (GESLayer *) tl->layers->data;
361     ges_timeline_remove_layer (GES_TIMELINE (object), layer);
362   }
363
364   /* FIXME: it should be possible to remove tracks before removing
365    * layers, but at the moment this creates a problem because the track
366    * objects aren't notified that their nleobjects have been destroyed.
367    */
368
369   while (tl->tracks)
370     ges_timeline_remove_track (GES_TIMELINE (object), tl->tracks->data);
371
372   groups = g_list_copy (priv->groups);
373   for (tmp = groups; tmp; tmp = tmp->next) {
374     GList *elems = ges_container_ungroup (tmp->data, FALSE);
375
376     g_list_free_full (elems, gst_object_unref);
377   }
378   g_list_free (priv->groups);
379   g_list_free (groups);
380
381   g_hash_table_unref (priv->by_start);
382   g_hash_table_unref (priv->by_end);
383   g_hash_table_unref (priv->by_object);
384   g_hash_table_unref (priv->by_layer);
385   g_hash_table_unref (priv->obj_iters);
386   g_sequence_free (priv->starts_ends);
387   g_sequence_free (priv->tracksources);
388   g_list_free (priv->movecontext.moving_trackelements);
389   g_hash_table_unref (priv->movecontext.toplevel_containers);
390
391   g_list_free_full (priv->auto_transitions, gst_object_unref);
392
393   g_hash_table_unref (priv->all_elements);
394
395   G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
396 }
397
398 static void
399 ges_timeline_finalize (GObject * object)
400 {
401   GESTimeline *tl = GES_TIMELINE (object);
402
403   g_rec_mutex_clear (&tl->priv->dyn_mutex);
404
405   G_OBJECT_CLASS (ges_timeline_parent_class)->finalize (object);
406 }
407
408
409
410 static void
411 ges_timeline_handle_message (GstBin * bin, GstMessage * message)
412 {
413   GESTimeline *timeline = GES_TIMELINE (bin);
414
415   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) {
416     GstMessage *amessage = NULL;
417     const GstStructure *mstructure = gst_message_get_structure (message);
418
419     if (gst_structure_has_name (mstructure, "NleCompositionStartUpdate")) {
420       if (g_strcmp0 (gst_structure_get_string (mstructure, "reason"), "Seek")) {
421         GST_INFO_OBJECT (timeline,
422             "A composition is starting an update because of %s"
423             " not considering async", gst_structure_get_string (mstructure,
424                 "reason"));
425
426         goto forward;
427       }
428
429       GST_OBJECT_LOCK (timeline);
430       if (timeline->priv->expected_async_done == 0) {
431         amessage = gst_message_new_async_start (GST_OBJECT_CAST (bin));
432         timeline->priv->expected_async_done = g_list_length (timeline->tracks);
433         GST_INFO_OBJECT (timeline, "Posting ASYNC_START %s",
434             gst_structure_get_string (mstructure, "reason"));
435       }
436       GST_OBJECT_UNLOCK (timeline);
437
438     } else if (gst_structure_has_name (mstructure, "NleCompositionUpdateDone")) {
439       if (g_strcmp0 (gst_structure_get_string (mstructure, "reason"), "Seek")) {
440         GST_INFO_OBJECT (timeline,
441             "A composition is done updating because of %s"
442             " not considering async", gst_structure_get_string (mstructure,
443                 "reason"));
444
445         goto forward;
446       }
447
448       GST_OBJECT_LOCK (timeline);
449       timeline->priv->expected_async_done -= 1;
450       if (timeline->priv->expected_async_done == 0) {
451         amessage = gst_message_new_async_done (GST_OBJECT_CAST (bin),
452             GST_CLOCK_TIME_NONE);
453         GST_INFO_OBJECT (timeline, "Posting ASYNC_DONE %s",
454             gst_structure_get_string (mstructure, "reason"));
455       }
456       GST_OBJECT_UNLOCK (timeline);
457     }
458
459     if (amessage)
460       gst_element_post_message (GST_ELEMENT_CAST (bin), amessage);
461   }
462
463 forward:
464   gst_element_post_message (GST_ELEMENT_CAST (bin), message);
465 }
466
467 /* we collect the first result */
468 static gboolean
469 _gst_array_accumulator (GSignalInvocationHint * ihint,
470     GValue * return_accu, const GValue * handler_return, gpointer dummy)
471 {
472   gpointer array;
473
474   array = g_value_get_boxed (handler_return);
475   if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
476     g_value_set_boxed (return_accu, array);
477
478   return FALSE;
479 }
480
481 static void
482 ges_timeline_class_init (GESTimelineClass * klass)
483 {
484   GObjectClass *object_class = G_OBJECT_CLASS (klass);
485   GstBinClass *bin_class = GST_BIN_CLASS (klass);
486
487   GST_DEBUG_CATEGORY_INIT (ges_timeline_debug, "gestimeline",
488       GST_DEBUG_FG_YELLOW, "ges timeline");
489
490   parent_class = g_type_class_peek_parent (klass);
491
492   object_class->get_property = ges_timeline_get_property;
493   object_class->set_property = ges_timeline_set_property;
494   object_class->dispose = ges_timeline_dispose;
495   object_class->finalize = ges_timeline_finalize;
496
497   bin_class->handle_message = GST_DEBUG_FUNCPTR (ges_timeline_handle_message);
498
499   /**
500    * GESTimeline:duration:
501    *
502    * Current duration (in nanoseconds) of the #GESTimeline
503    */
504   properties[PROP_DURATION] =
505       g_param_spec_uint64 ("duration", "Duration",
506       "The duration of the timeline", 0, G_MAXUINT64,
507       GST_CLOCK_TIME_NONE, G_PARAM_READABLE);
508   g_object_class_install_property (object_class, PROP_DURATION,
509       properties[PROP_DURATION]);
510
511   /**
512    * GESTimeline:auto-transition:
513    *
514    * Sets whether transitions are added automagically when clips overlap.
515    */
516   g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
517       g_param_spec_boolean ("auto-transition", "Auto-Transition",
518           "whether the transitions are added", FALSE, G_PARAM_READWRITE));
519
520   /**
521    * GESTimeline:snapping-distance:
522    *
523    * Distance (in nanoseconds) from which a moving object will snap
524    * with it neighboors. 0 means no snapping.
525    */
526   properties[PROP_SNAPPING_DISTANCE] =
527       g_param_spec_uint64 ("snapping-distance", "Snapping distance",
528       "Distance from which moving an object will snap with neighboors", 0,
529       G_MAXUINT64, 0, G_PARAM_READWRITE);
530   g_object_class_install_property (object_class, PROP_SNAPPING_DISTANCE,
531       properties[PROP_SNAPPING_DISTANCE]);
532
533   /**
534    * GESTimeline::track-added:
535    * @timeline: the #GESTimeline
536    * @track: the #GESTrack that was added to the timeline
537    *
538    * Will be emitted after the track was added to the timeline.
539    */
540   ges_timeline_signals[TRACK_ADDED] =
541       g_signal_new ("track-added", G_TYPE_FROM_CLASS (klass),
542       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_added), NULL,
543       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_TRACK);
544
545   /**
546    * GESTimeline::track-removed:
547    * @timeline: the #GESTimeline
548    * @track: the #GESTrack that was removed from the timeline
549    *
550    * Will be emitted after the track was removed from the timeline.
551    */
552   ges_timeline_signals[TRACK_REMOVED] =
553       g_signal_new ("track-removed", G_TYPE_FROM_CLASS (klass),
554       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_removed),
555       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_TRACK);
556
557   /**
558    * GESTimeline::layer-added:
559    * @timeline: the #GESTimeline
560    * @layer: the #GESLayer that was added to the timeline
561    *
562    * Will be emitted after a new layer is added to the timeline.
563    */
564   ges_timeline_signals[LAYER_ADDED] =
565       g_signal_new ("layer-added", G_TYPE_FROM_CLASS (klass),
566       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_added), NULL,
567       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_LAYER);
568
569   /**
570    * GESTimeline::layer-removed:
571    * @timeline: the #GESTimeline
572    * @layer: the #GESLayer that was removed from the timeline
573    *
574    * Will be emitted after the layer was removed from the timeline.
575    */
576   ges_timeline_signals[LAYER_REMOVED] =
577       g_signal_new ("layer-removed", G_TYPE_FROM_CLASS (klass),
578       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_removed),
579       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_LAYER);
580
581   /**
582    * GESTimeline::group-added
583    * @timeline: the #GESTimeline
584    * @group: the #GESGroup
585    *
586    * Will be emitted after a new group is added to to the timeline.
587    */
588   ges_timeline_signals[GROUP_ADDED] =
589       g_signal_new ("group-added", G_TYPE_FROM_CLASS (klass),
590       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, group_added), NULL,
591       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_GROUP);
592
593   /**
594    * GESTimeline::group-removed
595    * @timeline: the #GESTimeline
596    * @group: the #GESGroup
597    * @children: (element-type GES.Container) (transfer container): a list of #GESContainer
598    *
599    * Will be emitted after a group has been removed from the timeline.
600    */
601   ges_timeline_signals[GROUP_REMOVED] =
602       g_signal_new ("group-removed", G_TYPE_FROM_CLASS (klass),
603       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, group_removed),
604       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, GES_TYPE_GROUP,
605       G_TYPE_PTR_ARRAY);
606
607   /**
608    * GESTimeline::snapping-started:
609    * @timeline: the #GESTimeline
610    * @obj1: the first #GESTrackElement that was snapping.
611    * @obj2: the second #GESTrackElement that was snapping.
612    * @position: the position where the two objects finally snapping.
613    *
614    * Will be emitted when the 2 #GESTrackElement first snapped
615    */
616   ges_timeline_signals[SNAPING_STARTED] =
617       g_signal_new ("snapping-started", G_TYPE_FROM_CLASS (klass),
618       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
619       G_TYPE_NONE, 3, GES_TYPE_TRACK_ELEMENT, GES_TYPE_TRACK_ELEMENT,
620       G_TYPE_UINT64);
621
622   /**
623    * GESTimeline::snapping-ended:
624    * @timeline: the #GESTimeline
625    * @obj1: the first #GESTrackElement that was snapping.
626    * @obj2: the second #GESTrackElement that was snapping.
627    * @position: the position where the two objects finally snapping.
628    *
629    * Will be emitted when the 2 #GESTrackElement ended to snap
630    */
631   ges_timeline_signals[SNAPING_ENDED] =
632       g_signal_new ("snapping-ended", G_TYPE_FROM_CLASS (klass),
633       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
634       G_TYPE_NONE, 3, GES_TYPE_TRACK_ELEMENT, GES_TYPE_TRACK_ELEMENT,
635       G_TYPE_UINT64);
636
637   /**
638    * GESTimeline::select-tracks-for-object:
639    * @timeline: the #GESTimeline
640    * @clip: The #GESClip on which @track_element will land
641    * @track_element: The #GESTrackElement for which to choose the tracks it should land into
642    *
643    * Returns: (transfer full) (element-type GESTrack): a #GPtrArray of #GESTrack-s where that object should be added
644    */
645   ges_timeline_signals[SELECT_TRACKS_FOR_OBJECT] =
646       g_signal_new ("select-tracks-for-object", G_TYPE_FROM_CLASS (klass),
647       G_SIGNAL_RUN_LAST, 0, _gst_array_accumulator, NULL, NULL,
648       G_TYPE_PTR_ARRAY, 2, GES_TYPE_CLIP, GES_TYPE_TRACK_ELEMENT);
649
650   /**
651    * GESTimeline::commited:
652    * @timeline: the #GESTimeline
653    *
654    * This signal will be emitted once the changes initiated by #ges_timeline_commit
655    * have been executed in the backend. Use #ges_timeline_commit_sync if you
656    * don't need to do anything in the meantime.
657    */
658   ges_timeline_signals[COMMITED] =
659       g_signal_new ("commited", G_TYPE_FROM_CLASS (klass),
660       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
661 }
662
663 static void
664 ges_timeline_init (GESTimeline * self)
665 {
666   GESTimelinePrivate *priv = self->priv;
667
668   self->priv = ges_timeline_get_instance_private (self);
669
670   priv = self->priv;
671   self->layers = NULL;
672   self->tracks = NULL;
673   self->priv->duration = 0;
674   self->priv->auto_transition = FALSE;
675   priv->snapping_distance = 0;
676   priv->expected_async_done = 0;
677   priv->expected_commited = 0;
678
679   /* Move context initialization */
680   init_movecontext (&self->priv->movecontext, TRUE);
681   priv->movecontext.ignore_needs_ctx = FALSE;
682
683   priv->priv_tracks = NULL;
684   priv->by_start = g_hash_table_new (g_direct_hash, g_direct_equal);
685   priv->by_end = g_hash_table_new (g_direct_hash, g_direct_equal);
686   priv->by_object = g_hash_table_new (g_direct_hash, g_direct_equal);
687   priv->by_layer = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
688       (GDestroyNotify) g_sequence_free);
689   priv->obj_iters = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
690       (GDestroyNotify) _destroy_obj_iters);
691   priv->starts_ends = g_sequence_new (g_free);
692   priv->tracksources = g_sequence_new (gst_object_unref);
693
694   priv->needs_transitions_update = TRUE;
695
696   priv->all_elements =
697       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, gst_object_unref);
698
699   priv->group_id = -1;
700
701   g_signal_connect_after (self, "select-tracks-for-object",
702       G_CALLBACK (select_tracks_for_object_default), NULL);
703
704   g_rec_mutex_init (&priv->dyn_mutex);
705   g_mutex_init (&priv->commited_lock);
706   priv->valid_thread = g_thread_self ();
707 }
708
709 /* Private methods */
710
711 static inline GESContainer *
712 get_toplevel_container (gpointer element)
713 {
714   GESTimelineElement *ret =
715       ges_timeline_element_get_toplevel_parent ((GESTimelineElement
716           *) (element));
717
718   /*  We own a ref to the elements ourself */
719   gst_object_unref (ret);
720   return (GESContainer *) ret;
721 }
722
723 /* Sorting utils*/
724 static gint
725 sort_layers (gpointer a, gpointer b)
726 {
727   GESLayer *layer_a, *layer_b;
728   guint prio_a, prio_b;
729
730   layer_a = GES_LAYER (a);
731   layer_b = GES_LAYER (b);
732
733   prio_a = ges_layer_get_priority (layer_a);
734   prio_b = ges_layer_get_priority (layer_b);
735
736   if ((gint) prio_a > (guint) prio_b)
737     return 1;
738   if ((guint) prio_a < (guint) prio_b)
739     return -1;
740
741   return 0;
742 }
743
744 static void
745 _resync_layers (GESTimeline * timeline)
746 {
747   GList *tmp;
748   gint i = 0;
749
750   timeline->priv->resyncing_layers = TRUE;
751   for (tmp = timeline->layers; tmp; tmp = tmp->next) {
752     layer_set_priority (tmp->data, i, TRUE);
753     i++;
754   }
755   timeline->priv->resyncing_layers = FALSE;
756 }
757
758 static void
759 timeline_update_duration (GESTimeline * timeline)
760 {
761   GstClockTime *cduration;
762   GSequenceIter *it = g_sequence_get_end_iter (timeline->priv->starts_ends);
763
764   it = g_sequence_iter_prev (it);
765
766   if (g_sequence_iter_is_end (it)) {
767     timeline->priv->duration = 0;
768     g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_DURATION]);
769     return;
770   }
771
772   cduration = g_sequence_get (it);
773
774   if (cduration && timeline->priv->duration != *cduration) {
775     GST_DEBUG ("track duration : %" GST_TIME_FORMAT " current : %"
776         GST_TIME_FORMAT, GST_TIME_ARGS (*cduration),
777         GST_TIME_ARGS (timeline->priv->duration));
778
779     timeline->priv->duration = *cduration;
780
781     g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_DURATION]);
782   }
783 }
784
785 static gint
786 find_layer_by_prio (GESLayer * a, gpointer pprio)
787 {
788   gint prio = GPOINTER_TO_INT (pprio), lprio = ges_layer_get_priority (a);
789
790   if (lprio < prio)
791     return -1;
792   if (lprio > prio)
793     return 1;
794   return 0;
795 }
796
797 static void
798 sort_track_elements (GESTimeline * timeline, TrackObjIters * iters)
799 {
800   g_sequence_sort_changed (iters->iter_obj,
801       (GCompareDataFunc) element_start_compare, NULL);
802 }
803
804 static gint
805 compare_uint64 (guint64 * a, guint64 * b, gpointer user_data)
806 {
807   if (*a > *b)
808     return 1;
809   else if (*a == *b)
810     return 0;
811   else
812     return -1;
813 }
814
815 static gint
816 custom_find_track (TrackPrivate * tr_priv, GESTrack * track)
817 {
818   if (tr_priv->track == track)
819     return 0;
820   return -1;
821 }
822
823 static inline void
824 sort_starts_ends_end (GESTimeline * timeline, TrackObjIters * iters)
825 {
826   GESTimelineElement *obj = GES_TIMELINE_ELEMENT (iters->trackelement);
827   GESTimelinePrivate *priv = timeline->priv;
828   guint64 *end = g_hash_table_lookup (priv->by_end, obj);
829
830   *end = _START (obj) + _DURATION (obj);
831
832   g_sequence_sort_changed (iters->iter_end, (GCompareDataFunc) compare_uint64,
833       NULL);
834   timeline_update_duration (timeline);
835 }
836
837 static inline void
838 sort_starts_ends_start (GESTimeline * timeline, TrackObjIters * iters)
839 {
840   GESTimelineElement *obj = GES_TIMELINE_ELEMENT (iters->trackelement);
841   GESTimelinePrivate *priv = timeline->priv;
842   guint64 *start = g_hash_table_lookup (priv->by_start, obj);
843
844   *start = _START (obj);
845
846   g_sequence_sort_changed (iters->iter_start,
847       (GCompareDataFunc) compare_uint64, NULL);
848   timeline_update_duration (timeline);
849 }
850
851 static void
852 _destroy_auto_transition_cb (GESAutoTransition * auto_transition,
853     GESTimeline * timeline)
854 {
855   GESTimelinePrivate *priv = timeline->priv;
856   MoveContext *mv_ctx = &timeline->priv->movecontext;
857   GESClip *transition = auto_transition->transition_clip;
858   GESLayer *layer = ges_clip_get_layer (transition);
859   GESContainer *toplevel_prev =
860       get_toplevel_container (auto_transition->previous_clip), *toplevel_next =
861       get_toplevel_container (auto_transition->next_clip);
862
863   if (g_hash_table_lookup (mv_ctx->toplevel_containers, toplevel_prev) &&
864       g_hash_table_lookup (mv_ctx->toplevel_containers, toplevel_next)) {
865     GESLayer *nlayer = mv_ctx->moving_to_layer;
866
867     if (!nlayer)
868       return;
869
870     ges_clip_move_to_layer (transition, nlayer);
871
872     return;
873   }
874
875   ges_layer_remove_clip (layer, transition);
876   g_signal_handlers_disconnect_by_func (auto_transition,
877       _destroy_auto_transition_cb, timeline);
878
879
880   priv->auto_transitions =
881       g_list_remove (priv->auto_transitions, auto_transition);
882   gst_object_unref (auto_transition);
883 }
884
885 static GESAutoTransition *
886 create_transition (GESTimeline * timeline, GESTrackElement * previous,
887     GESTrackElement * next, GESClip * transition,
888     GESLayer * layer, guint64 start, guint64 duration)
889 {
890   GESAsset *asset;
891   GESAutoTransition *auto_transition;
892
893   if (transition == NULL) {
894     /* TODO make it possible to specify a Transition asset in the API */
895     asset = ges_asset_request (GES_TYPE_TRANSITION_CLIP, "crossfade", NULL);
896     transition =
897         ges_layer_add_asset (layer, asset, start, 0, duration,
898         ges_track_element_get_track_type (next));
899   } else {
900     GST_DEBUG_OBJECT (timeline,
901         "Reusing already existing transition: %" GST_PTR_FORMAT, transition);
902   }
903
904   /* We know there is only 1 TrackElement */
905   auto_transition =
906       ges_auto_transition_new (GES_CONTAINER_CHILDREN (transition)->data,
907       previous, next);
908
909   g_signal_connect (auto_transition, "destroy-me",
910       G_CALLBACK (_destroy_auto_transition_cb), timeline);
911
912   timeline->priv->auto_transitions =
913       g_list_prepend (timeline->priv->auto_transitions, auto_transition);
914
915   return auto_transition;
916 }
917
918 typedef GESAutoTransition *(*GetAutoTransitionFunc) (GESTimeline * timeline,
919     GESLayer * layer, GESTrack * track, GESTrackElement * previous,
920     GESTrackElement * next, GstClockTime transition_duration);
921
922 static GESAutoTransition *
923 _find_transition_from_auto_transitions (GESTimeline * timeline,
924     GESLayer * layer, GESTrack * track, GESTrackElement * prev,
925     GESTrackElement * next, GstClockTime transition_duration)
926 {
927   GList *tmp;
928
929   for (tmp = timeline->priv->auto_transitions; tmp; tmp = tmp->next) {
930     GESAutoTransition *auto_trans = (GESAutoTransition *) tmp->data;
931
932     /* We already have a transition linked to one of the elements we want to
933      * find a transition for */
934     if (auto_trans->previous_source == prev || auto_trans->next_source == next) {
935       if (auto_trans->previous_source != prev
936           || auto_trans->next_source != next) {
937         timeline->priv->needs_rollback = TRUE;
938         GST_INFO_OBJECT (timeline, "Failed creating auto transition, "
939             " trying to have 3 clips overlapping, rolling back");
940       }
941
942       return auto_trans;
943     }
944   }
945
946   return NULL;
947 }
948
949 static GESAutoTransition *
950 _create_auto_transition_from_transitions (GESTimeline * timeline,
951     GESLayer * layer, GESTrack * track, GESTrackElement * prev,
952     GESTrackElement * next, GstClockTime transition_duration)
953 {
954   GSequenceIter *tmp_iter;
955   GSequence *by_layer_sequence;
956
957   GESTimelinePrivate *priv = timeline->priv;
958   GESAutoTransition *auto_transition =
959       _find_transition_from_auto_transitions (timeline, layer, track, prev,
960       next, transition_duration);
961
962
963   if (auto_transition) {
964     if (timeline->priv->needs_rollback) {
965       GST_WARNING_OBJECT (timeline,
966           "Created an auto transition where we have 3 overlapping clips"
967           " removing it as this case is NOT allowed nor supported");
968       g_signal_emit_by_name (auto_transition, "destroy-me");
969       timeline->priv->needs_rollback = FALSE;
970       return NULL;
971     }
972     return auto_transition;
973   }
974
975
976   /* Try to find a transition that perfectly fits with the one that
977    * should be added at that place
978    * optimize: Use g_sequence_search instead of going over all the
979    * sequence */
980   by_layer_sequence = g_hash_table_lookup (priv->by_layer, layer);
981   for (tmp_iter = g_sequence_get_begin_iter (by_layer_sequence);
982       tmp_iter && !g_sequence_iter_is_end (tmp_iter);
983       tmp_iter = g_sequence_iter_next (tmp_iter)) {
984     GESTrackElement *maybe_transition = g_sequence_get (tmp_iter);
985
986     if (ges_track_element_get_track (maybe_transition) != track)
987       continue;
988
989     if (_START (maybe_transition) > _START (next))
990       break;
991     else if (_START (maybe_transition) != _START (next) ||
992         _DURATION (maybe_transition) != transition_duration)
993       continue;
994     else if (GES_IS_TRANSITION (maybe_transition))
995       /* Use that transition */
996       /* TODO We should make sure that the transition contains only
997        * TrackElement-s in @track and if it is not the case properly unlink the
998        * object to use it */
999       return create_transition (timeline, prev, next,
1000           GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (maybe_transition)), layer,
1001           _START (next), transition_duration);
1002   }
1003
1004   return NULL;
1005 }
1006
1007 /* Create all transition that do not exist on @layer.
1008  * @get_auto_transition is called to check if a particular transition exists.
1009  * If @track is specified, we will create the transitions only for that particular
1010  * track. */
1011 static void
1012 _create_transitions_on_layer (GESTimeline * timeline, GESLayer * layer,
1013     GESTrack * track, GESTrackElement * initiating_obj,
1014     GetAutoTransitionFunc get_auto_transition)
1015 {
1016   guint32 layer_prio;
1017   GSequenceIter *iter;
1018   GESAutoTransition *transition;
1019   GESContainer *toplevel_next;
1020   MoveContext *mv_ctx = &timeline->priv->movecontext;
1021   GESTrack *ctrack = track;
1022   GList *entered = NULL;        /* List of TrackElement for wich we walk through the
1023                                  * "start" but not the "end" in the starts_ends list */
1024   GESTimelinePrivate *priv = timeline->priv;
1025
1026   if (!layer || !ges_layer_get_auto_transition (layer))
1027     return;
1028
1029   layer_prio = ges_layer_get_priority (layer);
1030   for (iter = g_sequence_get_begin_iter (priv->starts_ends);
1031       iter && !g_sequence_iter_is_end (iter);
1032       iter = g_sequence_iter_next (iter)) {
1033     GList *tmp;
1034     guint *start_or_end = g_sequence_get (iter);
1035     GESTrackElement *next = g_hash_table_lookup (timeline->priv->by_object,
1036         start_or_end);
1037     GESContainer *toplevel =
1038         get_toplevel_container (GES_TIMELINE_ELEMENT (next));
1039
1040     /* Only object that are in that layer and track */
1041     if (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (next) != layer_prio ||
1042         (track && track != ges_track_element_get_track (next)))
1043       continue;
1044
1045     if (track == NULL)
1046       ctrack = ges_track_element_get_track (next);
1047
1048     if (start_or_end == g_hash_table_lookup (priv->by_end, next)) {
1049       if (initiating_obj == next) {
1050         /* We passed the objects that initiated the research
1051          * we are now done */
1052         g_list_free (entered);
1053         return;
1054       }
1055       entered = g_list_remove (entered, next);
1056
1057       continue;
1058     }
1059
1060     toplevel_next = get_toplevel_container (next);
1061     for (tmp = entered; tmp; tmp = tmp->next) {
1062       gint64 transition_duration;
1063       GESTrackElement *prev = tmp->data;
1064       GESContainer *toplevel_prev = get_toplevel_container (prev);
1065
1066       /* If we are not in the same track, we do not create a transition */
1067       if (ctrack != ges_track_element_get_track (prev))
1068         continue;
1069
1070       /* If elements are in the same toplevel element, we do not create a transition */
1071       if (get_toplevel_container (GES_TIMELINE_ELEMENT (prev)) == toplevel)
1072         continue;
1073
1074       /* If the element is inside a container we are moving, we do not
1075        * create a transition */
1076       if (g_hash_table_lookup (mv_ctx->toplevel_containers, toplevel_prev) &&
1077           g_hash_table_lookup (mv_ctx->toplevel_containers, toplevel_next))
1078         continue;
1079
1080       transition_duration = (_START (prev) + _DURATION (prev)) - _START (next);
1081       if (transition_duration > 0 && transition_duration < _DURATION (prev) &&
1082           transition_duration < _DURATION (next)) {
1083         transition =
1084             get_auto_transition (timeline, layer, ctrack, prev, next,
1085             transition_duration);
1086         if (!transition)
1087           create_transition (timeline, prev, next, NULL, layer,
1088               _START (next), transition_duration);
1089       }
1090     }
1091
1092     /* And add that object to the entered list so that it we can possibly set
1093      * a transition on its end edge */
1094     entered = g_list_append (entered, next);
1095   }
1096 }
1097
1098 /* @track_element must be a GESSource */
1099 void
1100 timeline_create_transitions (GESTimeline * timeline,
1101     GESTrackElement * track_element)
1102 {
1103   GESTrack *track;
1104   GList *layer_node;
1105
1106   GESTimelinePrivate *priv = timeline->priv;
1107   MoveContext *mv_ctx = &timeline->priv->movecontext;
1108
1109   if (!priv->needs_transitions_update)
1110     return;
1111
1112   if (mv_ctx->moving_trackelements &&
1113       GES_TIMELINE_ELEMENT_START (track_element) > mv_ctx->start) {
1114     GST_DEBUG_OBJECT (timeline, "Not creating transition around %"
1115         GES_TIMELINE_ELEMENT_FORMAT " as it is not the first rippled"
1116         " element", GES_TIMELINE_ELEMENT_ARGS (track_element));
1117     return;
1118   }
1119
1120   track = ges_track_element_get_track (track_element);
1121   layer_node = g_list_find_custom (timeline->layers,
1122       GINT_TO_POINTER (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (track_element)),
1123       (GCompareFunc) find_layer_by_prio);
1124
1125   _create_transitions_on_layer (timeline,
1126       layer_node ? layer_node->data : NULL, track, track_element,
1127       _find_transition_from_auto_transitions);
1128
1129   GST_DEBUG_OBJECT (timeline, "Done updating transitions");
1130 }
1131
1132 /* Timeline edition functions */
1133 static inline void
1134 init_movecontext (MoveContext * mv_ctx, gboolean first_init)
1135 {
1136   if (G_UNLIKELY (first_init))
1137     mv_ctx->toplevel_containers =
1138         g_hash_table_new (g_direct_hash, g_direct_equal);
1139
1140   mv_ctx->moving_trackelements = NULL;
1141   mv_ctx->start = G_MAXUINT64;
1142   mv_ctx->max_trim_pos = G_MAXUINT64;
1143   mv_ctx->min_move_layer = G_MAXUINT;
1144   mv_ctx->max_layer_prio = 0;
1145   mv_ctx->last_snaped1 = NULL;
1146   mv_ctx->last_snaped2 = NULL;
1147   mv_ctx->last_snap_ts = NULL;
1148   mv_ctx->moving_to_layer = NULL;
1149 }
1150
1151 static inline void
1152 clean_movecontext (MoveContext * mv_ctx)
1153 {
1154   g_list_free (mv_ctx->moving_trackelements);
1155   g_hash_table_remove_all (mv_ctx->toplevel_containers);
1156   init_movecontext (mv_ctx, FALSE);
1157 }
1158
1159 static void
1160 stop_tracking_track_element (GESTimeline * timeline,
1161     GESTrackElement * trackelement)
1162 {
1163   guint64 *start, *end;
1164   TrackObjIters *iters;
1165   GESTimelinePrivate *priv = timeline->priv;
1166
1167   iters = g_hash_table_lookup (priv->obj_iters, trackelement);
1168   if (G_LIKELY (iters->iter_by_layer)) {
1169     g_sequence_remove (iters->iter_by_layer);
1170   } else {
1171     GST_WARNING_OBJECT (timeline, "TrackElement %p was in no layer",
1172         trackelement);
1173   }
1174
1175   if (GES_IS_SOURCE (trackelement)) {
1176     start = g_hash_table_lookup (priv->by_start, trackelement);
1177     end = g_hash_table_lookup (priv->by_end, trackelement);
1178
1179     g_hash_table_remove (priv->by_start, trackelement);
1180     g_hash_table_remove (priv->by_end, trackelement);
1181     g_hash_table_remove (priv->by_object, end);
1182     g_hash_table_remove (priv->by_object, start);
1183     g_sequence_remove (iters->iter_start);
1184     g_sequence_remove (iters->iter_end);
1185     g_sequence_remove (iters->iter_obj);
1186     timeline_update_duration (timeline);
1187   }
1188   g_hash_table_remove (priv->obj_iters, trackelement);
1189 }
1190
1191 static void
1192 start_tracking_track_element (GESTimeline * timeline,
1193     GESTrackElement * trackelement)
1194 {
1195   guint64 *pstart, *pend;
1196   GSequence *by_layer_sequence;
1197   TrackObjIters *iters;
1198   GESTimelinePrivate *priv = timeline->priv;
1199
1200   guint layer_prio = GES_TIMELINE_ELEMENT_LAYER_PRIORITY (trackelement);
1201   GList *layer_node = g_list_find_custom (timeline->layers,
1202       GINT_TO_POINTER (layer_prio), (GCompareFunc) find_layer_by_prio);
1203   GESLayer *layer = layer_node ? layer_node->data : NULL;
1204
1205   iters = g_slice_new0 (TrackObjIters);
1206
1207   /* We add all TrackElement to obj_iters as we always follow them
1208    * in the by_layer Sequences */
1209   g_hash_table_insert (priv->obj_iters, trackelement, iters);
1210
1211   /* Track all objects by layer */
1212   if (G_UNLIKELY (layer == NULL)) {
1213     /* We handle the case where we have TrackElement that are in no layer by not
1214      * tracking them
1215      *
1216      * FIXME: Check if we should rather try to track them in some sort of
1217      * dummy layer, or even refuse TrackElements to be added in Tracks if
1218      * they land in no layer the timeline controls.
1219      */
1220     GST_ERROR_OBJECT (timeline, "Adding a TrackElement that lands in no layer "
1221         "we are controlling");
1222   } else {
1223     by_layer_sequence = g_hash_table_lookup (priv->by_layer, layer);
1224     iters->iter_by_layer =
1225         g_sequence_insert_sorted (by_layer_sequence, trackelement,
1226         (GCompareDataFunc) element_start_compare, NULL);
1227     iters->layer = layer;
1228   }
1229
1230   if (GES_IS_SOURCE (trackelement)) {
1231     /* Track only sources for timeline edition and snapping */
1232     pstart = g_malloc (sizeof (guint64));
1233     pend = g_malloc (sizeof (guint64));
1234     *pstart = _START (trackelement);
1235     *pend = *pstart + _DURATION (trackelement);
1236
1237     iters->iter_start = g_sequence_insert_sorted (priv->starts_ends, pstart,
1238         (GCompareDataFunc) compare_uint64, NULL);
1239     iters->iter_end = g_sequence_insert_sorted (priv->starts_ends, pend,
1240         (GCompareDataFunc) compare_uint64, NULL);
1241     iters->iter_obj =
1242         g_sequence_insert_sorted (priv->tracksources,
1243         gst_object_ref (trackelement), (GCompareDataFunc) element_start_compare,
1244         NULL);
1245     iters->trackelement = trackelement;
1246
1247     g_hash_table_insert (priv->by_start, trackelement, pstart);
1248     g_hash_table_insert (priv->by_object, pstart, trackelement);
1249     g_hash_table_insert (priv->by_end, trackelement, pend);
1250     g_hash_table_insert (priv->by_object, pend, trackelement);
1251
1252     timeline->priv->movecontext.needs_move_ctx = TRUE;
1253
1254     timeline_update_duration (timeline);
1255     timeline_create_transitions (timeline, trackelement);
1256   }
1257 }
1258
1259 static inline void
1260 ges_timeline_emit_snappig (GESTimeline * timeline, GESTrackElement * obj1,
1261     guint64 * timecode)
1262 {
1263   GESTrackElement *obj2;
1264   MoveContext *mv_ctx = &timeline->priv->movecontext;
1265   GstClockTime snap_time = timecode ? *timecode : 0;
1266   GstClockTime last_snap_ts = mv_ctx->last_snap_ts ?
1267       *mv_ctx->last_snap_ts : GST_CLOCK_TIME_NONE;
1268
1269   GST_DEBUG_OBJECT (timeline, "Distance: %" GST_TIME_FORMAT " snapping at %"
1270       GST_TIME_FORMAT, GST_TIME_ARGS (timeline->priv->snapping_distance),
1271       GST_TIME_ARGS (snap_time));
1272
1273   if (timecode == NULL) {
1274     if (mv_ctx->last_snaped1 != NULL && mv_ctx->last_snaped2 != NULL) {
1275       g_signal_emit (timeline, ges_timeline_signals[SNAPING_ENDED], 0,
1276           mv_ctx->last_snaped1, mv_ctx->last_snaped2, last_snap_ts);
1277
1278       /* We then need to recalculate the moving context */
1279       timeline->priv->movecontext.needs_move_ctx = TRUE;
1280     }
1281
1282     return;
1283   }
1284
1285   obj2 = g_hash_table_lookup (timeline->priv->by_object, timecode);
1286
1287   if (last_snap_ts != *timecode) {
1288     g_signal_emit (timeline, ges_timeline_signals[SNAPING_ENDED], 0,
1289         mv_ctx->last_snaped1, mv_ctx->last_snaped2, (last_snap_ts));
1290
1291     /* We want the snap start signal to be emited anyway */
1292     mv_ctx->last_snap_ts = NULL;
1293   }
1294
1295   if (mv_ctx->last_snap_ts == NULL) {
1296
1297     mv_ctx->last_snaped1 = obj1;
1298     mv_ctx->last_snaped2 = obj2;
1299     mv_ctx->last_snap_ts = timecode;
1300
1301     g_signal_emit (timeline, ges_timeline_signals[SNAPING_STARTED], 0,
1302         obj1, obj2, *timecode);
1303
1304   }
1305 }
1306
1307 static GstClockTime *
1308 ges_timeline_snap_position (GESTimeline * timeline,
1309     GESTrackElement * trackelement, GstClockTime * current,
1310     GstClockTime timecode, gboolean emit)
1311 {
1312   GESTimelinePrivate *priv = timeline->priv;
1313   GSequenceIter *iter, *end_iter;
1314   GESContainer *container = get_toplevel_container (trackelement);
1315   GstClockTime *ret = NULL;
1316   GstClockTime smallest_offset = G_MAXUINT64;
1317   GstClockTime tmp_pos;
1318
1319   tmp_pos = timecode - priv->snapping_distance;
1320   /* Rippling, not snapping with previous elements */
1321   if (priv->movecontext.moving_trackelements)
1322     tmp_pos = timecode;
1323   iter = g_sequence_search (priv->starts_ends, &tmp_pos,
1324       (GCompareDataFunc) compare_uint64, NULL);
1325
1326   tmp_pos = timecode + priv->snapping_distance;
1327   end_iter = g_sequence_search (priv->starts_ends, &tmp_pos,
1328       (GCompareDataFunc) compare_uint64, NULL);
1329
1330   for (; iter != end_iter && !g_sequence_iter_is_end (iter);
1331       iter = g_sequence_iter_next (iter)) {
1332     GstClockTime *iter_tc = g_sequence_get (iter);
1333     GESTrackElement *tmp_trackelement =
1334         g_hash_table_lookup (priv->by_object, iter_tc);
1335     GESContainer *tmp_container = get_toplevel_container (tmp_trackelement);
1336     GstClockTimeDiff diff;
1337
1338     if (tmp_container == container)
1339       continue;
1340
1341     if (g_hash_table_lookup (priv->movecontext.toplevel_containers,
1342             tmp_container))
1343       continue;
1344
1345     if (timecode > *iter_tc)
1346       diff = timecode - *iter_tc;
1347     else
1348       diff = *iter_tc - timecode;
1349
1350     if (diff > smallest_offset)
1351       break;
1352
1353     smallest_offset = diff;
1354     ret = iter_tc;
1355   }
1356
1357   /* We emit the snapping signal only if we snapped with a different value
1358    * than the current one */
1359   if (emit) {
1360     GstClockTime snap_time = ret ? *ret : GST_CLOCK_TIME_NONE;
1361
1362     if (!timeline->priv->needs_rollback)
1363       ges_timeline_emit_snappig (timeline, trackelement, ret);
1364     else
1365       ges_timeline_emit_snappig (timeline, trackelement, NULL);
1366
1367     GST_DEBUG_OBJECT (timeline, "Snaping at %" GST_TIME_FORMAT,
1368         GST_TIME_ARGS (snap_time));
1369   }
1370
1371   return ret;
1372 }
1373
1374 static inline GESContainer *
1375 add_toplevel_container (MoveContext * mv_ctx, GESTrackElement * trackelement)
1376 {
1377   guint layer_prio;
1378   GESContainer *toplevel = get_toplevel_container (trackelement);
1379
1380   /* Avoid recalculating */
1381   if (!g_hash_table_lookup (mv_ctx->toplevel_containers, toplevel)) {
1382     if (GES_IS_CLIP (toplevel)) {
1383
1384       layer_prio = ges_clip_get_layer_priority (GES_CLIP (toplevel));
1385       if (layer_prio == (guint32) - 1) {
1386         GST_WARNING_OBJECT (toplevel, "Not in any layer, can not move"
1387             " between layers");
1388
1389         return toplevel;
1390       }
1391       mv_ctx->min_move_layer = MIN (mv_ctx->min_move_layer, layer_prio);
1392       mv_ctx->max_layer_prio = MAX (mv_ctx->max_layer_prio, layer_prio);
1393     } else if GES_IS_GROUP
1394       (toplevel) {
1395       mv_ctx->min_move_layer = MIN (mv_ctx->min_move_layer,
1396           _PRIORITY (toplevel));
1397       mv_ctx->max_layer_prio = MAX (mv_ctx->max_layer_prio,
1398           _PRIORITY (toplevel) + GES_CONTAINER_HEIGHT (toplevel));
1399     } else
1400       g_assert_not_reached ();
1401
1402     mv_ctx->start = MIN (mv_ctx->start, GES_TIMELINE_ELEMENT_START (toplevel));
1403     g_hash_table_insert (mv_ctx->toplevel_containers, toplevel, toplevel);
1404
1405   }
1406
1407   return toplevel;
1408 }
1409
1410 static gboolean
1411 ges_move_context_set_objects (GESTimeline * timeline, GESTrackElement * obj,
1412     GESEdge edge)
1413 {
1414   TrackObjIters *iters;
1415   GESTrackElement *tmptrackelement;
1416   guint64 start, tmpend, moving_point = _START (obj);
1417   GSequenceIter *iter, *trackelement_iter, *tmpiter;
1418
1419   MoveContext *mv_ctx = &timeline->priv->movecontext;
1420   iters = g_hash_table_lookup (timeline->priv->obj_iters, obj);
1421   trackelement_iter = iters->iter_obj;
1422   switch (edge) {
1423     case GES_EDGE_START:
1424       /* set it properly in the context of "trimming" */
1425       mv_ctx->max_trim_pos = 0;
1426       mv_ctx->min_trim_pos = 0;
1427       start = _START (obj);
1428
1429       if (g_sequence_iter_is_begin (trackelement_iter))
1430         break;
1431
1432       /* Look for the objects */
1433       for (iter = g_sequence_iter_prev (trackelement_iter);
1434           iter && !g_sequence_iter_is_end (iter);
1435           iter = g_sequence_iter_prev (iter)) {
1436
1437         tmptrackelement = GES_TRACK_ELEMENT (g_sequence_get (iter));
1438         tmpend = _START (tmptrackelement) + _DURATION (tmptrackelement);
1439
1440         if (tmpend <= start && GES_TIMELINE_ELEMENT_PARENT (tmptrackelement)
1441             != GES_TIMELINE_ELEMENT_PARENT (obj)) {
1442           mv_ctx->max_trim_pos =
1443               MAX (mv_ctx->max_trim_pos, _START (tmptrackelement));
1444           mv_ctx->min_trim_pos = MAX (mv_ctx->min_trim_pos,
1445               _START (tmptrackelement) - _INPOINT (tmptrackelement));
1446           mv_ctx->moving_trackelements =
1447               g_list_prepend (mv_ctx->moving_trackelements, tmptrackelement);
1448         }
1449
1450
1451         if (g_sequence_iter_is_begin (iter))
1452           break;
1453       }
1454       break;
1455
1456     case GES_EDGE_END:
1457       moving_point = _START (obj) + _DURATION (obj);
1458       /* fall-through */
1459     case GES_EDGE_NONE:        /* In this case only works for ripple */
1460       mv_ctx->max_trim_pos = G_MAXUINT64;
1461
1462       iter = trackelement_iter;
1463       tmpiter = g_sequence_iter_prev (iter);
1464
1465       /* Make sure to get the first TimelineElement starting at
1466        * @moving_point */
1467       while (tmpiter && !g_sequence_iter_is_end (tmpiter)) {
1468         tmptrackelement = GES_TRACK_ELEMENT (g_sequence_get (iter));
1469
1470         if (GES_TIMELINE_ELEMENT_START (tmptrackelement) != moving_point)
1471           break;
1472
1473         iter = tmpiter;
1474         tmpiter = g_sequence_iter_prev (tmpiter);
1475
1476         if (g_sequence_iter_is_begin (tmpiter))
1477           break;
1478       }
1479
1480       /* Look for folowing objects */
1481       for (; iter && !g_sequence_iter_is_end (iter);
1482           iter = g_sequence_iter_next (iter)) {
1483         tmptrackelement = GES_TRACK_ELEMENT (g_sequence_get (iter));
1484
1485         if (_START (tmptrackelement) >= moving_point &&
1486             GES_TIMELINE_ELEMENT_PARENT (tmptrackelement) !=
1487             GES_TIMELINE_ELEMENT_PARENT (obj)) {
1488           tmpend = _START (tmptrackelement) + _DURATION (tmptrackelement);
1489           mv_ctx->max_trim_pos = MIN (mv_ctx->max_trim_pos, tmpend);
1490           mv_ctx->moving_trackelements =
1491               g_list_prepend (mv_ctx->moving_trackelements, tmptrackelement);
1492         }
1493       }
1494       break;
1495     default:
1496       GST_DEBUG ("Edge type %d no supported", edge);
1497       return FALSE;
1498   }
1499
1500   return TRUE;
1501 }
1502
1503 static gboolean
1504 ges_timeline_set_moving_context (GESTimeline * timeline, GESTrackElement * obj,
1505     GESEditMode mode, GESEdge edge, GList * layers)
1506 {
1507   /* A TrackElement that could initiate movement for other object */
1508   GESTrackElement *editor_trackelement = NULL;
1509   MoveContext *mv_ctx = &timeline->priv->movecontext;
1510   GESClip *clip = GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (obj));
1511
1512   /* Still in the same mv_ctx */
1513   if ((mv_ctx->clip == clip && mv_ctx->mode == mode &&
1514           mv_ctx->edge == edge && !mv_ctx->needs_move_ctx)) {
1515
1516     GST_DEBUG ("Keeping the same moving mv_ctx");
1517     return TRUE;
1518   }
1519
1520
1521   GST_DEBUG_OBJECT (clip,
1522       "Changing context:\nold: obj: %p, mode: %d, edge: %d \n"
1523       "new: obj: %p, mode: %d, edge: %d ! Has changed %i", mv_ctx->clip,
1524       mv_ctx->mode, mv_ctx->edge, clip, mode, edge, mv_ctx->needs_move_ctx);
1525
1526   /* Make sure snapping context is reset when changing the moving context */
1527   ges_timeline_emit_snappig (timeline, NULL, NULL);
1528   clean_movecontext (mv_ctx);
1529   mv_ctx->edge = edge;
1530   mv_ctx->mode = mode;
1531   mv_ctx->clip = clip;
1532   mv_ctx->needs_move_ctx = FALSE;
1533
1534   /* We try to find a Source inside the Clip so we can set the
1535    * moving context Else we just move the selected one only */
1536   if (GES_IS_SOURCE (obj) == FALSE) {
1537     GList *tmp;
1538
1539     for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
1540       if (GES_IS_SOURCE (tmp->data)) {
1541         editor_trackelement = tmp->data;
1542         break;
1543       }
1544     }
1545   } else {
1546     editor_trackelement = obj;
1547   }
1548
1549   if (editor_trackelement) {
1550     switch (mode) {
1551       case GES_EDIT_MODE_RIPPLE:
1552       case GES_EDIT_MODE_ROLL:
1553         if (!(ges_move_context_set_objects (timeline, editor_trackelement,
1554                     edge)))
1555           return FALSE;
1556       default:
1557         break;
1558     }
1559     add_toplevel_container (&timeline->priv->movecontext, editor_trackelement);
1560   } else {
1561     /* We add the main object to the toplevel_containers set */
1562     add_toplevel_container (&timeline->priv->movecontext, obj);
1563   }
1564
1565
1566   return TRUE;
1567 }
1568
1569 static gboolean
1570 _trim_transition (GESTimeline * timeline, GESLayer * layer,
1571     GESTrackElement * element, GESEdge edge, GstClockTime position)
1572 {
1573
1574   GList *tmp;
1575
1576   if (!ges_layer_get_auto_transition (layer))
1577     goto fail;
1578
1579   gst_object_unref (layer);
1580   for (tmp = timeline->priv->auto_transitions; tmp; tmp = tmp->next) {
1581     GESAutoTransition *auto_transition = tmp->data;
1582
1583     if (auto_transition->transition == GES_TRACK_ELEMENT (element)) {
1584       /* Trimming an auto transition mean trimming its neighboors */
1585       if (!auto_transition->positioning) {
1586         if (edge == GES_EDGE_END) {
1587           ges_container_edit (GES_CONTAINER (auto_transition->previous_clip),
1588               NULL, -1, GES_EDIT_MODE_TRIM, GES_EDGE_END, position);
1589         } else {
1590           ges_container_edit (GES_CONTAINER (auto_transition->next_clip),
1591               NULL, -1, GES_EDIT_MODE_TRIM, GES_EDGE_START, position);
1592         }
1593
1594         return TRUE;
1595       }
1596
1597       return FALSE;
1598     }
1599   }
1600
1601   return FALSE;
1602
1603 fail:
1604   gst_object_unref (layer);
1605   return FALSE;
1606 }
1607
1608 gboolean
1609 ges_timeline_trim_object_simple (GESTimeline * timeline,
1610     GESTimelineElement * element, GList * layers, GESEdge edge,
1611     guint64 position, gboolean snapping)
1612 {
1613   guint64 start, inpoint, duration, max_duration, *snapped, *cur;
1614   gboolean ret = TRUE;
1615   gint64 real_dur;
1616   GESTrackElement *track_element;
1617
1618   if (GES_IS_TRANSITION (element)) {
1619     return _trim_transition (timeline,
1620         ges_clip_get_layer (GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (element))),
1621         GES_TRACK_ELEMENT (element), edge, position);
1622   } else if (GES_IS_SOURCE (element) == FALSE) {
1623     return FALSE;
1624   }
1625
1626   track_element = GES_TRACK_ELEMENT (element);
1627   GST_DEBUG_OBJECT (track_element, "Trimming to %" GST_TIME_FORMAT
1628       " %s snaping, edge %i", GST_TIME_ARGS (position),
1629       snapping ? "Is" : "Not", edge);
1630
1631   start = _START (track_element);
1632   g_object_get (track_element, "max-duration", &max_duration, NULL);
1633
1634   switch (edge) {
1635     case GES_EDGE_START:
1636     {
1637       GESTimelineElement *toplevel;
1638       GESChildrenControlMode old_mode;
1639       gboolean use_inpoint;
1640       toplevel = ges_timeline_element_get_toplevel_parent (element);
1641
1642       if (position < _START (toplevel) && _START (toplevel) < _START (element)) {
1643         GST_DEBUG_OBJECT (toplevel, "Not trimming %p as not at begining "
1644             "of the container", element);
1645
1646         gst_object_unref (toplevel);
1647         return FALSE;
1648       }
1649
1650       old_mode = GES_CONTAINER (toplevel)->children_control_mode;
1651       if (GES_IS_GROUP (toplevel) && old_mode == GES_CHILDREN_UPDATE) {
1652         GST_DEBUG_OBJECT (toplevel, "Setting children udpate mode to"
1653             " UPDDATE_ALL_VALUES so we can trim without moving the contained");
1654         /* The container will update its values itself according to new
1655          * values of the children */
1656         GES_CONTAINER (toplevel)->children_control_mode =
1657             GES_CHILDREN_UPDATE_ALL_VALUES;
1658       }
1659
1660       inpoint = _INPOINT (track_element);
1661       duration = _DURATION (track_element);
1662
1663       if (snapping) {
1664         cur = g_hash_table_lookup (timeline->priv->by_start, track_element);
1665
1666         snapped = ges_timeline_snap_position (timeline, track_element, cur,
1667             position, TRUE);
1668         if (snapped)
1669           position = *snapped;
1670       }
1671
1672       /* Calculate new values */
1673       position = MIN (position, start + duration);
1674
1675       use_inpoint =
1676           GES_TIMELINE_ELEMENT_GET_CLASS (track_element)->set_inpoint ? TRUE :
1677           FALSE;
1678
1679       if (use_inpoint && inpoint + position < start) {
1680         GST_ERROR_OBJECT (timeline, "Track element %s inpoint %" GST_TIME_FORMAT
1681             " would be negative,"
1682             " not trimming", GES_TIMELINE_ELEMENT_NAME (track_element),
1683             GST_TIME_ARGS (inpoint));
1684         gst_object_unref (toplevel);
1685         return FALSE;
1686       }
1687
1688       inpoint = inpoint + position - start;
1689       real_dur = _END (element) - position;
1690       if (use_inpoint)
1691         duration = CLAMP (real_dur, 0, max_duration > inpoint ?
1692             max_duration - inpoint : G_MAXUINT64);
1693       else
1694         duration = real_dur;
1695
1696
1697       /* If we already are at max duration or duration == 0 do no useless work */
1698       if ((duration == _DURATION (track_element) &&
1699               _DURATION (track_element) == _MAXDURATION (track_element)) ||
1700           (duration == 0 && _DURATION (element) == 0)) {
1701         GST_DEBUG_OBJECT (track_element,
1702             "Duration already == max_duration, no triming");
1703         gst_object_unref (toplevel);
1704         return FALSE;
1705       }
1706
1707       timeline->priv->needs_transitions_update = FALSE;
1708       _set_start0 (GES_TIMELINE_ELEMENT (track_element), position);
1709       _set_inpoint0 (GES_TIMELINE_ELEMENT (track_element), inpoint);
1710       timeline->priv->needs_transitions_update = TRUE;
1711
1712       _set_duration0 (GES_TIMELINE_ELEMENT (track_element), duration);
1713       if (GES_IS_GROUP (toplevel))
1714         GES_CONTAINER (toplevel)->children_control_mode = old_mode;
1715
1716       gst_object_unref (toplevel);
1717       break;
1718     }
1719     case GES_EDGE_END:
1720     {
1721       cur = g_hash_table_lookup (timeline->priv->by_end, track_element);
1722       snapped = ges_timeline_snap_position (timeline, track_element, cur,
1723           position, TRUE);
1724       if (snapped)
1725         position = *snapped;
1726
1727       /* Calculate new values */
1728       real_dur = position - start;
1729       duration = MAX (0, real_dur);
1730       duration = MIN (duration, max_duration - _INPOINT (track_element));
1731
1732       if (duration == 0) {
1733         GST_INFO_OBJECT (timeline, "Duration would be 0, not rippling");
1734         return FALSE;
1735       }
1736
1737       /* Not moving, avoid overhead */
1738       if (duration == _DURATION (track_element)) {
1739         GST_DEBUG_OBJECT (track_element, "No change in duration");
1740         return FALSE;
1741       }
1742
1743       _set_duration0 (GES_TIMELINE_ELEMENT (track_element), duration);
1744       break;
1745     }
1746     default:
1747       GST_WARNING ("Can not trim with %i GESEdge", edge);
1748       return FALSE;
1749   }
1750
1751   return ret;
1752 }
1753
1754 gboolean
1755 timeline_ripple_object (GESTimeline * timeline, GESTrackElement * obj,
1756     GList * layers, GESEdge edge, guint64 position)
1757 {
1758   GList *tmp, *moved_clips = NULL;
1759   GESTrackElement *trackelement;
1760   GESContainer *container;
1761   guint64 duration, new_start, *snapped, *cur;
1762   gint64 offset;
1763
1764   MoveContext *mv_ctx = &timeline->priv->movecontext;
1765
1766   mv_ctx->ignore_needs_ctx = TRUE;
1767   timeline->priv->needs_rollback = FALSE;
1768   if (!ges_timeline_set_moving_context (timeline, obj, GES_EDIT_MODE_RIPPLE,
1769           edge, layers))
1770     goto error;
1771
1772   switch (edge) {
1773     case GES_EDGE_NONE:
1774       GST_DEBUG ("Simply rippling");
1775
1776       /* We should be smart here to avoid recalculate transitions when possible */
1777       cur = g_hash_table_lookup (timeline->priv->by_end, obj);
1778       snapped = ges_timeline_snap_position (timeline, obj, cur, position, TRUE);
1779       if (snapped)
1780         position = *snapped;
1781
1782       offset = position - _START (obj);
1783
1784       for (tmp = mv_ctx->moving_trackelements; tmp; tmp = tmp->next) {
1785         trackelement = GES_TRACK_ELEMENT (tmp->data);
1786         new_start = _START (trackelement) + offset;
1787
1788         container = add_toplevel_container (mv_ctx, trackelement);
1789         /* Make sure not to move 2 times the same Clip */
1790         if (g_list_find (moved_clips, container) == NULL) {
1791           _set_start0 (GES_TIMELINE_ELEMENT (trackelement), new_start);
1792           moved_clips = g_list_prepend (moved_clips, container);
1793         }
1794
1795       }
1796       g_list_free (moved_clips);
1797       _set_start0 (GES_TIMELINE_ELEMENT (obj), position);
1798
1799       moved_clips = NULL;
1800       if (timeline->priv->needs_rollback && !timeline->priv->rolling_back) {
1801         timeline->priv->rolling_back = TRUE;
1802         for (tmp = mv_ctx->moving_trackelements; tmp; tmp = tmp->next) {
1803           trackelement = GES_TRACK_ELEMENT (tmp->data);
1804           new_start = _START (trackelement) - offset;
1805
1806           container = add_toplevel_container (mv_ctx, trackelement);
1807           /* Make sure not to move 2 times the same Clip */
1808           if (g_list_find (moved_clips, container) == NULL) {
1809             _set_start0 (GES_TIMELINE_ELEMENT (trackelement), new_start);
1810             moved_clips = g_list_prepend (moved_clips, container);
1811           }
1812
1813         }
1814         g_list_free (moved_clips);
1815         moved_clips = NULL;
1816         _set_start0 (GES_TIMELINE_ELEMENT (obj), position - offset);
1817
1818         ges_timeline_emit_snappig (timeline, obj, NULL);
1819         mv_ctx->needs_move_ctx = TRUE;
1820         timeline->priv->rolling_back = FALSE;
1821
1822         goto error;
1823       }
1824
1825       break;
1826     case GES_EDGE_END:
1827       timeline->priv->needs_transitions_update = FALSE;
1828       GST_DEBUG ("Rippling end");
1829
1830       cur = g_hash_table_lookup (timeline->priv->by_end, obj);
1831       snapped = ges_timeline_snap_position (timeline, obj, cur, position, TRUE);
1832       if (snapped)
1833         position = *snapped;
1834
1835       duration = _DURATION (obj);
1836
1837       if (!ges_timeline_trim_object_simple (timeline,
1838               GES_TIMELINE_ELEMENT (obj), NULL, GES_EDGE_END, position,
1839               FALSE)) {
1840         return FALSE;
1841       }
1842
1843       offset = _DURATION (obj) - duration;
1844       for (tmp = mv_ctx->moving_trackelements; tmp; tmp = tmp->next) {
1845         trackelement = GES_TRACK_ELEMENT (tmp->data);
1846         new_start = _START (trackelement) + offset;
1847
1848         container = add_toplevel_container (mv_ctx, trackelement);
1849         if (GES_IS_GROUP (container))
1850           container->children_control_mode = GES_CHILDREN_UPDATE_OFFSETS;
1851         /* Make sure not to move 2 times the same Clip */
1852         if (g_list_find (moved_clips, container) == NULL) {
1853           _set_start0 (GES_TIMELINE_ELEMENT (trackelement), new_start);
1854           moved_clips = g_list_prepend (moved_clips, container);
1855         }
1856         if (GES_IS_GROUP (container))
1857           container->children_control_mode = GES_CHILDREN_UPDATE;
1858       }
1859
1860       g_list_free (moved_clips);
1861       timeline->priv->needs_transitions_update = TRUE;
1862       GST_DEBUG ("Done Rippling end");
1863       break;
1864     case GES_EDGE_START:
1865       GST_INFO ("Ripple start doesn't make sense, trimming instead");
1866       timeline->priv->movecontext.needs_move_ctx = TRUE;
1867       timeline_trim_object (timeline, obj, layers, edge, position);
1868       break;
1869     default:
1870       GST_DEBUG ("Can not ripple edge: %i", edge);
1871
1872       break;
1873   }
1874
1875   mv_ctx->ignore_needs_ctx = FALSE;
1876
1877   return TRUE;
1878
1879 error:
1880   mv_ctx->ignore_needs_ctx = FALSE;
1881
1882   return FALSE;
1883 }
1884
1885 gboolean
1886 timeline_slide_object (GESTimeline * timeline, GESTrackElement * obj,
1887     GList * layers, GESEdge edge, guint64 position)
1888 {
1889
1890   /* FIXME implement me! */
1891   GST_FIXME_OBJECT (timeline, "Slide mode editing not implemented yet");
1892
1893   return FALSE;
1894 }
1895
1896 gboolean
1897 timeline_trim_object (GESTimeline * timeline, GESTrackElement * object,
1898     GList * layers, GESEdge edge, guint64 position)
1899 {
1900   gboolean ret = FALSE;
1901   GstClockTime cpos;
1902   MoveContext *mv_ctx = &timeline->priv->movecontext;
1903
1904   mv_ctx->ignore_needs_ctx = TRUE;
1905
1906   timeline->priv->needs_rollback = FALSE;
1907   if (!ges_timeline_set_moving_context (timeline, object, GES_EDIT_MODE_TRIM,
1908           edge, layers))
1909     goto end;
1910
1911   switch (edge) {
1912     case GES_EDGE_START:
1913       cpos = GES_TIMELINE_ELEMENT_START (object);
1914       break;
1915     case GES_EDGE_END:
1916       cpos = GES_TIMELINE_ELEMENT_END (object);
1917       break;
1918     default:
1919       goto end;
1920   }
1921   ret = ges_timeline_trim_object_simple (timeline,
1922       GES_TIMELINE_ELEMENT (object), layers, edge, position, TRUE);
1923
1924   if (timeline->priv->needs_rollback && !timeline->priv->rolling_back) {
1925     timeline->priv->rolling_back = TRUE;
1926     ret = FALSE;
1927     timeline_trim_object (timeline, object, layers, edge, cpos);
1928     ges_timeline_emit_snappig (timeline, object, NULL);
1929     timeline->priv->rolling_back = FALSE;
1930   }
1931
1932 end:
1933   mv_ctx->ignore_needs_ctx = FALSE;
1934
1935   return ret;
1936 }
1937
1938 gboolean
1939 timeline_roll_object (GESTimeline * timeline, GESTrackElement * obj,
1940     GList * layers, GESEdge edge, guint64 position)
1941 {
1942   MoveContext *mv_ctx = &timeline->priv->movecontext;
1943   guint64 start, duration, end, tmpstart, tmpduration, tmpend, *snapped, *cur;
1944   gboolean ret = TRUE;
1945   GList *tmp;
1946
1947   mv_ctx->ignore_needs_ctx = TRUE;
1948
1949   GST_DEBUG_OBJECT (obj, "Rolling object to %" GST_TIME_FORMAT,
1950       GST_TIME_ARGS (position));
1951
1952   if (!ges_timeline_set_moving_context (timeline, obj, GES_EDIT_MODE_ROLL,
1953           edge, layers))
1954     goto error;
1955
1956   start = _START (obj);
1957   duration = _DURATION (obj);
1958   end = start + duration;
1959
1960   timeline->priv->needs_transitions_update = FALSE;
1961   switch (edge) {
1962     case GES_EDGE_START:
1963
1964       /* Avoid negative durations */
1965       if (position < mv_ctx->max_trim_pos || position > end ||
1966           position < mv_ctx->min_trim_pos)
1967         goto error;
1968
1969       cur = g_hash_table_lookup (timeline->priv->by_start, obj);
1970       snapped = ges_timeline_snap_position (timeline, obj, cur, position, TRUE);
1971       if (snapped)
1972         position = *snapped;
1973
1974       ret &= ges_timeline_trim_object_simple (timeline,
1975           GES_TIMELINE_ELEMENT (obj), layers, GES_EDGE_START, position, FALSE);
1976
1977       if (!ret) {
1978         GST_INFO_OBJECT (timeline, "Could not trim %s",
1979             GES_TIMELINE_ELEMENT_NAME (obj));
1980
1981         return FALSE;
1982       }
1983
1984
1985       /* In the case we reached max_duration we just make sure to roll
1986        * everything to the real new position */
1987       position = _START (obj);
1988
1989       /* Send back changes to the neighbourhood */
1990       for (tmp = mv_ctx->moving_trackelements; tmp; tmp = tmp->next) {
1991         GESTimelineElement *tmpelement = GES_TIMELINE_ELEMENT (tmp->data);
1992
1993         tmpstart = _START (tmpelement);
1994         tmpduration = _DURATION (tmpelement);
1995         tmpend = tmpstart + tmpduration;
1996
1997         /* Check that the object should be resized at this position
1998          * even if an error accurs, we keep doing our job */
1999         if (tmpend == start) {
2000           ret &= ges_timeline_trim_object_simple (timeline, tmpelement, NULL,
2001               GES_EDGE_END, position, FALSE);
2002           break;
2003         }
2004       }
2005       break;
2006     case GES_EDGE_END:
2007
2008       /* Avoid negative durations */
2009       if (position > mv_ctx->max_trim_pos || position < start)
2010         goto error;
2011
2012       end = _START (obj) + _DURATION (obj);
2013
2014       cur = g_hash_table_lookup (timeline->priv->by_end, obj);
2015       snapped = ges_timeline_snap_position (timeline, obj, cur, position, TRUE);
2016       if (snapped)
2017         position = *snapped;
2018
2019       ret &= ges_timeline_trim_object_simple (timeline,
2020           GES_TIMELINE_ELEMENT (obj), NULL, GES_EDGE_END, position, FALSE);
2021
2022       if (ret == FALSE) {
2023         GST_DEBUG_OBJECT (timeline, "No triming, bailing out");
2024         goto done;
2025       }
2026
2027       /* In the case we reached max_duration we just make sure to roll
2028        * everything to the real new position */
2029       position = _START (obj) + _DURATION (obj);
2030
2031       /* Send back changes to the neighbourhood */
2032       for (tmp = mv_ctx->moving_trackelements; tmp; tmp = tmp->next) {
2033         GESTimelineElement *tmpelement = GES_TIMELINE_ELEMENT (tmp->data);
2034
2035         tmpstart = _START (tmpelement);
2036         tmpduration = _DURATION (tmpelement);
2037         tmpend = tmpstart + tmpduration;
2038
2039         /* Check that the object should be resized at this position
2040          * even if an error accure, we keep doing our job */
2041         if (end == tmpstart) {
2042           ret &= ges_timeline_trim_object_simple (timeline, tmpelement, NULL,
2043               GES_EDGE_START, position, FALSE);
2044         }
2045       }
2046       break;
2047     default:
2048       GST_DEBUG ("Edge type %i not handled here", edge);
2049       break;
2050   }
2051
2052 done:
2053   timeline->priv->needs_transitions_update = TRUE;
2054   mv_ctx->ignore_needs_ctx = FALSE;
2055
2056   return ret;
2057
2058 error:
2059   GST_DEBUG_OBJECT (obj, "Could not roll edge %d to %" GST_TIME_FORMAT,
2060       edge, GST_TIME_ARGS (position));
2061
2062   ret = FALSE;
2063   goto done;
2064 }
2065
2066 gboolean
2067 timeline_move_object (GESTimeline * timeline, GESTrackElement * object,
2068     GList * layers, GESEdge edge, guint64 position)
2069 {
2070   if (!ges_timeline_set_moving_context (timeline, object, GES_EDIT_MODE_NORMAL,
2071           edge, layers)) {
2072     GST_DEBUG_OBJECT (object, "Could not move to %" GST_TIME_FORMAT,
2073         GST_TIME_ARGS (position));
2074
2075     return FALSE;
2076   }
2077
2078   return ges_timeline_move_object_simple (timeline,
2079       GES_TIMELINE_ELEMENT (object), layers, edge, position);
2080 }
2081
2082 gboolean
2083 ges_timeline_move_object_simple (GESTimeline * timeline,
2084     GESTimelineElement * element, GList * layers, GESEdge edge,
2085     guint64 position)
2086 {
2087   GstClockTime cpos = GES_TIMELINE_ELEMENT_START (element);
2088   guint64 *snap_end, *snap_st, *cur, position_offset, off1, off2, top_end;
2089   GESTrackElement *track_element;
2090   GESContainer *toplevel;
2091
2092   /* We only work with GESSource-s and we check that we are not already moving
2093    * the specified element ourself */
2094   if (GES_IS_SOURCE (element) == FALSE ||
2095       g_list_find (timeline->priv->movecontext.moving_trackelements, element))
2096     return FALSE;
2097
2098   timeline->priv->needs_rollback = FALSE;
2099   track_element = GES_TRACK_ELEMENT (element);
2100   toplevel = get_toplevel_container (track_element);
2101   position_offset = position - _START (track_element);
2102
2103   top_end = _START (toplevel) + _DURATION (toplevel) + position_offset;
2104   cur = g_hash_table_lookup (timeline->priv->by_end, track_element);
2105
2106   GST_DEBUG_OBJECT (timeline, "Moving %" GST_PTR_FORMAT " to %"
2107       GST_TIME_FORMAT " (end %" GST_TIME_FORMAT ")", element,
2108       GST_TIME_ARGS (position), GST_TIME_ARGS (top_end));
2109
2110   snap_end = ges_timeline_snap_position (timeline, track_element, cur, top_end,
2111       FALSE);
2112   if (snap_end)
2113     off1 = top_end > *snap_end ? top_end - *snap_end : *snap_end - top_end;
2114   else
2115     off1 = G_MAXUINT64;
2116
2117   cur = g_hash_table_lookup (timeline->priv->by_start, track_element);
2118   snap_st =
2119       ges_timeline_snap_position (timeline, track_element, cur, position,
2120       FALSE);
2121   if (snap_st)
2122     off2 = position > *snap_st ? position - *snap_st : *snap_st - position;
2123   else
2124     off2 = G_MAXUINT64;
2125
2126   /* In the case we could snap on both sides, we snap on the end */
2127   if (snap_end && off1 <= off2) {
2128     position = position + *snap_end - top_end;
2129     ges_timeline_emit_snappig (timeline, track_element, snap_end);
2130   } else if (snap_st) {
2131     position = position + *snap_st - position;
2132     ges_timeline_emit_snappig (timeline, track_element, snap_st);
2133   } else
2134     ges_timeline_emit_snappig (timeline, track_element, NULL);
2135   timeline->priv->needs_rollback = FALSE;
2136
2137   _set_start0 (GES_TIMELINE_ELEMENT (track_element), position);
2138
2139   if (timeline->priv->needs_rollback && !timeline->priv->rolling_back) {
2140     timeline->priv->needs_rollback = FALSE;
2141     timeline->priv->rolling_back = TRUE;
2142     ges_timeline_move_object_simple (timeline, element, layers, edge, cpos);
2143     ges_timeline_emit_snappig (timeline, track_element, NULL);
2144     timeline->priv->rolling_back = FALSE;
2145
2146     return FALSE;
2147   }
2148
2149   return TRUE;
2150 }
2151
2152 gboolean
2153 timeline_context_to_layer (GESTimeline * timeline, gint offset)
2154 {
2155   gboolean ret = TRUE;
2156   GHashTableIter iter;
2157   GESContainer *key, *value;
2158   GESLayer *new_layer;
2159   guint prio;
2160   MoveContext *mv_ctx = &timeline->priv->movecontext;
2161
2162   /* Layer's priority is always positive */
2163   if (offset == 0)
2164     return ret;
2165
2166   if (offset < 0 && mv_ctx->min_move_layer < -offset)
2167     return ret;
2168
2169   GST_DEBUG ("Moving %d object, offset %d",
2170       g_hash_table_size (mv_ctx->toplevel_containers), offset);
2171
2172   mv_ctx->ignore_needs_ctx = TRUE;
2173   timeline->priv->needs_rollback = FALSE;
2174   g_hash_table_iter_init (&iter, mv_ctx->toplevel_containers);
2175   while (g_hash_table_iter_next (&iter, (gpointer *) & key,
2176           (gpointer *) & value)) {
2177
2178     if (GES_IS_CLIP (value)) {
2179       prio = ges_clip_get_layer_priority (GES_CLIP (value));
2180
2181       /* We know that the layer exists as we created it */
2182       new_layer = GES_LAYER (g_list_nth_data (timeline->layers, prio + offset));
2183
2184       if (new_layer == NULL) {
2185         do {
2186           new_layer = ges_timeline_append_layer (timeline);
2187         } while (ges_layer_get_priority (new_layer) < prio + offset);
2188       }
2189
2190       mv_ctx->moving_to_layer = new_layer;
2191       ret &= ges_clip_move_to_layer (GES_CLIP (key), new_layer);
2192     } else if (GES_IS_GROUP (value)) {
2193       guint32 last_prio = _PRIORITY (value) + offset +
2194           GES_CONTAINER_HEIGHT (value) - 1;
2195
2196       new_layer = GES_LAYER (g_list_nth_data (timeline->layers, last_prio));
2197
2198       if (new_layer == NULL) {
2199         do {
2200           new_layer = ges_timeline_append_layer (timeline);
2201         } while (ges_layer_get_priority (new_layer) < last_prio);
2202       }
2203
2204       mv_ctx->moving_to_layer = NULL;
2205       _set_priority0 (GES_TIMELINE_ELEMENT (value), _PRIORITY (value) + offset);
2206     }
2207   }
2208
2209   /* Readjust min_move_layer */
2210   mv_ctx->min_move_layer = mv_ctx->min_move_layer + offset;
2211   mv_ctx->ignore_needs_ctx = FALSE;
2212
2213   if (timeline->priv->needs_rollback && !timeline->priv->rolling_back) {
2214     ret = FALSE;
2215     timeline->priv->rolling_back = TRUE;
2216     timeline_context_to_layer (timeline, -offset);
2217     timeline->priv->rolling_back = FALSE;
2218   }
2219   mv_ctx->moving_to_layer = NULL;
2220
2221   return ret;
2222 }
2223
2224 void
2225 timeline_add_group (GESTimeline * timeline, GESGroup * group)
2226 {
2227   GST_DEBUG_OBJECT (timeline, "Adding group %" GST_PTR_FORMAT, group);
2228
2229   timeline->priv->movecontext.needs_move_ctx = TRUE;
2230   timeline->priv->groups = g_list_prepend (timeline->priv->groups,
2231       gst_object_ref_sink (group));
2232
2233   ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (group), timeline);
2234 }
2235
2236 /**
2237  * timeline_emit_group_added:
2238  * @timeline: a #GESTimeline
2239  * @group: group that was added
2240  *
2241  * Emit group-added signal.
2242  */
2243 void
2244 timeline_emit_group_added (GESTimeline * timeline, GESGroup * group)
2245 {
2246   g_signal_emit (timeline, ges_timeline_signals[GROUP_ADDED], 0, group);
2247 }
2248
2249 /**
2250  * timeline_emit_group_removed:
2251  * @timeline: a #GESTimeline
2252  * @group: group that was removed
2253  * @array: (element-type GESTimelineElement): children that were removed
2254  *
2255  * Emit group-removed signal.
2256  */
2257 void
2258 timeline_emit_group_removed (GESTimeline * timeline, GESGroup * group,
2259     GPtrArray * array)
2260 {
2261   g_signal_emit (timeline, ges_timeline_signals[GROUP_REMOVED], 0, group,
2262       array);
2263 }
2264
2265 void
2266 timeline_remove_group (GESTimeline * timeline, GESGroup * group)
2267 {
2268   GST_DEBUG_OBJECT (timeline, "Removing group %" GST_PTR_FORMAT, group);
2269
2270   timeline->priv->groups = g_list_remove (timeline->priv->groups, group);
2271
2272   timeline->priv->movecontext.needs_move_ctx = TRUE;
2273   ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (group), NULL);
2274   gst_object_unref (group);
2275 }
2276
2277 static GPtrArray *
2278 select_tracks_for_object_default (GESTimeline * timeline,
2279     GESClip * clip, GESTrackElement * tr_object, gpointer user_data)
2280 {
2281   GPtrArray *result;
2282   GList *tmp;
2283
2284   result = g_ptr_array_new ();
2285
2286   for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
2287     GESTrack *track = GES_TRACK (tmp->data);
2288
2289     if ((track->type & ges_track_element_get_track_type (tr_object))) {
2290       gst_object_ref (track);
2291       g_ptr_array_add (result, track);
2292     }
2293   }
2294
2295   return result;
2296 }
2297
2298 static void
2299 add_object_to_tracks (GESTimeline * timeline, GESClip * clip, GESTrack * track)
2300 {
2301   gint i;
2302   GList *tmp, *list;
2303   GESTrackType types, visited_type = GES_TRACK_TYPE_UNKNOWN;
2304
2305   GST_DEBUG_OBJECT (timeline, "Creating %" GST_PTR_FORMAT
2306       " trackelements and adding them to our tracks", clip);
2307
2308   types = ges_clip_get_supported_formats (clip);
2309   if (track) {
2310     if ((types & track->type) == 0)
2311       return;
2312     types = track->type;
2313   }
2314
2315   for (i = 0, tmp = timeline->tracks; tmp; tmp = tmp->next, i++) {
2316     GESTrack *track = GES_TRACK (tmp->data);
2317
2318     if (((track->type & types) == 0 || (track->type & visited_type)))
2319       continue;
2320
2321     list = ges_clip_create_track_elements (clip, track->type);
2322     g_list_free (list);
2323   }
2324 }
2325
2326 static void
2327 layer_auto_transition_changed_cb (GESLayer * layer,
2328     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
2329 {
2330   GList *tmp, *clips;
2331
2332   timeline->priv->needs_rollback = FALSE;
2333   _create_transitions_on_layer (timeline, layer, NULL, NULL,
2334       _create_auto_transition_from_transitions);
2335
2336   clips = ges_layer_get_clips (layer);
2337   for (tmp = clips; tmp; tmp = tmp->next) {
2338     if (GES_IS_TRANSITION_CLIP (tmp->data)) {
2339       GList *tmpautotrans;
2340       gboolean found = FALSE;
2341
2342       for (tmpautotrans = timeline->priv->auto_transitions; tmpautotrans;
2343           tmpautotrans = tmpautotrans->next) {
2344         if (GES_AUTO_TRANSITION (tmpautotrans->data)->transition_clip ==
2345             tmp->data) {
2346           found = TRUE;
2347           break;
2348         }
2349       }
2350
2351       if (!found) {
2352         GST_ERROR_OBJECT (timeline,
2353             "Transition %s could not be wrapped into an auto transition"
2354             " REMOVING it", GES_TIMELINE_ELEMENT_NAME (tmp->data));
2355
2356         ges_layer_remove_clip (layer, tmp->data);
2357       }
2358     }
2359   }
2360   g_list_free_full (clips, gst_object_unref);
2361 }
2362
2363 static void
2364 clip_track_element_added_cb (GESClip * clip,
2365     GESTrackElement * track_element, GESTimeline * timeline)
2366 {
2367   guint i;
2368   GESTrack *track;
2369   gboolean is_source;
2370   GPtrArray *tracks = NULL;
2371   GESTrackElement *existing_src = NULL;
2372
2373   if (timeline->priv->ignore_track_element_added == clip) {
2374     GST_DEBUG_OBJECT (timeline, "Ignoring element added (%" GST_PTR_FORMAT
2375         " in %" GST_PTR_FORMAT, track_element, clip);
2376
2377     return;
2378   }
2379
2380   if (ges_track_element_get_track (track_element)) {
2381     GST_WARNING_OBJECT (track_element, "Already in a track");
2382
2383     return;
2384   }
2385
2386   g_signal_emit (G_OBJECT (timeline),
2387       ges_timeline_signals[SELECT_TRACKS_FOR_OBJECT], 0, clip, track_element,
2388       &tracks);
2389
2390   if (!tracks || tracks->len == 0) {
2391     GST_WARNING_OBJECT (timeline, "Got no Track to add %p (type %s), removing"
2392         " from clip (stopping 'child-added' signal emission).",
2393         track_element, ges_track_type_name (ges_track_element_get_track_type
2394             (track_element)));
2395
2396     if (tracks)
2397       g_ptr_array_unref (tracks);
2398
2399     g_signal_stop_emission_by_name (clip, "child-added");
2400     ges_container_remove (GES_CONTAINER (clip),
2401         GES_TIMELINE_ELEMENT (track_element));
2402
2403     return;
2404   }
2405
2406   /* We add the current element to the first track */
2407   track = g_ptr_array_index (tracks, 0);
2408
2409   is_source = g_type_is_a (G_OBJECT_TYPE (track_element), GES_TYPE_SOURCE);
2410   if (is_source)
2411     existing_src = ges_clip_find_track_element (clip, track, GES_TYPE_SOURCE);
2412
2413   if (existing_src == NULL) {
2414     if (!ges_track_add_element (track, track_element)) {
2415       GST_WARNING_OBJECT (clip, "Failed to add track element to track");
2416       ges_container_remove (GES_CONTAINER (clip),
2417           GES_TIMELINE_ELEMENT (track_element));
2418       g_ptr_array_unref (tracks);
2419       return;
2420     }
2421   } else {
2422     GST_INFO_OBJECT (clip, "Already had a Source Element in %" GST_PTR_FORMAT
2423         " of type %s, removing new one. (stopping 'child-added' emission)",
2424         track, G_OBJECT_TYPE_NAME (track_element));
2425     g_signal_stop_emission_by_name (clip, "child-added");
2426     ges_container_remove (GES_CONTAINER (clip),
2427         GES_TIMELINE_ELEMENT (track_element));
2428   }
2429   gst_object_unref (track);
2430   g_clear_object (&existing_src);
2431
2432   /* And create copies to add to other tracks */
2433   timeline->priv->ignore_track_element_added = clip;
2434   for (i = 1; i < tracks->len; i++) {
2435     GESTrack *track;
2436     GESTrackElement *track_element_copy;
2437
2438     track = g_ptr_array_index (tracks, i);
2439     if (is_source)
2440       existing_src = ges_clip_find_track_element (clip, track, GES_TYPE_SOURCE);
2441     if (existing_src == NULL) {
2442       ges_container_remove (GES_CONTAINER (clip),
2443           GES_TIMELINE_ELEMENT (track_element));
2444       gst_object_unref (track);
2445       g_ptr_array_unref (tracks);
2446       continue;
2447     } else {
2448       GST_INFO_OBJECT (clip, "Already had a Source Element in %" GST_PTR_FORMAT
2449           " of type %s, removing new one. (stopping 'child-added' emission)",
2450           track, G_OBJECT_TYPE_NAME (track_element));
2451       g_signal_stop_emission_by_name (clip, "child-added");
2452       ges_container_remove (GES_CONTAINER (clip),
2453           GES_TIMELINE_ELEMENT (track_element));
2454     }
2455     g_clear_object (&existing_src);
2456
2457     track_element_copy =
2458         GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT
2459             (track_element), TRUE));
2460
2461     GST_LOG_OBJECT (timeline, "Trying to add %p to track %p",
2462         track_element_copy, track);
2463
2464     if (!ges_container_add (GES_CONTAINER (clip),
2465             GES_TIMELINE_ELEMENT (track_element_copy))) {
2466       GST_WARNING_OBJECT (clip, "Failed to add track element to clip");
2467       gst_object_unref (track_element_copy);
2468       g_ptr_array_unref (tracks);
2469       return;
2470     }
2471
2472     if (!ges_track_add_element (track, track_element_copy)) {
2473       GST_WARNING_OBJECT (clip, "Failed to add track element to track");
2474       ges_container_remove (GES_CONTAINER (clip),
2475           GES_TIMELINE_ELEMENT (track_element_copy));
2476       gst_object_unref (track_element_copy);
2477       g_ptr_array_unref (tracks);
2478       return;
2479     }
2480
2481     gst_object_unref (track);
2482   }
2483   timeline->priv->ignore_track_element_added = NULL;
2484   g_ptr_array_unref (tracks);
2485 }
2486
2487 static void
2488 clip_track_element_removed_cb (GESClip * clip,
2489     GESTrackElement * track_element, GESTimeline * timeline)
2490 {
2491   GESTrack *track = ges_track_element_get_track (track_element);
2492
2493   if (track)
2494     ges_track_remove_element (track, track_element);
2495 }
2496
2497 static void
2498 layer_object_added_cb (GESLayer * layer, GESClip * clip, GESTimeline * timeline)
2499 {
2500   GESProject *project;
2501
2502   /* We make sure not to be connected twice */
2503   g_signal_handlers_disconnect_by_func (clip, clip_track_element_added_cb,
2504       timeline);
2505   g_signal_handlers_disconnect_by_func (clip, clip_track_element_removed_cb,
2506       timeline);
2507
2508   /* And we connect to the object */
2509   g_signal_connect (clip, "child-added",
2510       G_CALLBACK (clip_track_element_added_cb), timeline);
2511   g_signal_connect (clip, "child-removed",
2512       G_CALLBACK (clip_track_element_removed_cb), timeline);
2513
2514   if (ges_clip_is_moving_from_layer (clip)) {
2515     GST_DEBUG ("Clip %p moving from one layer to another, not creating "
2516         "TrackElement", clip);
2517     timeline->priv->movecontext.needs_move_ctx = TRUE;
2518     _create_transitions_on_layer (timeline, layer, NULL, NULL,
2519         _find_transition_from_auto_transitions);
2520     return;
2521   }
2522
2523
2524   add_object_to_tracks (timeline, clip, NULL);
2525
2526   GST_DEBUG ("Making sure that the asset is in our project");
2527   project =
2528       GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE (timeline)));
2529   ges_project_add_asset (project,
2530       ges_extractable_get_asset (GES_EXTRACTABLE (clip)));
2531
2532   GST_DEBUG ("Done");
2533 }
2534
2535 static void
2536 layer_priority_changed_cb (GESLayer * layer,
2537     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
2538 {
2539   if (timeline->priv->resyncing_layers)
2540     return;
2541
2542   timeline->layers = g_list_sort (timeline->layers, (GCompareFunc)
2543       sort_layers);
2544 }
2545
2546 static void
2547 layer_object_removed_cb (GESLayer * layer, GESClip * clip,
2548     GESTimeline * timeline)
2549 {
2550   GList *trackelements, *tmp;
2551
2552   if (ges_clip_is_moving_from_layer (clip)) {
2553     GST_DEBUG ("Clip %p is moving from a layer to another, not doing"
2554         " anything on it", clip);
2555     return;
2556   }
2557
2558   GST_DEBUG ("Clip %p removed from layer %p", clip, layer);
2559
2560   /* Go over the clip's track element and figure out which one belongs to
2561    * the list of tracks we control */
2562
2563   trackelements = ges_container_get_children (GES_CONTAINER (clip), FALSE);
2564   for (tmp = trackelements; tmp; tmp = tmp->next) {
2565     GESTrackElement *track_element = (GESTrackElement *) tmp->data;
2566     GESTrack *track = ges_track_element_get_track (track_element);
2567
2568     if (!track)
2569       continue;
2570
2571     GST_DEBUG_OBJECT (timeline, "Trying to remove TrackElement %p",
2572         track_element);
2573
2574     /* FIXME Check if we should actually check that we control the
2575      * track in the new management of TrackElement context */
2576     LOCK_DYN (timeline);
2577     if (G_LIKELY (g_list_find_custom (timeline->priv->priv_tracks, track,
2578                 (GCompareFunc) custom_find_track) || track == NULL)) {
2579       GST_DEBUG ("Belongs to one of the tracks we control");
2580
2581       ges_track_remove_element (track, track_element);
2582     }
2583     UNLOCK_DYN (timeline);
2584   }
2585   g_signal_handlers_disconnect_by_func (clip, clip_track_element_added_cb,
2586       timeline);
2587   g_signal_handlers_disconnect_by_func (clip, clip_track_element_removed_cb,
2588       timeline);
2589
2590   g_list_free_full (trackelements, gst_object_unref);
2591
2592   GST_DEBUG ("Done");
2593 }
2594
2595 static void
2596 trackelement_start_changed_cb (GESTrackElement * child,
2597     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
2598 {
2599   GESTimelinePrivate *priv = timeline->priv;
2600   TrackObjIters *iters = g_hash_table_lookup (priv->obj_iters, child);
2601
2602   if (G_LIKELY (iters->iter_by_layer))
2603     g_sequence_sort_changed (iters->iter_by_layer,
2604         (GCompareDataFunc) element_start_compare, NULL);
2605
2606   if (GES_IS_SOURCE (child)) {
2607     sort_track_elements (timeline, iters);
2608     sort_starts_ends_start (timeline, iters);
2609     sort_starts_ends_end (timeline, iters);
2610
2611     /* If the timeline is set to snap objects together, we
2612      * are sure that all movement of TrackElement-s are done within
2613      * the moving context, so we do not need to recalculate the
2614      * move context as often */
2615     if (timeline->priv->movecontext.ignore_needs_ctx &&
2616         timeline->priv->snapping_distance == 0)
2617       timeline->priv->movecontext.needs_move_ctx = TRUE;
2618
2619     timeline_create_transitions (timeline, child);
2620   }
2621 }
2622
2623 static void
2624 trackelement_priority_changed_cb (GESTrackElement * child,
2625     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
2626 {
2627   GESTimelinePrivate *priv = timeline->priv;
2628
2629   GList *layer_node = g_list_find_custom (timeline->layers,
2630       GINT_TO_POINTER (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (child)),
2631       (GCompareFunc) find_layer_by_prio);
2632   GESLayer *layer = layer_node ? layer_node->data : NULL;
2633   TrackObjIters *iters = g_hash_table_lookup (priv->obj_iters,
2634       child);
2635
2636   if (G_UNLIKELY (layer == NULL)) {
2637     GST_ERROR_OBJECT (timeline,
2638         "Changing a TrackElement prio, which would not "
2639         "land in no layer we are controlling");
2640     if (iters->iter_by_layer)
2641       g_sequence_remove (iters->iter_by_layer);
2642     iters->iter_by_layer = NULL;
2643     iters->layer = NULL;
2644   } else {
2645     /* If it moves from layer, properly change it */
2646     if (layer != iters->layer) {
2647       GSequence *by_layer_sequence =
2648           g_hash_table_lookup (priv->by_layer, layer);
2649
2650       GST_DEBUG_OBJECT (child, "Moved from layer %" GST_PTR_FORMAT
2651           "(prio %d) to" " %" GST_PTR_FORMAT " (prio %d)", layer,
2652           ges_layer_get_priority (layer), iters->layer,
2653           ges_layer_get_priority (iters->layer));
2654
2655       g_sequence_remove (iters->iter_by_layer);
2656       iters->iter_by_layer =
2657           g_sequence_insert_sorted (by_layer_sequence, child,
2658           (GCompareDataFunc) element_start_compare, NULL);
2659       iters->layer = layer;
2660     } else {
2661       g_sequence_sort_changed (iters->iter_by_layer,
2662           (GCompareDataFunc) element_start_compare, NULL);
2663     }
2664   }
2665
2666   if (GES_IS_SOURCE (child))
2667     sort_track_elements (timeline, iters);
2668 }
2669
2670 static void
2671 trackelement_duration_changed_cb (GESTrackElement * child,
2672     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
2673 {
2674   GESTimelinePrivate *priv = timeline->priv;
2675   TrackObjIters *iters = g_hash_table_lookup (priv->obj_iters, child);
2676
2677   if (GES_IS_SOURCE (child)) {
2678     sort_starts_ends_end (timeline, iters);
2679
2680     /* If the timeline is set to snap objects together, we
2681      * are sure that all movement of TrackElement-s are done within
2682      * the moving context, so we do not need to recalculate the
2683      * move context as often */
2684     if (timeline->priv->movecontext.ignore_needs_ctx &&
2685         timeline->priv->snapping_distance == 0) {
2686       timeline->priv->movecontext.needs_move_ctx = TRUE;
2687     }
2688
2689     timeline_create_transitions (timeline, child);
2690   }
2691 }
2692
2693 static void
2694 track_element_added_cb (GESTrack * track, GESTrackElement * track_element,
2695     GESTimeline * timeline)
2696 {
2697   /* Auto transition should be updated before we receive the signal */
2698   g_signal_connect_after (GES_TRACK_ELEMENT (track_element), "notify::start",
2699       G_CALLBACK (trackelement_start_changed_cb), timeline);
2700   g_signal_connect_after (GES_TRACK_ELEMENT (track_element),
2701       "notify::duration", G_CALLBACK (trackelement_duration_changed_cb),
2702       timeline);
2703   g_signal_connect_after (GES_TRACK_ELEMENT (track_element),
2704       "notify::priority", G_CALLBACK (trackelement_priority_changed_cb),
2705       timeline);
2706
2707   start_tracking_track_element (timeline, track_element);
2708 }
2709
2710 static void
2711 track_element_removed_cb (GESTrack * track,
2712     GESTrackElement * track_element, GESTimeline * timeline)
2713 {
2714
2715   if (GES_IS_SOURCE (track_element)) {
2716     /* Make sure to reinitialise the moving context next time */
2717     timeline->priv->movecontext.needs_move_ctx = TRUE;
2718   }
2719
2720   /* Disconnect all signal handlers */
2721   g_signal_handlers_disconnect_by_func (track_element,
2722       trackelement_start_changed_cb, timeline);
2723   g_signal_handlers_disconnect_by_func (track_element,
2724       trackelement_duration_changed_cb, timeline);
2725   g_signal_handlers_disconnect_by_func (track_element,
2726       trackelement_priority_changed_cb, timeline);
2727
2728   stop_tracking_track_element (timeline, track_element);
2729 }
2730
2731 static GstPadProbeReturn
2732 _pad_probe_cb (GstPad * mixer_pad, GstPadProbeInfo * info,
2733     GESTimeline * timeline)
2734 {
2735   GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
2736   if (GST_EVENT_TYPE (event) == GST_EVENT_STREAM_START) {
2737     LOCK_DYN (timeline);
2738     if (timeline->priv->group_id == -1) {
2739       if (!gst_event_parse_group_id (event, &timeline->priv->group_id))
2740         timeline->priv->group_id = gst_util_group_id_next ();
2741     }
2742
2743     info->data = gst_event_make_writable (event);
2744     gst_event_set_group_id (GST_PAD_PROBE_INFO_EVENT (info),
2745         timeline->priv->group_id);
2746     UNLOCK_DYN (timeline);
2747
2748     return GST_PAD_PROBE_REMOVE;
2749   }
2750
2751   return GST_PAD_PROBE_OK;
2752 }
2753
2754 static void
2755 _ghost_track_srcpad (TrackPrivate * tr_priv)
2756 {
2757   GstPad *pad;
2758   gchar *padname;
2759   gboolean no_more;
2760   GList *tmp;
2761   GESTrack *track = tr_priv->track;
2762
2763   pad = gst_element_get_static_pad (GST_ELEMENT (track), "src");
2764
2765   GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
2766
2767   /* Remember the pad */
2768   LOCK_DYN (tr_priv->timeline);
2769   GST_OBJECT_LOCK (track);
2770   tr_priv->pad = pad;
2771
2772   no_more = TRUE;
2773   for (tmp = tr_priv->timeline->priv->priv_tracks; tmp; tmp = g_list_next (tmp)) {
2774     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
2775
2776     if (!tr_priv->pad) {
2777       GST_LOG ("Found track without pad %p", tr_priv->track);
2778       no_more = FALSE;
2779     }
2780   }
2781   GST_OBJECT_UNLOCK (track);
2782
2783   /* ghost it ! */
2784   GST_DEBUG ("Ghosting pad and adding it to ourself");
2785   padname = g_strdup_printf ("track_%p_src", track);
2786   tr_priv->ghostpad = gst_ghost_pad_new (padname, pad);
2787   g_free (padname);
2788   gst_pad_set_active (tr_priv->ghostpad, TRUE);
2789   gst_element_add_pad (GST_ELEMENT (tr_priv->timeline), tr_priv->ghostpad);
2790
2791   if (no_more) {
2792     GST_DEBUG ("Signaling no-more-pads");
2793     gst_element_no_more_pads (GST_ELEMENT (tr_priv->timeline));
2794   }
2795
2796   tr_priv->probe_id = gst_pad_add_probe (pad,
2797       GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
2798       (GstPadProbeCallback) _pad_probe_cb, tr_priv->timeline, NULL);
2799
2800   UNLOCK_DYN (tr_priv->timeline);
2801 }
2802
2803 gboolean
2804 timeline_add_element (GESTimeline * timeline, GESTimelineElement * element)
2805 {
2806   GESTimelineElement *same_name =
2807       g_hash_table_lookup (timeline->priv->all_elements,
2808       element->name);
2809
2810   GST_DEBUG_OBJECT (timeline, "Adding element: %s", element->name);
2811   if (same_name) {
2812     GST_ERROR_OBJECT (timeline, "%s Already in the timeline %" GST_PTR_FORMAT,
2813         element->name, same_name);
2814     return FALSE;
2815   }
2816
2817   g_hash_table_insert (timeline->priv->all_elements,
2818       ges_timeline_element_get_name (element), gst_object_ref (element));
2819
2820   return TRUE;
2821 }
2822
2823 gboolean
2824 timeline_remove_element (GESTimeline * timeline, GESTimelineElement * element)
2825 {
2826   return g_hash_table_remove (timeline->priv->all_elements, element->name);
2827 }
2828
2829 void
2830 timeline_fill_gaps (GESTimeline * timeline)
2831 {
2832   GList *tmp;
2833
2834   for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
2835     track_resort_and_fill_gaps (tmp->data);
2836   }
2837 }
2838
2839 /**** API *****/
2840 /**
2841  * ges_timeline_new:
2842  *
2843  * Creates a new empty #GESTimeline.
2844  *
2845  * Returns: (transfer floating): The new timeline.
2846  */
2847
2848 GESTimeline *
2849 ges_timeline_new (void)
2850 {
2851   GESProject *project = ges_project_new (NULL);
2852   GESExtractable *timeline = g_object_new (GES_TYPE_TIMELINE, NULL);
2853
2854   ges_extractable_set_asset (timeline, GES_ASSET (project));
2855   gst_object_unref (project);
2856
2857   return GES_TIMELINE (timeline);
2858 }
2859
2860 /**
2861  * ges_timeline_new_from_uri:
2862  * @uri: the URI to load from
2863  * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
2864  *
2865  * Creates a timeline from the given URI.
2866  *
2867  * Returns: (transfer floating) (nullable): A new timeline if the uri was loaded
2868  * successfully, or %NULL if the uri could not be loaded.
2869  */
2870 GESTimeline *
2871 ges_timeline_new_from_uri (const gchar * uri, GError ** error)
2872 {
2873   GESTimeline *ret;
2874   GESProject *project = ges_project_new (uri);
2875
2876   ret = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), error));
2877   gst_object_unref (project);
2878
2879   return ret;
2880 }
2881
2882 /**
2883  * ges_timeline_load_from_uri:
2884  * @timeline: an empty #GESTimeline into which to load the formatter
2885  * @uri: The URI to load from
2886  * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
2887  *
2888  * Loads the contents of URI into the given timeline.
2889  *
2890  * Returns: %TRUE if the timeline was loaded successfully, or %FALSE if the uri
2891  * could not be loaded.
2892  */
2893 gboolean
2894 ges_timeline_load_from_uri (GESTimeline * timeline, const gchar * uri,
2895     GError ** error)
2896 {
2897   GESProject *project;
2898   gboolean ret = FALSE;
2899
2900   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
2901   g_return_val_if_fail ((ges_extractable_get_asset (GES_EXTRACTABLE
2902               (timeline)) == NULL), FALSE);
2903
2904   project = ges_project_new (uri);
2905   ret = ges_project_load (project, timeline, error);
2906   gst_object_unref (project);
2907
2908   return ret;
2909 }
2910
2911 /**
2912  * ges_timeline_save_to_uri:
2913  * @timeline: a #GESTimeline
2914  * @uri: The location to save to
2915  * @formatter_asset: (allow-none): The formatter asset to use or %NULL. If %NULL,
2916  * will try to save in the same format as the one from which the timeline as been loaded
2917  * or default to the formatter with highest rank
2918  * @overwrite: %TRUE to overwrite file if it exists
2919  * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
2920  *
2921  * Saves the timeline to the given location
2922  *
2923  * Returns: %TRUE if the timeline was successfully saved to the given location,
2924  * else %FALSE.
2925  */
2926 gboolean
2927 ges_timeline_save_to_uri (GESTimeline * timeline, const gchar * uri,
2928     GESAsset * formatter_asset, gboolean overwrite, GError ** error)
2929 {
2930   GESProject *project;
2931
2932   gboolean ret, created_proj = FALSE;
2933
2934   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
2935   project =
2936       GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE (timeline)));
2937
2938   if (project == NULL) {
2939     project = ges_project_new (NULL);
2940     created_proj = TRUE;
2941   }
2942
2943   ret = ges_project_save (project, timeline, uri, formatter_asset, overwrite,
2944       error);
2945
2946   if (created_proj)
2947     gst_object_unref (project);
2948
2949   return ret;
2950 }
2951
2952 /**
2953  * ges_timeline_get_groups:
2954  * @timeline: a #GESTimeline
2955  *
2956  * Get the list of #GESGroup present in the Timeline.
2957  *
2958  * Returns: (transfer none) (element-type GESGroup): the list of
2959  * #GESGroup that contain clips present in the timeline's layers.
2960  * Must not be changed.
2961  */
2962 GList *
2963 ges_timeline_get_groups (GESTimeline * timeline)
2964 {
2965   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
2966   CHECK_THREAD (timeline);
2967
2968   return timeline->priv->groups;
2969 }
2970
2971 /**
2972  * ges_timeline_append_layer:
2973  * @timeline: a #GESTimeline
2974  *
2975  * Append a newly created #GESLayer to @timeline
2976  * Note that you do not own any reference to the returned layer.
2977  *
2978  * Returns: (transfer none): The newly created #GESLayer, or the last (empty)
2979  * #GESLayer of @timeline.
2980  */
2981 GESLayer *
2982 ges_timeline_append_layer (GESTimeline * timeline)
2983 {
2984   guint32 priority;
2985   GESLayer *layer;
2986
2987   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
2988   CHECK_THREAD (timeline);
2989
2990   layer = ges_layer_new ();
2991   priority = g_list_length (timeline->layers);
2992   ges_layer_set_priority (layer, priority);
2993
2994   ges_timeline_add_layer (timeline, layer);
2995
2996   return layer;
2997 }
2998
2999 /**
3000  * ges_timeline_add_layer:
3001  * @timeline: a #GESTimeline
3002  * @layer: (transfer floating): the #GESLayer to add
3003  *
3004  * Add the layer to the timeline. The reference to the @layer will be stolen
3005  * by the @timeline.
3006  *
3007  * Returns: %TRUE if the layer was properly added, else %FALSE.
3008  */
3009 gboolean
3010 ges_timeline_add_layer (GESTimeline * timeline, GESLayer * layer)
3011 {
3012   gboolean auto_transition;
3013   GList *objects, *tmp;
3014
3015   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3016   g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
3017   CHECK_THREAD (timeline);
3018
3019   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
3020
3021   /* We can only add a layer that doesn't already belong to another timeline */
3022   if (G_UNLIKELY (layer->timeline)) {
3023     GST_WARNING ("Layer belongs to another timeline, can't add it");
3024     gst_object_ref_sink (layer);
3025     gst_object_unref (layer);
3026     return FALSE;
3027   }
3028
3029   /* Add to the list of layers, make sure we don't already control it */
3030   if (G_UNLIKELY (g_list_find (timeline->layers, (gconstpointer) layer))) {
3031     GST_WARNING ("Layer is already controlled by this timeline");
3032     gst_object_ref_sink (layer);
3033     gst_object_unref (layer);
3034     return FALSE;
3035   }
3036
3037   auto_transition = ges_layer_get_auto_transition (layer);
3038
3039   /* If the user doesn't explicitely set layer auto_transition, then set our */
3040   if (!auto_transition) {
3041     auto_transition = ges_timeline_get_auto_transition (timeline);
3042     ges_layer_set_auto_transition (layer, auto_transition);
3043   }
3044
3045   gst_object_ref_sink (layer);
3046   timeline->layers = g_list_insert_sorted (timeline->layers, layer,
3047       (GCompareFunc) sort_layers);
3048
3049   /* Inform the layer that it belongs to a new timeline */
3050   ges_layer_set_timeline (layer, timeline);
3051
3052   g_hash_table_insert (timeline->priv->by_layer, layer, g_sequence_new (NULL));
3053
3054   /* Connect to 'clip-added'/'clip-removed' signal from the new layer */
3055   g_signal_connect_after (layer, "clip-added",
3056       G_CALLBACK (layer_object_added_cb), timeline);
3057   g_signal_connect_after (layer, "clip-removed",
3058       G_CALLBACK (layer_object_removed_cb), timeline);
3059   g_signal_connect (layer, "notify::priority",
3060       G_CALLBACK (layer_priority_changed_cb), timeline);
3061   g_signal_connect (layer, "notify::auto-transition",
3062       G_CALLBACK (layer_auto_transition_changed_cb), timeline);
3063
3064   GST_DEBUG ("Done adding layer, emitting 'layer-added' signal");
3065   g_signal_emit (timeline, ges_timeline_signals[LAYER_ADDED], 0, layer);
3066
3067   /* add any existing clips to the timeline */
3068   objects = ges_layer_get_clips (layer);
3069   for (tmp = objects; tmp; tmp = tmp->next) {
3070     layer_object_added_cb (layer, tmp->data, timeline);
3071     gst_object_unref (tmp->data);
3072     tmp->data = NULL;
3073   }
3074   g_list_free (objects);
3075
3076   timeline->priv->movecontext.needs_move_ctx = TRUE;
3077
3078   return TRUE;
3079 }
3080
3081 /**
3082  * ges_timeline_remove_layer:
3083  * @timeline: a #GESTimeline
3084  * @layer: the #GESLayer to remove
3085  *
3086  * Removes the layer from the timeline. The reference that the @timeline holds on
3087  * the layer will be dropped. If you wish to use the @layer after calling this
3088  * method, you need to take a reference before calling.
3089  *
3090  * Returns: %TRUE if the layer was properly removed, else %FALSE.
3091  */
3092
3093 gboolean
3094 ges_timeline_remove_layer (GESTimeline * timeline, GESLayer * layer)
3095 {
3096   GList *layer_objects, *tmp;
3097
3098   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3099   g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
3100   CHECK_THREAD (timeline);
3101
3102   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
3103
3104   if (G_UNLIKELY (!g_list_find (timeline->layers, layer))) {
3105     GST_WARNING ("Layer doesn't belong to this timeline");
3106     return FALSE;
3107   }
3108
3109   /* remove objects from any private data structures */
3110
3111   layer_objects = ges_layer_get_clips (layer);
3112   for (tmp = layer_objects; tmp; tmp = tmp->next) {
3113     layer_object_removed_cb (layer, GES_CLIP (tmp->data), timeline);
3114     gst_object_unref (G_OBJECT (tmp->data));
3115     tmp->data = NULL;
3116   }
3117   g_list_free (layer_objects);
3118
3119   /* Disconnect signals */
3120   GST_DEBUG ("Disconnecting signal callbacks");
3121   g_signal_handlers_disconnect_by_func (layer, layer_object_added_cb, timeline);
3122   g_signal_handlers_disconnect_by_func (layer, layer_object_removed_cb,
3123       timeline);
3124   g_signal_handlers_disconnect_by_func (layer, layer_priority_changed_cb,
3125       timeline);
3126   g_signal_handlers_disconnect_by_func (layer,
3127       layer_auto_transition_changed_cb, timeline);
3128
3129   g_hash_table_remove (timeline->priv->by_layer, layer);
3130   timeline->layers = g_list_remove (timeline->layers, layer);
3131   ges_layer_set_timeline (layer, NULL);
3132
3133   g_signal_emit (timeline, ges_timeline_signals[LAYER_REMOVED], 0, layer);
3134
3135   gst_object_unref (layer);
3136   timeline->priv->movecontext.needs_move_ctx = TRUE;
3137
3138   return TRUE;
3139 }
3140
3141 /**
3142  * ges_timeline_add_track:
3143  * @timeline: a #GESTimeline
3144  * @track: (transfer full): the #GESTrack to add
3145  *
3146  * Add a track to the timeline. The reference to the track will be stolen by the
3147  * pipeline.
3148  *
3149  * Returns: %TRUE if the track was properly added, else %FALSE.
3150  */
3151
3152 /* FIXME: create track elements for clips which have already been
3153  * added to existing layers.
3154  */
3155
3156 gboolean
3157 ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
3158 {
3159   TrackPrivate *tr_priv;
3160   GList *tmp;
3161
3162   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3163   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
3164   CHECK_THREAD (timeline);
3165
3166   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
3167
3168   /* make sure we don't already control it */
3169   if (G_UNLIKELY (g_list_find (timeline->tracks, (gconstpointer) track))) {
3170     GST_WARNING ("Track is already controlled by this timeline");
3171     return FALSE;
3172   }
3173
3174   /* Add the track to ourself (as a GstBin)
3175    * Reference is stolen ! */
3176   if (G_UNLIKELY (!gst_bin_add (GST_BIN (timeline), GST_ELEMENT (track)))) {
3177     GST_WARNING ("Couldn't add track to ourself (GST)");
3178     return FALSE;
3179   }
3180
3181   tr_priv = g_new0 (TrackPrivate, 1);
3182   tr_priv->timeline = timeline;
3183   tr_priv->track = track;
3184
3185   /* Add the track to the list of tracks we track */
3186   LOCK_DYN (timeline);
3187   timeline->priv->priv_tracks = g_list_append (timeline->priv->priv_tracks,
3188       tr_priv);
3189   UNLOCK_DYN (timeline);
3190   timeline->tracks = g_list_append (timeline->tracks, track);
3191
3192   /* Inform the track that it's currently being used by ourself */
3193   ges_track_set_timeline (track, timeline);
3194
3195   GST_DEBUG ("Done adding track, emitting 'track-added' signal");
3196
3197   _ghost_track_srcpad (tr_priv);
3198
3199   /* emit 'track-added' */
3200   g_signal_emit (timeline, ges_timeline_signals[TRACK_ADDED], 0, track);
3201
3202   /* ensure that each existing clip has the opportunity to create a
3203    * track element for this track*/
3204
3205   /* We connect to the object for the timeline editing mode management */
3206   g_signal_connect (G_OBJECT (track), "track-element-added",
3207       G_CALLBACK (track_element_added_cb), timeline);
3208   g_signal_connect (G_OBJECT (track), "track-element-removed",
3209       G_CALLBACK (track_element_removed_cb), timeline);
3210
3211   for (tmp = timeline->layers; tmp; tmp = tmp->next) {
3212     GList *objects, *obj;
3213     objects = ges_layer_get_clips (tmp->data);
3214
3215     for (obj = objects; obj; obj = obj->next) {
3216       GESClip *clip = obj->data;
3217
3218       add_object_to_tracks (timeline, clip, track);
3219       gst_object_unref (clip);
3220     }
3221     g_list_free (objects);
3222   }
3223
3224   /* FIXME Check if we should rollback if we can't sync state */
3225   gst_element_sync_state_with_parent (GST_ELEMENT (track));
3226   g_object_set (track, "message-forward", TRUE, NULL);
3227
3228   return TRUE;
3229 }
3230
3231 /**
3232  * ges_timeline_remove_track:
3233  * @timeline: a #GESTimeline
3234  * @track: the #GESTrack to remove
3235  *
3236  * Remove the @track from the @timeline. The reference stolen when adding the
3237  * @track will be removed. If you wish to use the @track after calling this
3238  * function you must ensure that you have a reference to it.
3239  *
3240  * Returns: %TRUE if the @track was properly removed, else %FALSE.
3241  */
3242
3243 /* FIXME: release any track elements associated with this layer. currenly this
3244  * will not happen if you remove the track before removing *all*
3245  * clips which have a track element in this track.
3246  */
3247
3248 gboolean
3249 ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track)
3250 {
3251   GList *tmp;
3252   TrackPrivate *tr_priv;
3253   GESTimelinePrivate *priv;
3254
3255   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
3256   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3257   CHECK_THREAD (timeline);
3258
3259   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
3260
3261   priv = timeline->priv;
3262   LOCK_DYN (timeline);
3263   if (G_UNLIKELY (!(tmp = g_list_find_custom (priv->priv_tracks,
3264                   track, (GCompareFunc) custom_find_track)))) {
3265     GST_WARNING ("Track doesn't belong to this timeline");
3266     UNLOCK_DYN (timeline);
3267     return FALSE;
3268   }
3269
3270   tr_priv = tmp->data;
3271   gst_object_unref (tr_priv->pad);
3272   priv->priv_tracks = g_list_remove (priv->priv_tracks, tr_priv);
3273   UNLOCK_DYN (timeline);
3274   timeline->tracks = g_list_remove (timeline->tracks, track);
3275
3276   ges_track_set_timeline (track, NULL);
3277
3278   /* Remove ghost pad */
3279   if (tr_priv->ghostpad) {
3280     GST_DEBUG ("Removing ghostpad");
3281     gst_pad_set_active (tr_priv->ghostpad, FALSE);
3282     gst_ghost_pad_set_target ((GstGhostPad *) tr_priv->ghostpad, NULL);
3283     gst_element_remove_pad (GST_ELEMENT (timeline), tr_priv->ghostpad);
3284   }
3285
3286   /* Remove pad-added/-removed handlers */
3287   g_signal_handlers_disconnect_by_func (track, track_element_added_cb,
3288       timeline);
3289   g_signal_handlers_disconnect_by_func (track, track_element_removed_cb,
3290       timeline);
3291
3292   /* Signal track removal to all layers/objects */
3293   g_signal_emit (timeline, ges_timeline_signals[TRACK_REMOVED], 0, track);
3294
3295   /* remove track from our bin */
3296   gst_object_ref (track);
3297   if (G_UNLIKELY (!gst_bin_remove (GST_BIN (timeline), GST_ELEMENT (track)))) {
3298     GST_WARNING ("Couldn't remove track to ourself (GST)");
3299     gst_object_unref (track);
3300     return FALSE;
3301   }
3302
3303   /* set track state to NULL */
3304   gst_element_set_state (GST_ELEMENT (track), GST_STATE_NULL);
3305
3306   gst_object_unref (track);
3307
3308   g_free (tr_priv);
3309
3310   return TRUE;
3311 }
3312
3313 /**
3314  * ges_timeline_get_track_for_pad:
3315  * @timeline: The #GESTimeline
3316  * @pad: The #GstPad
3317  *
3318  * Search the #GESTrack corresponding to the given @timeline's @pad.
3319  *
3320  * Returns: (transfer none) (nullable): The corresponding #GESTrack if it is
3321  * found, or %NULL if there is an error.
3322  */
3323
3324 GESTrack *
3325 ges_timeline_get_track_for_pad (GESTimeline * timeline, GstPad * pad)
3326 {
3327   GList *tmp;
3328
3329   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
3330
3331   LOCK_DYN (timeline);
3332   for (tmp = timeline->priv->priv_tracks; tmp; tmp = g_list_next (tmp)) {
3333     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
3334     if (pad == tr_priv->ghostpad) {
3335       UNLOCK_DYN (timeline);
3336       return tr_priv->track;
3337     }
3338   }
3339   UNLOCK_DYN (timeline);
3340
3341   return NULL;
3342 }
3343
3344 /**
3345  * ges_timeline_get_pad_for_track:
3346  * @timeline: The #GESTimeline
3347  * @track: The #GESTrack
3348  *
3349  * Search the #GstPad corresponding to the given @timeline's @track.
3350  *
3351  * Returns: (transfer none) (nullable): The corresponding #GstPad if it is
3352  * found, or %NULL if there is an error.
3353  */
3354
3355 GstPad *
3356 ges_timeline_get_pad_for_track (GESTimeline * timeline, GESTrack * track)
3357 {
3358   GList *tmp;
3359
3360   LOCK_DYN (timeline);
3361   for (tmp = timeline->priv->priv_tracks; tmp; tmp = g_list_next (tmp)) {
3362     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
3363
3364     if (track == tr_priv->track) {
3365       if (tr_priv->ghostpad)
3366         gst_object_ref (tr_priv->ghostpad);
3367
3368       UNLOCK_DYN (timeline);
3369       return tr_priv->ghostpad;
3370     }
3371   }
3372   UNLOCK_DYN (timeline);
3373
3374   return NULL;
3375 }
3376
3377 /**
3378  * ges_timeline_get_tracks:
3379  * @timeline: a #GESTimeline
3380  *
3381  * Returns the list of #GESTrack used by the Timeline.
3382  *
3383  * Returns: (transfer full) (element-type GESTrack): A list of #GESTrack.
3384  * The caller should unref each track once he is done with them.
3385  */
3386 GList *
3387 ges_timeline_get_tracks (GESTimeline * timeline)
3388 {
3389   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
3390   CHECK_THREAD (timeline);
3391
3392   return g_list_copy_deep (timeline->tracks, (GCopyFunc) gst_object_ref, NULL);
3393 }
3394
3395 /**
3396  * ges_timeline_get_layers:
3397  * @timeline: a #GESTimeline
3398  *
3399  * Get the list of #GESLayer present in the Timeline.
3400  *
3401  * Returns: (transfer full) (element-type GESLayer): the list of
3402  * #GESLayer present in the Timeline sorted by priority.
3403  * The caller should unref each Layer once he is done with them.
3404  */
3405 GList *
3406 ges_timeline_get_layers (GESTimeline * timeline)
3407 {
3408   GList *tmp, *res = NULL;
3409
3410   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
3411   CHECK_THREAD (timeline);
3412
3413   for (tmp = timeline->layers; tmp; tmp = g_list_next (tmp)) {
3414     res = g_list_insert_sorted (res, gst_object_ref (tmp->data),
3415         (GCompareFunc) sort_layers);
3416   }
3417
3418   return res;
3419 }
3420
3421 static void
3422 track_commited_cb (GESTrack * track, GESTimeline * timeline)
3423 {
3424   gboolean emit_commited = FALSE;
3425   GST_OBJECT_LOCK (timeline);
3426   timeline->priv->expected_commited -= 1;
3427   if (timeline->priv->expected_commited == 0)
3428     emit_commited = TRUE;
3429   g_signal_handlers_disconnect_by_func (track, track_commited_cb, timeline);
3430   GST_OBJECT_UNLOCK (timeline);
3431
3432   if (emit_commited) {
3433     g_signal_emit (timeline, ges_timeline_signals[COMMITED], 0);
3434   }
3435 }
3436
3437 /* Must be called with the timeline's DYN_LOCK */
3438 static gboolean
3439 ges_timeline_commit_unlocked (GESTimeline * timeline)
3440 {
3441   GList *tmp;
3442   gboolean res = TRUE;
3443
3444   GST_DEBUG_OBJECT (timeline, "commiting changes");
3445
3446   for (tmp = timeline->layers; tmp; tmp = tmp->next) {
3447     GESLayer *layer = tmp->data;
3448
3449     _create_transitions_on_layer (timeline, layer, NULL, NULL,
3450         _find_transition_from_auto_transitions);
3451
3452     /* Ensure clip priorities are correct after an edit */
3453     ges_layer_resync_priorities (layer);
3454   }
3455
3456   timeline->priv->expected_commited =
3457       g_list_length (timeline->priv->priv_tracks);
3458
3459   if (timeline->priv->expected_commited == 0) {
3460     g_signal_emit (timeline, ges_timeline_signals[COMMITED], 0);
3461   } else {
3462     for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
3463       g_signal_connect (tmp->data, "commited", G_CALLBACK (track_commited_cb),
3464           timeline);
3465       if (!ges_track_commit (GES_TRACK (tmp->data)))
3466         res = FALSE;
3467     }
3468   }
3469
3470   /* Make sure we reset the context */
3471   timeline->priv->movecontext.needs_move_ctx = TRUE;
3472
3473   return res;
3474 }
3475
3476 /**
3477  * ges_timeline_commit:
3478  * @timeline: a #GESTimeline
3479  *
3480  * Commit all the pending changes of the clips contained in the
3481  * @timeline.
3482  *
3483  * When changes happen in a timeline, they are not
3484  * directly executed in the non-linear engine. Call this method once you are
3485  * done with a set of changes and want it to be executed.
3486  *
3487  * The #GESTimeline::commited signal will be emitted when the (possibly updated)
3488  * #GstPipeline is ready to output data again, except if the state of the
3489  * timeline was #GST_STATE_READY or #GST_STATE_NULL.
3490  *
3491  * Note that all the pending changes will automatically be executed when the
3492  * timeline goes from #GST_STATE_READY to #GST_STATE_PAUSED, which usually is
3493  * triggered by corresponding state changes in a containing #GESPipeline.
3494  *
3495  * You should not try to change the state of the timeline, seek it or add
3496  * tracks to it during a commit operation, that is between a call to this
3497  * function and after receiving the #GESTimeline::commited signal.
3498  *
3499  * See #ges_timeline_commit_sync if you don't want to bother with waiting
3500  * for the signal.
3501  *
3502  * Returns: %TRUE if pending changes were commited or %FALSE if nothing needed
3503  * to be commited
3504  */
3505 gboolean
3506 ges_timeline_commit (GESTimeline * timeline)
3507 {
3508   gboolean ret;
3509
3510   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3511
3512   LOCK_DYN (timeline);
3513   ret = ges_timeline_commit_unlocked (timeline);
3514   UNLOCK_DYN (timeline);
3515
3516   ges_timeline_emit_snappig (timeline, NULL, NULL);
3517   return ret;
3518 }
3519
3520 static void
3521 commited_cb (GESTimeline * timeline)
3522 {
3523   g_mutex_lock (&timeline->priv->commited_lock);
3524   g_cond_signal (&timeline->priv->commited_cond);
3525   g_mutex_unlock (&timeline->priv->commited_lock);
3526 }
3527
3528 /**
3529  * ges_timeline_commit_sync:
3530  * @timeline: a #GESTimeline
3531  *
3532  * Commit all the pending changes of the #GESClips contained in the
3533  * @timeline.
3534  *
3535  * Will return once the update is complete, that is when the
3536  * (possibly updated) #GstPipeline is ready to output data again, or if the
3537  * state of the timeline was #GST_STATE_READY or #GST_STATE_NULL.
3538  *
3539  * This function will wait for any pending state change of the timeline by
3540  * calling #gst_element_get_state with a #GST_CLOCK_TIME_NONE timeout, you
3541  * should not try to change the state from another thread before this function
3542  * has returned.
3543  *
3544  * See #ges_timeline_commit for more information.
3545  *
3546  * Returns: %TRUE if pending changes were commited or %FALSE if nothing needed
3547  * to be commited
3548  */
3549 gboolean
3550 ges_timeline_commit_sync (GESTimeline * timeline)
3551 {
3552   gboolean ret;
3553   gboolean wait_for_signal;
3554
3555   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3556
3557   /* Let's make sure our state is stable */
3558   gst_element_get_state (GST_ELEMENT (timeline), NULL, NULL,
3559       GST_CLOCK_TIME_NONE);
3560
3561   /* Let's make sure no track gets added between now and the actual commiting */
3562   LOCK_DYN (timeline);
3563   wait_for_signal = g_list_length (timeline->priv->priv_tracks) > 0
3564       && GST_STATE (timeline) >= GST_STATE_PAUSED;
3565
3566   if (!wait_for_signal) {
3567     ret = ges_timeline_commit_unlocked (timeline);
3568   } else {
3569     gulong handler_id =
3570         g_signal_connect (timeline, "commited", (GCallback) commited_cb, NULL);
3571
3572     g_mutex_lock (&timeline->priv->commited_lock);
3573
3574     ret = ges_timeline_commit_unlocked (timeline);
3575     g_cond_wait (&timeline->priv->commited_cond,
3576         &timeline->priv->commited_lock);
3577     g_mutex_unlock (&timeline->priv->commited_lock);
3578     g_signal_handler_disconnect (timeline, handler_id);
3579   }
3580
3581   UNLOCK_DYN (timeline);
3582
3583   return ret;
3584 }
3585
3586 /**
3587  * ges_timeline_get_duration:
3588  * @timeline: a #GESTimeline
3589  *
3590  * Get the current duration of @timeline
3591  *
3592  * Returns: The current duration of @timeline
3593  */
3594 GstClockTime
3595 ges_timeline_get_duration (GESTimeline * timeline)
3596 {
3597   g_return_val_if_fail (GES_IS_TIMELINE (timeline), GST_CLOCK_TIME_NONE);
3598   CHECK_THREAD (timeline);
3599
3600   return timeline->priv->duration;
3601 }
3602
3603 /**
3604  * ges_timeline_get_auto_transition:
3605  * @timeline: a #GESTimeline
3606  *
3607  * Gets whether transitions are automatically added when objects
3608  * overlap or not.
3609  *
3610  * Returns: %TRUE if transitions are automatically added, else %FALSE.
3611  */
3612 gboolean
3613 ges_timeline_get_auto_transition (GESTimeline * timeline)
3614 {
3615   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3616   CHECK_THREAD (timeline);
3617
3618   return timeline->priv->auto_transition;
3619 }
3620
3621 /**
3622  * ges_timeline_set_auto_transition:
3623  * @timeline: a #GESLayer
3624  * @auto_transition: whether the auto_transition is active
3625  *
3626  * Sets the layer to the given @auto_transition. See the documentation of the
3627  * property auto_transition for more information.
3628  */
3629 void
3630 ges_timeline_set_auto_transition (GESTimeline * timeline,
3631     gboolean auto_transition)
3632 {
3633   GList *layers;
3634   GESLayer *layer;
3635
3636   g_return_if_fail (GES_IS_TIMELINE (timeline));
3637   CHECK_THREAD (timeline);
3638
3639   timeline->priv->auto_transition = auto_transition;
3640   g_object_notify (G_OBJECT (timeline), "auto-transition");
3641
3642   layers = timeline->layers;
3643   for (; layers; layers = layers->next) {
3644     layer = layers->data;
3645     ges_layer_set_auto_transition (layer, auto_transition);
3646   }
3647 }
3648
3649 /**
3650  * ges_timeline_get_snapping_distance:
3651  * @timeline: a #GESTimeline
3652  *
3653  * Gets the configured snapping distance of the timeline. See
3654  * the documentation of the property snapping_distance for more
3655  * information.
3656  *
3657  * Returns: The @snapping_distance property of the timeline
3658  */
3659 GstClockTime
3660 ges_timeline_get_snapping_distance (GESTimeline * timeline)
3661 {
3662   g_return_val_if_fail (GES_IS_TIMELINE (timeline), GST_CLOCK_TIME_NONE);
3663   CHECK_THREAD (timeline);
3664
3665   return timeline->priv->snapping_distance;
3666
3667 }
3668
3669 /**
3670  * ges_timeline_set_snapping_distance:
3671  * @timeline: a #GESLayer
3672  * @snapping_distance: whether the snapping_distance is active
3673  *
3674  * Sets the @snapping_distance of the timeline. See the documentation of the
3675  * property snapping_distance for more information.
3676  */
3677 void
3678 ges_timeline_set_snapping_distance (GESTimeline * timeline,
3679     GstClockTime snapping_distance)
3680 {
3681   g_return_if_fail (GES_IS_TIMELINE (timeline));
3682   CHECK_THREAD (timeline);
3683
3684   timeline->priv->snapping_distance = snapping_distance;
3685 }
3686
3687 /**
3688  * ges_timeline_get_element:
3689  * @timeline: a #GESTimeline
3690  *
3691  * Gets a #GESTimelineElement contained in the timeline
3692  *
3693  * Returns: (transfer full) (nullable): The #GESTimelineElement or %NULL if
3694  * not found.
3695  */
3696 GESTimelineElement *
3697 ges_timeline_get_element (GESTimeline * timeline, const gchar * name)
3698 {
3699   GESTimelineElement *ret;
3700
3701   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
3702   CHECK_THREAD (timeline);
3703
3704   ret = g_hash_table_lookup (timeline->priv->all_elements, name);
3705
3706   if (ret)
3707     return gst_object_ref (ret);
3708
3709 #ifndef GST_DISABLE_GST_DEBUG
3710   {
3711     GList *element_names, *tmp;
3712     element_names = g_hash_table_get_keys (timeline->priv->all_elements);
3713
3714     GST_INFO_OBJECT (timeline, "Does not contain element %s", name);
3715
3716     for (tmp = element_names; tmp; tmp = tmp->next) {
3717       GST_DEBUG_OBJECT (timeline, "Containes: %s", (gchar *) tmp->data);
3718     }
3719     g_list_free (element_names);
3720   }
3721 #endif
3722
3723   return NULL;
3724 }
3725
3726 /**
3727  * ges_timeline_is_empty:
3728  * @timeline: a #GESTimeline
3729  *
3730  * Check whether a #GESTimeline is empty or not
3731  *
3732  * Returns: %TRUE if the timeline is empty %FALSE otherwize
3733  */
3734 gboolean
3735 ges_timeline_is_empty (GESTimeline * timeline)
3736 {
3737   GHashTableIter iter;
3738   gpointer key, value;
3739
3740   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3741   CHECK_THREAD (timeline);
3742
3743   if (g_hash_table_size (timeline->priv->all_elements) == 0)
3744     return TRUE;
3745
3746   g_hash_table_iter_init (&iter, timeline->priv->all_elements);
3747   while (g_hash_table_iter_next (&iter, &key, &value)) {
3748     if (GES_IS_SOURCE (value) &&
3749         ges_track_element_is_active (GES_TRACK_ELEMENT (value)))
3750       return FALSE;
3751   }
3752
3753   return TRUE;
3754 }
3755
3756 /**
3757  * ges_timeline_get_layer:
3758  * @timeline: The #GESTimeline to retrive a layer from
3759  * @priority: The priority of the layer to find
3760  *
3761  * Retrieve the layer with @priority as a priority
3762  *
3763  * Returns: (transfer full) (nullable): A #GESLayer or %NULL if no layer with
3764  * @priority was found
3765  *
3766  * Since 1.6
3767  */
3768 GESLayer *
3769 ges_timeline_get_layer (GESTimeline * timeline, guint priority)
3770 {
3771   GList *tmp;
3772   GESLayer *layer = NULL;
3773
3774   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
3775   CHECK_THREAD (timeline);
3776
3777   for (tmp = timeline->layers; tmp; tmp = tmp->next) {
3778     GESLayer *tmp_layer = GES_LAYER (tmp->data);
3779     guint tmp_priority;
3780
3781     g_object_get (tmp_layer, "priority", &tmp_priority, NULL);
3782     if (tmp_priority == priority) {
3783       layer = gst_object_ref (tmp_layer);
3784       break;
3785     }
3786   }
3787
3788   return layer;
3789 }
3790
3791 /**
3792  * ges_timeline_paste_element:
3793  * @timeline: The #GESTimeline onto which the #GESTimelineElement should be pasted
3794  * @element: The #GESTimelineElement to paste
3795  * @position: The position in the timeline the element should
3796  * be pasted to, meaning it will become the start of @element
3797  * @layer_priority: The #GESLayer to which the element should be pasted to.
3798  * -1 means paste to the same layer from which the @element has been copied from.
3799  *
3800  * Paste @element inside the timeline. @element must have been
3801  * created using ges_timeline_element_copy with deep=TRUE set,
3802  * i.e. it must be a deep copy, otherwise it will fail.
3803  *
3804  * Returns: (transfer none): Shallow copy of the @element pasted
3805  */
3806 GESTimelineElement *
3807 ges_timeline_paste_element (GESTimeline * timeline,
3808     GESTimelineElement * element, GstClockTime position, gint layer_priority)
3809 {
3810   GESTimelineElement *res, *copied_from;
3811   GESTimelineElementClass *element_class;
3812
3813   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3814   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (element), FALSE);
3815   CHECK_THREAD (timeline);
3816
3817   element_class = GES_TIMELINE_ELEMENT_GET_CLASS (element);
3818   copied_from = ges_timeline_element_get_copied_from (element);
3819
3820   if (!copied_from) {
3821     GST_ERROR_OBJECT (element, "Is not being 'deeply' copied!");
3822
3823     return NULL;
3824   }
3825
3826   if (!element_class->paste) {
3827     GST_ERROR_OBJECT (element, "No paste vmethod implemented");
3828
3829     return NULL;
3830   }
3831
3832   /*
3833    * Currently the API only supports pasting onto the same layer from which
3834    * the @element has been copied from, i.e., @layer_priority needs to be -1.
3835    */
3836   if (layer_priority != -1) {
3837     GST_WARNING_OBJECT (timeline,
3838         "Only -1 value for layer priority is supported");
3839   }
3840
3841   res = element_class->paste (element, copied_from, position);
3842
3843   g_clear_object (&copied_from);
3844
3845   return g_object_ref (res);
3846 }
3847
3848 /**
3849  * ges_timeline_move_layer:
3850  * @timeline: The timeline in which @layer must be
3851  * @layer: The layer to move at @new_layer_priority
3852  * @new_layer_priority: The index at which @layer should land
3853  *
3854  * Moves @layer at @new_layer_priority meaning that @layer
3855  * we land at that position in the stack of layers inside
3856  * the timeline. If @new_layer_priority is superior than the number
3857  * of layers present in the time, it will move to the end of the
3858  * stack of layers.
3859  */
3860 gboolean
3861 ges_timeline_move_layer (GESTimeline * timeline, GESLayer * layer,
3862     guint new_layer_priority)
3863 {
3864   gint current_priority;
3865
3866   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3867   g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
3868   g_return_val_if_fail (ges_layer_get_timeline (layer) == timeline, FALSE);
3869   CHECK_THREAD (timeline);
3870
3871   current_priority = ges_layer_get_priority (layer);
3872
3873   if (new_layer_priority == current_priority) {
3874     GST_DEBUG_OBJECT (timeline,
3875         "Nothing to do for %" GST_PTR_FORMAT ", same priorities", layer);
3876
3877     return TRUE;
3878   }
3879
3880   timeline->layers = g_list_remove (timeline->layers, layer);
3881   timeline->layers = g_list_insert (timeline->layers, layer,
3882       (gint) new_layer_priority);
3883
3884   _resync_layers (timeline);
3885
3886   return TRUE;
3887 }