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