timeline-layer: Reorganize file
[platform/upstream/gstreamer.git] / ges / ges-timeline-layer.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
3  *               2009 Nokia Corporation
4  *               2011 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:ges-timeline-layer
24  * @short_description: Non-overlapping sequence of #GESTimelineObject
25  *
26  * Responsible for the ordering of the various contained TimelineObject(s). A
27  * timeline layer has a "priority" property, which is used to manage the
28  * priorities of individual TimelineObjects. Two layers should not have the
29  * same priority within a given timeline.
30  */
31
32 #include "ges-internal.h"
33 #include "gesmarshal.h"
34 #include "ges-timeline-layer.h"
35 #include "ges.h"
36 #include "ges-timeline-source.h"
37
38 #define LAYER_HEIGHT 1000
39
40 static void
41 timeline_object_height_changed_cb (GESTimelineObject * obj,
42     GESTrackEffect * tr_eff, GESTimelineObject * second_obj);
43
44 G_DEFINE_TYPE (GESTimelineLayer, ges_timeline_layer, G_TYPE_INITIALLY_UNOWNED);
45
46 struct _GESTimelineLayerPrivate
47 {
48   /*< private > */
49   GList *objects_start;         /* The TimelineObjects sorted by start and
50                                  * priority */
51
52   guint32 priority;             /* The priority of the layer within the
53                                  * containing timeline */
54   gboolean auto_transition;
55 };
56
57 enum
58 {
59   PROP_0,
60   PROP_PRIORITY,
61   PROP_AUTO_TRANSITION,
62   PROP_LAST
63 };
64
65 enum
66 {
67   OBJECT_ADDED,
68   OBJECT_REMOVED,
69   LAST_SIGNAL
70 };
71
72 static guint ges_timeline_layer_signals[LAST_SIGNAL] = { 0 };
73
74 /* GObject standard vmethods */
75 static void
76 ges_timeline_layer_get_property (GObject * object, guint property_id,
77     GValue * value, GParamSpec * pspec)
78 {
79   GESTimelineLayer *layer = GES_TIMELINE_LAYER (object);
80
81   switch (property_id) {
82     case PROP_PRIORITY:
83       g_value_set_uint (value, layer->priv->priority);
84       break;
85     case PROP_AUTO_TRANSITION:
86       g_value_set_boolean (value, layer->priv->auto_transition);
87       break;
88     default:
89       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
90   }
91 }
92
93 static void
94 ges_timeline_layer_set_property (GObject * object, guint property_id,
95     const GValue * value, GParamSpec * pspec)
96 {
97   GESTimelineLayer *layer = GES_TIMELINE_LAYER (object);
98
99   switch (property_id) {
100     case PROP_PRIORITY:
101       ges_timeline_layer_set_priority (layer, g_value_get_uint (value));
102       break;
103     case PROP_AUTO_TRANSITION:
104       ges_timeline_layer_set_auto_transition (layer,
105           g_value_get_boolean (value));
106       break;
107     default:
108       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
109   }
110 }
111
112 static void
113 ges_timeline_layer_dispose (GObject * object)
114 {
115   GESTimelineLayer *layer = GES_TIMELINE_LAYER (object);
116   GESTimelineLayerPrivate *priv = layer->priv;
117
118   GST_DEBUG ("Disposing layer");
119
120   while (priv->objects_start)
121     ges_timeline_layer_remove_object (layer,
122         (GESTimelineObject *) priv->objects_start->data);
123
124   G_OBJECT_CLASS (ges_timeline_layer_parent_class)->dispose (object);
125 }
126
127 static void
128 ges_timeline_layer_class_init (GESTimelineLayerClass * klass)
129 {
130   GObjectClass *object_class = G_OBJECT_CLASS (klass);
131
132   g_type_class_add_private (klass, sizeof (GESTimelineLayerPrivate));
133
134   object_class->get_property = ges_timeline_layer_get_property;
135   object_class->set_property = ges_timeline_layer_set_property;
136   object_class->dispose = ges_timeline_layer_dispose;
137
138   /**
139    * GESTimelineLayer:priority
140    *
141    * The priority of the layer in the #GESTimeline. 0 is the highest
142    * priority. Conceptually, a #GESTimeline is a stack of GESTimelineLayers,
143    * and the priority of the layer represents its position in the stack. Two
144    * layers should not have the same priority within a given GESTimeline.
145    */
146   g_object_class_install_property (object_class, PROP_PRIORITY,
147       g_param_spec_uint ("priority", "Priority",
148           "The priority of the layer", 0, G_MAXUINT, 0, G_PARAM_READWRITE));
149
150   /**
151    * GESTimelineLayer:auto-transition
152    *
153    * Sets whether transitions are added automagically when timeline objects overlap.
154    */
155   g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
156       g_param_spec_boolean ("auto-transition", "Auto-Transition",
157           "whether the transitions are added", FALSE, G_PARAM_READWRITE));
158
159   /**
160    * GESTimelineLayer::object-added
161    * @layer: the #GESTimelineLayer
162    * @object: the #GESTimelineObject that was added.
163    *
164    * Will be emitted after the object was added to the layer.
165    */
166   ges_timeline_layer_signals[OBJECT_ADDED] =
167       g_signal_new ("object-added", G_TYPE_FROM_CLASS (klass),
168       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineLayerClass, object_added),
169       NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
170       GES_TYPE_TIMELINE_OBJECT);
171
172   /**
173    * GESTimelineLayer::object-removed
174    * @layer: the #GESTimelineLayer
175    * @object: the #GESTimelineObject that was removed
176    *
177    * Will be emitted after the object was removed from the layer.
178    */
179   ges_timeline_layer_signals[OBJECT_REMOVED] =
180       g_signal_new ("object-removed", G_TYPE_FROM_CLASS (klass),
181       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineLayerClass,
182           object_removed), NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
183       GES_TYPE_TIMELINE_OBJECT);
184 }
185
186 static void
187 ges_timeline_layer_init (GESTimelineLayer * self)
188 {
189   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
190       GES_TYPE_TIMELINE_LAYER, GESTimelineLayerPrivate);
191
192   self->priv->priority = 0;
193   self->priv->auto_transition = FALSE;
194   self->min_gnl_priority = 0;
195   self->max_gnl_priority = LAYER_HEIGHT;
196 }
197
198 /* Private methods and utils */
199 static gint
200 objects_start_compare (GESTimelineObject * a, GESTimelineObject * b)
201 {
202   if (a->start == b->start) {
203     if (a->priority < b->priority)
204       return -1;
205     if (a->priority > b->priority)
206       return 1;
207     return 0;
208   }
209   if (a->start < b->start)
210     return -1;
211   if (a->start > b->start)
212     return 1;
213   return 0;
214 }
215
216 static GList *
217 track_get_by_layer (GESTimelineLayer * layer, GESTrack * track)
218 {
219   GList *tck_objects_list = NULL, *tmp = NULL, *return_list = NULL;
220   GESTimelineObject *tl_obj;
221
222   tck_objects_list = ges_track_get_objects (track);
223   for (tmp = tck_objects_list; tmp; tmp = tmp->next) {
224     tl_obj = ges_track_object_get_timeline_object (tmp->data);
225
226     if (ges_timeline_object_get_layer (tl_obj) == layer) {
227       /*  We steal the reference from tck_objects_list */
228       return_list = g_list_append (return_list, tmp->data);
229
230     } else
231       g_object_unref (tmp->data);
232   }
233
234   return return_list;
235 }
236
237 /* Compare:
238  * @compared: The #GList of #GESTrackObjects that we compare with @track_object
239  * @track_object: The #GESTrackObject that serves as a reference
240  * @ahead: %TRUE if we are comparing frontward %FALSE if we are comparing
241  * backward*/
242 static void
243 compare (GList * compared, GESTrackObject * track_object, gboolean ahead)
244 {
245   GList *tmp;
246   gint64 start, duration, compared_start, compared_duration, end, compared_end,
247       tr_start, tr_duration;
248   GESTimelineStandardTransition *trans = NULL;
249   GESTrack *track;
250   GESTimelineLayer *layer;
251   GESTimelineObject *object, *compared_object, *first_object, *second_object;
252   gint priority;
253
254   g_return_if_fail (compared);
255
256   GST_DEBUG ("Recalculating transitions");
257
258   object = ges_track_object_get_timeline_object (track_object);
259
260   if (!object) {
261     GST_WARNING ("Trackobject not in a timeline object: "
262         "Can not calulate transitions");
263
264     return;
265   }
266
267   compared_object = ges_track_object_get_timeline_object (compared->data);
268   layer = ges_timeline_object_get_layer (object);
269
270   start = ges_track_object_get_start (track_object);
271   duration = ges_track_object_get_duration (track_object);
272   compared_start = ges_track_object_get_start (compared->data);
273   compared_duration = ges_track_object_get_duration (compared->data);
274   end = start + duration;
275   compared_end = compared_start + compared_duration;
276
277   if (ahead) {
278     /* Make sure we remove the last transition we created it is not needed
279      * FIXME make it a smarter way */
280     if (compared->prev && GES_IS_TRACK_TRANSITION (compared->prev->data)) {
281       trans = GES_TIMELINE_STANDARD_TRANSITION
282           (ges_track_object_get_timeline_object (compared->prev->data));
283       g_object_get (compared->prev->data, "start", &tr_start, "duration",
284           &tr_duration, NULL);
285       if (tr_start >= compared_start && tr_start + tr_duration <= compared_end)
286         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
287       trans = NULL;
288     }
289
290     for (tmp = compared->next; tmp; tmp = tmp->next) {
291       /* If we have a transitionmnmnm we recaluculuculate its values */
292       if (GES_IS_TRACK_TRANSITION (tmp->data)) {
293         g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
294             NULL);
295
296         if (tr_start + tr_duration == compared_start + compared_duration) {
297           GESTimelineObject *tlobj;
298           tlobj = ges_track_object_get_timeline_object (tmp->data);
299
300           trans = GES_TIMELINE_STANDARD_TRANSITION (tlobj);
301           break;
302         }
303       }
304     }
305
306     if (compared_end <= start) {
307       if (trans) {
308         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
309         g_object_get (compared_object, "priority", &priority, NULL);
310         g_object_set (object, "priority", priority, NULL);
311       }
312
313       goto clean;
314     } else if (start > compared_start && end < compared_end) {
315       if (trans) {
316         /* Transition not needed anymore */
317         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
318       }
319       goto clean;
320     } else if (start <= compared_start) {
321       if (trans) {
322         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
323       }
324
325       goto clean;
326     }
327
328   } else {
329     if (compared->next && GES_IS_TRACK_TRANSITION (compared->next->data)) {
330       trans = GES_TIMELINE_STANDARD_TRANSITION
331           (ges_track_object_get_timeline_object (compared->next->data));
332       g_object_get (compared->prev->data, "start", &tr_start, "duration",
333           &tr_duration, NULL);
334       if (tr_start >= compared_start && tr_start + tr_duration <= compared_end)
335         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
336       trans = NULL;
337     }
338     for (tmp = compared->prev; tmp; tmp = tmp->prev) {
339       if GES_IS_TRACK_TRANSITION
340         (tmp->data) {
341         g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
342             NULL);
343         if (tr_start == compared_start) {
344           trans = GES_TIMELINE_STANDARD_TRANSITION
345               (ges_track_object_get_timeline_object (tmp->data));
346           break;
347         }
348         }
349     }
350
351     if (start + duration <= compared_start) {
352       if (trans) {
353         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
354         g_object_get (object, "priority", &priority, NULL);
355         g_object_set (compared_object, "priority", priority, NULL);
356       }
357       goto clean;
358
359     } else if (start > compared_start) {
360       if (trans)
361         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
362
363       goto clean;
364     } else if (start < compared_start && end > compared_end) {
365       if (trans) {
366         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
367       }
368
369       goto clean;
370     }
371   }
372
373   if (!trans) {
374     gint height;
375
376     trans =
377         ges_timeline_standard_transition_new_for_nick ((gchar *) "crossfade");
378     track = ges_track_object_get_track (track_object);
379
380     ges_timeline_object_set_supported_formats (GES_TIMELINE_OBJECT (trans),
381         track->type);
382
383     ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (trans));
384
385     if (ahead) {
386       first_object = ges_track_object_get_timeline_object (compared->data);
387       second_object = object;
388     } else {
389       second_object = ges_track_object_get_timeline_object (compared->data);
390       first_object = object;
391     }
392
393     g_object_get (first_object, "priority", &priority, "height", &height, NULL);
394     g_object_set (second_object, "priority", priority + height, NULL);
395     g_signal_connect (first_object, "notify::height",
396         (GCallback) timeline_object_height_changed_cb, second_object);
397   }
398
399   if (ahead) {
400     g_object_set (trans, "start", start, "duration",
401         compared_duration + compared_start - start, NULL);
402   } else {
403     g_object_set (trans, "start", compared_start, "duration",
404         start + duration - compared_start, NULL);
405   }
406
407 clean:
408   g_object_unref (layer);
409 }
410
411 static void
412 calculate_next_transition_with_list (GESTrackObject * track_object,
413     GList * tckobjs_in_layer, GESTimelineLayer * layer)
414 {
415   GList *compared;
416
417   if (!(compared = g_list_find (tckobjs_in_layer, track_object)))
418     return;
419
420   if (compared == NULL)
421     /* This is the last TrackObject of the Track */
422     return;
423
424   do {
425     compared = compared->next;
426     if (compared == NULL)
427       return;
428   } while (!GES_IS_TRACK_SOURCE (compared->data));
429
430   compare (compared, track_object, FALSE);
431 }
432
433
434 static void
435 calculate_next_transition (GESTrackObject * track_object,
436     GESTimelineLayer * layer)
437 {
438   GESTrack *track;
439   GList *tckobjs_in_layer;
440
441   if ((track = ges_track_object_get_track (track_object))) {
442     tckobjs_in_layer = track_get_by_layer (layer, track);
443     calculate_next_transition_with_list (track_object, tckobjs_in_layer, layer);
444
445     g_list_foreach (tckobjs_in_layer, (GFunc) g_object_unref, NULL);
446     g_list_free (tckobjs_in_layer);
447   }
448 }
449
450 static void
451 calculate_transitions (GESTrackObject * track_object)
452 {
453   GList *tckobjs_in_layer, *compared;
454   GESTimelineLayer *layer;
455   GESTimelineObject *tlobj;
456
457   GESTrack *track = ges_track_object_get_track (track_object);
458
459   if (track == NULL)
460     return;
461
462   tlobj = ges_track_object_get_timeline_object (track_object);
463   layer = ges_timeline_object_get_layer (tlobj);
464   tckobjs_in_layer = track_get_by_layer (layer, track);
465   if (!(compared = g_list_find (tckobjs_in_layer, track_object)))
466     return;
467   do {
468     compared = compared->prev;
469
470     if (compared == NULL) {
471       /* Nothing before, let's check after */
472       calculate_next_transition_with_list (track_object, tckobjs_in_layer,
473           layer);
474       goto done;
475
476     }
477   } while (!GES_IS_TRACK_SOURCE (compared->data));
478
479   compare (compared, track_object, TRUE);
480
481   calculate_next_transition_with_list (track_object, tckobjs_in_layer, layer);
482
483 done:
484   g_list_foreach (tckobjs_in_layer, (GFunc) g_object_unref, NULL);
485   g_list_free (tckobjs_in_layer);
486 }
487
488
489 static void
490 look_for_transition (GESTrackObject * track_object, GESTimelineLayer * layer)
491 {
492   GESTrack *track;
493   GList *track_objects, *tmp, *cur;
494
495   track = ges_track_object_get_track (track_object);
496
497   track_objects = ges_track_get_objects (track);
498
499   cur = g_list_find (track_objects, track_object);
500
501   for (tmp = cur->next; tmp; tmp = tmp->next) {
502     if (GES_IS_TRACK_SOURCE (tmp->data)) {
503       break;
504     }
505     if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
506         || GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
507       ges_timeline_layer_remove_object (layer,
508           ges_track_object_get_timeline_object (tmp->data));
509     }
510   }
511
512   for (tmp = cur->prev; tmp; tmp = tmp->prev) {
513     if (GES_IS_TRACK_SOURCE (tmp->data)) {
514       break;
515     }
516     if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
517         || GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
518       ges_timeline_layer_remove_object (layer,
519           ges_track_object_get_timeline_object (tmp->data));
520     }
521   }
522   g_list_foreach (track_objects, (GFunc) g_object_unref, NULL);
523   g_list_free (track_objects);
524 }
525
526 /**
527  * ges_timeline_layer_resync_priorities:
528  * @layer: a #GESTimelineLayer
529  *
530  * Resyncs the priorities of the objects controlled by @layer.
531  * This method
532  */
533 static gboolean
534 ges_timeline_layer_resync_priorities (GESTimelineLayer * layer)
535 {
536   GList *tmp;
537   GESTimelineObject *obj;
538
539   GST_DEBUG ("Resync priorities of %p", layer);
540
541   /* TODO : Inhibit composition updates while doing this.
542    * Ideally we want to do it from an even higher level, but here will
543    * do in the meantime. */
544
545   for (tmp = layer->priv->objects_start; tmp; tmp = tmp->next) {
546     obj = GES_TIMELINE_OBJECT (tmp->data);
547     ges_timeline_object_set_priority (obj, GES_TIMELINE_OBJECT_PRIORITY (obj));
548   }
549
550   return TRUE;
551 }
552
553 /* Callbacks */
554
555 static void
556 track_object_duration_cb (GESTrackObject * track_object,
557     GParamSpec * arg G_GNUC_UNUSED)
558 {
559   GESTimelineLayer *layer;
560   GESTimelineObject *tlobj;
561
562   tlobj = ges_track_object_get_timeline_object (track_object);
563   layer = ges_timeline_object_get_layer (tlobj);
564   if (G_LIKELY (GES_IS_TRACK_SOURCE (track_object)))
565     calculate_next_transition (track_object, layer);
566 }
567
568 static void
569 track_object_removed_cb (GESTrack * track, GESTrackObject * track_object)
570 {
571   GList *track_objects, *tmp, *cur;
572   GESTimelineLayer *layer;
573
574   track_objects = ges_track_get_objects (track);
575   cur = g_list_find (track_objects, track_object);
576   for (tmp = cur->next; tmp; tmp = tmp->next) {
577     if (GES_IS_TRACK_SOURCE (tmp->data)) {
578       break;
579     }
580     if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
581         || GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
582       layer =
583           ges_timeline_object_get_layer (ges_track_object_get_timeline_object
584           (tmp->data));
585       if (ges_timeline_layer_get_auto_transition (layer)) {
586         ges_track_enable_update (track, FALSE);
587         ges_timeline_layer_remove_object (layer,
588             ges_track_object_get_timeline_object (tmp->data));
589         ges_track_enable_update (track, TRUE);
590       }
591       g_object_unref (layer);
592     }
593   }
594
595   for (tmp = cur->prev; tmp; tmp = tmp->prev) {
596     if (GES_IS_TRACK_SOURCE (tmp->data)) {
597       break;
598     }
599     if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
600         || GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
601       layer =
602           ges_timeline_object_get_layer (ges_track_object_get_timeline_object
603           (tmp->data));
604       if (ges_timeline_layer_get_auto_transition (layer)) {
605         ges_track_enable_update (track, FALSE);
606         ges_timeline_layer_remove_object (layer,
607             ges_track_object_get_timeline_object (tmp->data));
608         ges_track_enable_update (track, TRUE);
609       }
610       g_object_unref (layer);
611     }
612   }
613   g_object_unref (track_object);
614 }
615
616 static void
617 track_object_changed_cb (GESTrackObject * track_object,
618     GParamSpec * arg G_GNUC_UNUSED)
619 {
620   if (G_LIKELY (GES_IS_TRACK_SOURCE (track_object)))
621     calculate_transitions (track_object);
622 }
623
624 static void
625 track_object_added_cb (GESTrack * track, GESTrackObject * track_object,
626     GESTimelineLayer * layer)
627 {
628   GST_ERROR ("TRACKOBJECTADDED %i", GES_IS_TRACK_SOURCE (track_object));
629   if (GES_IS_TRACK_SOURCE (track_object)) {
630     g_signal_connect (G_OBJECT (track_object), "notify::start",
631         G_CALLBACK (track_object_changed_cb), NULL);
632     g_signal_connect (G_OBJECT (track_object), "notify::duration",
633         G_CALLBACK (track_object_duration_cb), NULL);
634     calculate_transitions (track_object);
635   }
636
637 }
638
639 static void
640 track_removed_cb (GESTrack * track, GESTrackObject * track_object,
641     GESTimelineLayer * layer)
642 {
643   g_signal_handlers_disconnect_by_func (track, track_object_added_cb, layer);
644   g_signal_handlers_disconnect_by_func (track, track_object_removed_cb, NULL);
645 }
646
647 static void
648 track_added_cb (GESTrack * track, GESTrackObject * track_object,
649     GESTimelineLayer * layer)
650 {
651   g_signal_connect (track, "track-object-removed",
652       (GCallback) track_object_removed_cb, NULL);
653   g_signal_connect (track, "track-object-added",
654       (GCallback) track_object_added_cb, NULL);
655 }
656
657 static void
658 timeline_object_height_changed_cb (GESTimelineObject * obj,
659     GESTrackEffect * tr_eff, GESTimelineObject * second_obj)
660 {
661   gint priority, height;
662   g_object_get (obj, "height", &height, "priority", &priority, NULL);
663   g_object_set (second_obj, "priority", priority + height, NULL);
664 }
665
666 /* Public methods */
667 /**
668  * ges_timeline_layer_remove_object:
669  * @layer: a #GESTimelineLayer
670  * @object: the #GESTimelineObject to remove
671  *
672  * Removes the given @object from the @layer and unparents it.
673  * Unparenting it means the reference owned by @layer on the @object will be
674  * removed. If you wish to use the @object after this function, make sure you
675  * call g_object_ref() before removing it from the @layer.
676  *
677  * Returns: TRUE if the object could be removed, FALSE if the layer does
678  * not want to remove the object.
679  */
680 gboolean
681 ges_timeline_layer_remove_object (GESTimelineLayer * layer,
682     GESTimelineObject * object)
683 {
684   GESTimelineLayer *tl_obj_layer;
685   GList *trackobjects, *tmp;
686
687   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
688   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
689
690   GST_DEBUG ("layer:%p, object:%p", layer, object);
691
692   tl_obj_layer = ges_timeline_object_get_layer (object);
693   if (G_UNLIKELY (tl_obj_layer != layer)) {
694     GST_WARNING ("TimelineObject doesn't belong to this layer");
695     if (tl_obj_layer != NULL)
696       g_object_unref (tl_obj_layer);
697     return FALSE;
698   }
699   g_object_unref (tl_obj_layer);
700
701   if (layer->priv->auto_transition) {
702     trackobjects = ges_timeline_object_get_track_objects (object);
703
704     for (tmp = trackobjects; tmp; tmp = tmp->next) {
705       look_for_transition (tmp->data, layer);
706     }
707
708     g_list_foreach (trackobjects, (GFunc) g_object_unref, NULL);
709     g_list_free (trackobjects);
710   }
711
712   /* emit 'object-removed' */
713   g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_REMOVED], 0, object);
714
715   /* inform the object it's no longer in a layer */
716   ges_timeline_object_set_layer (object, NULL);
717
718   /* Remove it from our list of controlled objects */
719   layer->priv->objects_start =
720       g_list_remove (layer->priv->objects_start, object);
721
722   /* Remove our reference to the object */
723   g_object_unref (object);
724
725   return TRUE;
726 }
727
728 /**
729  * ges_timeline_layer_set_priority:
730  * @layer: a #GESTimelineLayer
731  * @priority: the priority to set
732  *
733  * Sets the layer to the given @priority. See the documentation of the
734  * priority property for more information.
735  */
736 void
737 ges_timeline_layer_set_priority (GESTimelineLayer * layer, guint priority)
738 {
739   g_return_if_fail (GES_IS_TIMELINE_LAYER (layer));
740
741   GST_DEBUG ("layer:%p, priority:%d", layer, priority);
742
743   if (priority != layer->priv->priority) {
744     layer->priv->priority = priority;
745     layer->min_gnl_priority = (priority * LAYER_HEIGHT);
746     layer->max_gnl_priority = ((priority + 1) * LAYER_HEIGHT) - 1;
747
748     ges_timeline_layer_resync_priorities (layer);
749   }
750 }
751
752 /**
753  * ges_timeline_layer_get_auto_transition:
754  * @layer: a #GESTimelineLayer
755  *
756  * Gets whether transitions are automatically added when objects
757  * overlap or not.
758  *
759  * Returns: %TRUE if transitions are automatically added, else %FALSE.
760  */
761 gboolean
762 ges_timeline_layer_get_auto_transition (GESTimelineLayer * layer)
763 {
764   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), 0);
765
766   return layer->priv->auto_transition;
767 }
768
769 /**
770  * ges_timeline_layer_set_auto_transition:
771  * @layer: a #GESTimelineLayer
772  * @auto_transition: whether the auto_transition is active
773  *
774  * Sets the layer to the given @auto_transition. See the documentation of the
775  * property auto_transition for more information.
776  */
777 void
778 ges_timeline_layer_set_auto_transition (GESTimelineLayer * layer,
779     gboolean auto_transition)
780 {
781
782   GList *tmp;
783   g_return_if_fail (GES_IS_TIMELINE_LAYER (layer));
784
785   if (auto_transition && layer->timeline) {
786     GList *tracks = ges_timeline_get_tracks (layer->timeline);
787
788     for (tmp = tracks; tmp; tmp = tmp->next) {
789       g_signal_connect (G_OBJECT (tmp->data), "track-object-added",
790           G_CALLBACK (track_object_added_cb), layer);
791       g_signal_connect (G_OBJECT (tmp->data), "track-object-removed",
792           G_CALLBACK (track_object_removed_cb), NULL);
793     }
794     /* FIXME calculate all the transitions at that time */
795   }
796   layer->priv->auto_transition = auto_transition;
797 }
798
799 /**
800  * ges_timeline_layer_get_priority:
801  * @layer: a #GESTimelineLayer
802  *
803  * Get the priority of @layer within the timeline.
804  *
805  * Returns: The priority of the @layer within the timeline.
806  */
807 guint
808 ges_timeline_layer_get_priority (GESTimelineLayer * layer)
809 {
810   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), 0);
811
812   return layer->priv->priority;
813 }
814
815 /**
816  * ges_timeline_layer_get_objects:
817  * @layer: a #GESTimelineLayer
818  *
819  * Get the timeline objects this layer contains.
820  *
821  * Returns: (transfer full) (element-type GESTimelineObject): a #GList of
822  * timeline objects. The user is responsible for
823  * unreffing the contained objects and freeing the list.
824  */
825
826 GList *
827 ges_timeline_layer_get_objects (GESTimelineLayer * layer)
828 {
829   GList *ret = NULL;
830   GList *tmp;
831   GESTimelineLayerClass *klass;
832
833   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), NULL);
834
835   klass = GES_TIMELINE_LAYER_GET_CLASS (layer);
836
837   if (klass->get_objects) {
838     return klass->get_objects (layer);
839   }
840
841   for (tmp = layer->priv->objects_start; tmp; tmp = tmp->next) {
842     ret = g_list_prepend (ret, tmp->data);
843     g_object_ref (tmp->data);
844   }
845
846   ret = g_list_reverse (ret);
847   return ret;
848 }
849
850 /**
851  * ges_timeline_layer_is_empty:
852  * @layer: The #GESTimelineLayer to check
853  *
854  * Convenience method to check if @layer is empty (doesn't contain any object),
855  * or not.
856  *
857  * Returns: %TRUE if @layer is empty, %FALSE if it already contains at least
858  * one #GESTimelineObject
859  */
860 gboolean
861 ges_timeline_layer_is_empty (GESTimelineLayer * layer)
862 {
863   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
864
865   return (layer->priv->objects_start == NULL);
866 }
867
868 /**
869  * ges_timeline_layer_add_object:
870  * @layer: a #GESTimelineLayer
871  * @object: (transfer full): the #GESTimelineObject to add.
872  *
873  * Adds the given object to the layer. Sets the object's parent, and thus
874  * takes ownership of the object.
875  *
876  * An object can only be added to one layer.
877  *
878  * Calling this method will construct and properly set all the media related
879  * elements on @object. If you need to know when those objects (actually #GESTrackObject)
880  * are constructed, you should connect to the object::track-object-added signal which
881  * is emited right after those elements are ready to be used.
882  *
883  * Returns: TRUE if the object was properly added to the layer, or FALSE
884  * if the @layer refuses to add the object.
885  */
886 gboolean
887 ges_timeline_layer_add_object (GESTimelineLayer * layer,
888     GESTimelineObject * object)
889 {
890   GESTimelineLayer *tl_obj_layer;
891   guint32 maxprio, minprio, prio;
892
893   GST_DEBUG ("layer:%p, object:%p", layer, object);
894
895   tl_obj_layer = ges_timeline_object_get_layer (object);
896
897   if (G_UNLIKELY (tl_obj_layer)) {
898     GST_WARNING ("TimelineObject %p already belongs to another layer", object);
899     g_object_unref (tl_obj_layer);
900     return FALSE;
901   }
902
903   g_object_ref_sink (object);
904
905   /* Take a reference to the object and store it stored by start/priority */
906   layer->priv->objects_start =
907       g_list_insert_sorted (layer->priv->objects_start, object,
908       (GCompareFunc) objects_start_compare);
909
910   /* Inform the object it's now in this layer */
911   ges_timeline_object_set_layer (object, layer);
912
913   GST_DEBUG ("current object priority : %d, layer min/max : %d/%d",
914       GES_TIMELINE_OBJECT_PRIORITY (object),
915       layer->min_gnl_priority, layer->max_gnl_priority);
916
917   /* Set the priority. */
918   maxprio = layer->max_gnl_priority;
919   minprio = layer->min_gnl_priority;
920   prio = GES_TIMELINE_OBJECT_PRIORITY (object);
921   if (minprio + prio > (maxprio)) {
922     GST_WARNING ("%p is out of the layer %p space, setting its priority to "
923         "setting its priority %d to failthe maximum priority of the layer %d",
924         object, layer, prio, maxprio - minprio);
925     ges_timeline_object_set_priority (object, LAYER_HEIGHT - 1);
926   }
927   /* If the object has an acceptable priority, we just let it with its current
928    * priority */
929
930   ges_timeline_layer_resync_priorities (layer);
931
932   /* emit 'object-added' */
933   g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_ADDED], 0, object);
934
935   return TRUE;
936 }
937
938 /**
939  * ges_timeline_layer_new:
940  *
941  * Creates a new #GESTimelineLayer.
942  *
943  * Returns: A new #GESTimelineLayer
944  */
945 GESTimelineLayer *
946 ges_timeline_layer_new (void)
947 {
948   return g_object_new (GES_TYPE_TIMELINE_LAYER, NULL);
949 }
950
951 /**
952  * ges_timeline_layer_get_timeline:
953  * @layer: The #GESTimelineLayer to get the parent #GESTimeline from
954  *
955  * Get the #GESTimeline in which #GESTimelineLayer currently is.
956  *
957  * Returns: (transfer none):  the #GESTimeline in which #GESTimelineLayer
958  * currently is or %NULL if not in any timeline yet.
959  */
960 GESTimeline *
961 ges_timeline_layer_get_timeline (GESTimelineLayer * layer)
962 {
963   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), NULL);
964
965   return layer->timeline;
966 }
967
968 void
969 ges_timeline_layer_set_timeline (GESTimelineLayer * layer,
970     GESTimeline * timeline)
971 {
972   GST_DEBUG ("layer:%p, timeline:%p", layer, timeline);
973
974   if (layer->priv->auto_transition == TRUE) {
975     if (layer->timeline != NULL) {
976       g_signal_handlers_disconnect_by_func (layer->timeline, track_added_cb,
977           layer);
978       g_signal_handlers_disconnect_by_func (layer->timeline, track_removed_cb,
979           layer);
980     }
981
982     if (timeline != NULL) {
983       GList *tmp, *tracks = ges_timeline_get_tracks (timeline);
984
985       g_signal_connect (timeline, "track-added", G_CALLBACK (track_added_cb),
986           layer);
987       g_signal_connect (timeline, "track-removed",
988           G_CALLBACK (track_removed_cb), layer);
989
990       for (tmp = tracks; tmp; tmp = tmp->next) {
991         g_signal_connect (G_OBJECT (tmp->data), "track-object-added",
992             G_CALLBACK (track_object_added_cb), layer);
993         g_signal_connect (G_OBJECT (tmp->data), "track-object-removed",
994             G_CALLBACK (track_object_removed_cb), NULL);
995       }
996     }
997   }
998
999   layer->timeline = timeline;
1000 }