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