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