Merge branch '0.10'
[platform/upstream/gstreamer.git] / ges / ges-timeline-object.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-object
23  * @short_description: Base Class for objects in a #GESTimelineLayer
24  *
25  * A #GESTimelineObject is a 'natural' object which controls one or more
26  * #GESTrackObject(s) in one or more #GESTrack(s).
27  *
28  * Keeps a reference to the #GESTrackObject(s) it created and
29  * sets/updates their properties.
30  */
31
32 #include "ges-timeline-object.h"
33 #include "ges.h"
34 #include "ges-internal.h"
35
36 #include <string.h>
37
38 gboolean
39 ges_timeline_object_fill_track_object_func (GESTimelineObject * object,
40     GESTrackObject * trackobj, GstElement * gnlobj);
41
42 gboolean
43 ges_timeline_object_create_track_objects_func (GESTimelineObject
44     * object, GESTrack * track);
45
46 void default_set_max_duration (GESTimelineObject * object, guint64 maxduration);
47
48 static void
49 track_object_start_changed_cb (GESTrackObject * child,
50     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object);
51 static void
52 track_object_inpoint_changed_cb (GESTrackObject * child,
53     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object);
54 static void
55 track_object_duration_changed_cb (GESTrackObject * child,
56     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object);
57 static void
58 track_object_priority_changed_cb (GESTrackObject * child,
59     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object);
60 static void update_height (GESTimelineObject * object);
61
62 void
63 tck_object_added_cb (GESTimelineObject * object,
64     GESTrackObject * track_object, GList * track_objects);
65
66 static gint sort_track_effects (gpointer a, gpointer b,
67     GESTimelineObject * object);
68 static void
69 get_layer_priorities (GESTimelineLayer * layer, guint32 * layer_min_gnl_prio,
70     guint32 * layer_max_gnl_prio);
71
72 static gboolean
73 ges_timeline_object_set_start_internal (GESTimelineObject * object,
74     guint64 start);
75 static gboolean ges_timeline_object_set_inpoint_internal (GESTimelineObject *
76     object, guint64 inpoint);
77 static gboolean ges_timeline_object_set_duration_internal (GESTimelineObject *
78     object, guint64 duration);
79 static gboolean ges_timeline_object_set_priority_internal (GESTimelineObject *
80     object, guint32 priority);
81
82 static GESTimelineObject *ges_timeline_object_copy (GESTimelineObject * object,
83     gboolean * deep);
84
85 G_DEFINE_ABSTRACT_TYPE (GESTimelineObject, ges_timeline_object,
86     G_TYPE_INITIALLY_UNOWNED);
87
88 /* Mapping of relationship between a TimelineObject and the TrackObjects
89  * it controls
90  *
91  * NOTE : how do we make this public in the future ?
92  */
93 typedef struct
94 {
95   GESTrackObject *object;
96   gint64 start_offset;
97   gint64 duration_offset;
98   gint64 inpoint_offset;
99   gint32 priority_offset;
100
101   guint start_notifyid;
102   guint duration_notifyid;
103   guint inpoint_notifyid;
104   guint priority_notifyid;
105
106   /* track mapping ?? */
107 } ObjectMapping;
108
109 enum
110 {
111   EFFECT_ADDED,
112   EFFECT_REMOVED,
113   TRACK_OBJECT_ADDED,
114   TRACK_OBJECT_REMOVED,
115   LAST_SIGNAL
116 };
117
118 static guint ges_timeline_object_signals[LAST_SIGNAL] = { 0 };
119
120 struct _GESTimelineObjectPrivate
121 {
122   /*< public > */
123   GESTimelineLayer *layer;
124
125   /*< private > */
126   /* A list of TrackObject controlled by this TimelineObject sorted by
127    * priority */
128   GList *trackobjects;
129
130   /* Set to TRUE when the timelineobject is doing updates of track object
131    * properties so we don't end up in infinite property update loops
132    */
133   gboolean ignore_notifies;
134   gboolean is_moving;
135
136   guint64 maxduration;
137
138   GList *mappings;
139
140   guint nb_effects;
141
142   GESTrackObject *initiated_move;
143
144   /* The formats supported by this TimelineObject */
145   GESTrackType supportedformats;
146 };
147
148 enum
149 {
150   PROP_0,
151   PROP_START,
152   PROP_INPOINT,
153   PROP_DURATION,
154   PROP_PRIORITY,
155   PROP_HEIGHT,
156   PROP_LAYER,
157   PROP_SUPPORTED_FORMATS,
158   PROP_MAX_DURATION,
159   PROP_LAST
160 };
161
162 static GParamSpec *properties[PROP_LAST];
163
164 static void
165 ges_timeline_object_get_property (GObject * object, guint property_id,
166     GValue * value, GParamSpec * pspec)
167 {
168   GESTimelineObject *tobj = GES_TIMELINE_OBJECT (object);
169
170   switch (property_id) {
171     case PROP_START:
172       g_value_set_uint64 (value, tobj->start);
173       break;
174     case PROP_INPOINT:
175       g_value_set_uint64 (value, tobj->inpoint);
176       break;
177     case PROP_DURATION:
178       g_value_set_uint64 (value, tobj->duration);
179       break;
180     case PROP_PRIORITY:
181       g_value_set_uint (value, tobj->priority);
182       break;
183     case PROP_HEIGHT:
184       g_value_set_uint (value, tobj->height);
185       break;
186     case PROP_LAYER:
187       g_value_set_object (value, tobj->priv->layer);
188       break;
189     case PROP_SUPPORTED_FORMATS:
190       g_value_set_flags (value, tobj->priv->supportedformats);
191       break;
192     case PROP_MAX_DURATION:
193       g_value_set_uint64 (value, tobj->priv->maxduration);
194       break;
195     default:
196       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
197   }
198 }
199
200 static void
201 ges_timeline_object_set_property (GObject * object, guint property_id,
202     const GValue * value, GParamSpec * pspec)
203 {
204   GESTimelineObject *tobj = GES_TIMELINE_OBJECT (object);
205
206   switch (property_id) {
207     case PROP_START:
208       ges_timeline_object_set_start_internal (tobj, g_value_get_uint64 (value));
209       break;
210     case PROP_INPOINT:
211       ges_timeline_object_set_inpoint_internal (tobj,
212           g_value_get_uint64 (value));
213       break;
214     case PROP_DURATION:
215       ges_timeline_object_set_duration_internal (tobj,
216           g_value_get_uint64 (value));
217       break;
218     case PROP_PRIORITY:
219       ges_timeline_object_set_priority_internal (tobj,
220           g_value_get_uint (value));
221       break;
222     case PROP_SUPPORTED_FORMATS:
223       ges_timeline_object_set_supported_formats (tobj,
224           g_value_get_flags (value));
225       break;
226     case PROP_MAX_DURATION:
227       ges_timeline_object_set_max_duration (tobj, g_value_get_uint64 (value));
228       break;
229     default:
230       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
231   }
232 }
233
234 static void
235 ges_timeline_object_class_init (GESTimelineObjectClass * klass)
236 {
237   GObjectClass *object_class = G_OBJECT_CLASS (klass);
238
239   g_type_class_add_private (klass, sizeof (GESTimelineObjectPrivate));
240
241   object_class->get_property = ges_timeline_object_get_property;
242   object_class->set_property = ges_timeline_object_set_property;
243   klass->create_track_objects = ges_timeline_object_create_track_objects_func;
244   klass->set_max_duration = default_set_max_duration;
245   klass->track_object_added = NULL;
246   klass->track_object_released = NULL;
247
248   /**
249    * GESTimelineObject:start
250    *
251    * The position of the object in the #GESTimelineLayer (in nanoseconds).
252    */
253   properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
254       "The position in the container", 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
255   g_object_class_install_property (object_class, PROP_START,
256       properties[PROP_START]);
257
258   /**
259    * GESTimelineObject:in-point
260    *
261    * The in-point at which this #GESTimelineObject will start outputting data
262    * from its contents (in nanoseconds).
263    *
264    * Ex : an in-point of 5 seconds means that the first outputted buffer will
265    * be the one located 5 seconds in the controlled resource.
266    */
267   properties[PROP_INPOINT] =
268       g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
269       G_MAXUINT64, 0, G_PARAM_READWRITE);
270   g_object_class_install_property (object_class, PROP_INPOINT,
271       properties[PROP_INPOINT]);
272
273   /**
274    * GESTimelineObject:duration
275    *
276    * The duration (in nanoseconds) which will be used in the container #GESTrack
277    * starting from 'in-point'.
278    */
279   properties[PROP_DURATION] =
280       g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
281       G_MAXUINT64, GST_CLOCK_TIME_NONE, G_PARAM_READWRITE);
282   g_object_class_install_property (object_class, PROP_DURATION,
283       properties[PROP_DURATION]);
284
285   /**
286    * GESTimelineObject:priority
287    *
288    * The layer priority of the timeline object.
289    */
290   properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
291       "The priority of the object", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
292   g_object_class_install_property (object_class, PROP_PRIORITY,
293       properties[PROP_PRIORITY]);
294
295   /**
296    * GESTimelineObject:height
297    *
298    * The span of layer priorities which this object occupies.
299    */
300   properties[PROP_HEIGHT] = g_param_spec_uint ("height", "Height",
301       "The span of priorities this object occupies", 0, G_MAXUINT, 1,
302       G_PARAM_READABLE);
303   g_object_class_install_property (object_class, PROP_HEIGHT,
304       properties[PROP_HEIGHT]);
305
306   /**
307    * GESTimelineObject:supported-formats:
308    *
309    * The formats supported by the object.
310    *
311    * Since: 0.10.XX
312    */
313   properties[PROP_SUPPORTED_FORMATS] = g_param_spec_flags ("supported-formats",
314       "Supported formats", "Formats supported by the file",
315       GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_UNKNOWN,
316       G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
317
318   g_object_class_install_property (object_class, PROP_SUPPORTED_FORMATS,
319       properties[PROP_SUPPORTED_FORMATS]);
320
321   /**
322    * GESTimelineObject:layer
323    *
324    * The GESTimelineLayer where this object is being used.
325    */
326   properties[PROP_LAYER] = g_param_spec_object ("layer", "Layer",
327       "The GESTimelineLayer where this object is being used.",
328       GES_TYPE_TIMELINE_LAYER, G_PARAM_READABLE);
329   g_object_class_install_property (object_class, PROP_LAYER,
330       properties[PROP_LAYER]);
331
332   /**
333    * GESTimelineObject::effect-added
334    * @object: the #GESTimelineObject
335    * @effect: the #GESTrackEffect that was added.
336    *
337    * Will be emitted after an effect was added to the object.
338    *
339    * Since: 0.10.2
340    */
341   ges_timeline_object_signals[EFFECT_ADDED] =
342       g_signal_new ("effect-added", G_TYPE_FROM_CLASS (klass),
343       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
344       G_TYPE_NONE, 1, GES_TYPE_TRACK_EFFECT);
345
346   /**
347    * GESTimelineObject::effect-removed
348    * @object: the #GESTimelineObject
349    * @effect: the #GESTrackEffect that was added.
350    *
351    * Will be emitted after an effect was remove from the object.
352    *
353    * Since: 0.10.2
354    */
355   ges_timeline_object_signals[EFFECT_REMOVED] =
356       g_signal_new ("effect-removed", G_TYPE_FROM_CLASS (klass),
357       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
358       G_TYPE_NONE, 1, GES_TYPE_TRACK_EFFECT);
359
360   /**
361    * GESTimelineObject::track-object-added
362    * @object: the #GESTimelineObject
363    * @tckobj: the #GESTrackObject that was added.
364    *
365    * Will be emitted after a track object was added to the object.
366    *
367    * Since: 0.10.2
368    */
369   ges_timeline_object_signals[TRACK_OBJECT_ADDED] =
370       g_signal_new ("track-object-added", G_TYPE_FROM_CLASS (klass),
371       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
372       G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
373
374   /**
375    * GESTimelineObject::track-object-removed
376    * @object: the #GESTimelineObject
377    * @tckobj: the #GESTrackObject that was removed.
378    *
379    * Will be emitted after a track object was removed from @object.
380    *
381    * Since: 0.10.2
382    */
383   ges_timeline_object_signals[TRACK_OBJECT_REMOVED] =
384       g_signal_new ("track-object-removed", G_TYPE_FROM_CLASS (klass),
385       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
386       G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
387
388   /**
389    * GESTimelineObject:max-duration:
390    *
391    * The maximum duration (in nanoseconds) of the #GESTimelineObject.
392    *
393    * Since: 0.10.XX
394    */
395   g_object_class_install_property (object_class, PROP_MAX_DURATION,
396       g_param_spec_uint64 ("max-duration", "Maximum duration",
397           "The duration of the object", 0, G_MAXUINT64, G_MAXUINT64,
398           G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
399
400   klass->need_fill_track = TRUE;
401 }
402
403 static void
404 ges_timeline_object_init (GESTimelineObject * self)
405 {
406   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
407       GES_TYPE_TIMELINE_OBJECT, GESTimelineObjectPrivate);
408   self->duration = GST_SECOND;
409   self->height = 1;
410   self->priv->trackobjects = NULL;
411   self->priv->layer = NULL;
412   self->priv->nb_effects = 0;
413   self->priv->is_moving = FALSE;
414   self->priv->maxduration = G_MAXUINT64;
415 }
416
417 /**
418  * ges_timeline_object_create_track_object:
419  * @object: The origin #GESTimelineObject
420  * @track: The #GESTrack to create a #GESTrackObject for.
421  *
422  * Creates a #GESTrackObject for the provided @track. The timeline object
423  * keep a reference to the newly created trackobject, you therefore need to
424  * call @ges_timeline_object_release_track_object when you are done with it.
425  *
426  * Returns: (transfer none): A #GESTrackObject. Returns NULL if the #GESTrackObject could not
427  * be created.
428  */
429
430 GESTrackObject *
431 ges_timeline_object_create_track_object (GESTimelineObject * object,
432     GESTrack * track)
433 {
434   GESTimelineObjectClass *class;
435   GESTrackObject *res;
436
437   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
438   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
439
440   class = GES_TIMELINE_OBJECT_GET_CLASS (object);
441
442   if (G_UNLIKELY (class->create_track_object == NULL)) {
443     GST_ERROR ("No 'create_track_object' implementation available");
444     return NULL;
445   }
446
447   res = class->create_track_object (object, track);
448   return res;
449
450 }
451
452 /**
453  * ges_timeline_object_create_track_objects:
454  * @object: The origin #GESTimelineObject
455  * @track: The #GESTrack to create each #GESTrackObject for.
456  *
457  * Creates all #GESTrackObjects supported by this object and adds them to the
458  * provided track. The track is responsible for calling
459  * #ges_timeline_release_track_object on these objects when it is finished
460  * with them.
461  *
462  * Returns: %TRUE if each track object was created successfully, or %FALSE if an
463  * error occured.
464  */
465
466 gboolean
467 ges_timeline_object_create_track_objects (GESTimelineObject * object,
468     GESTrack * track)
469 {
470   GESTimelineObjectClass *klass;
471
472   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
473   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
474
475   klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
476
477   if (!(klass->create_track_objects)) {
478     GST_WARNING ("no GESTimelineObject::create_track_objects implentation");
479     return FALSE;
480   }
481   return klass->create_track_objects (object, track);
482 }
483
484 /* Default implementation of default_set_max_duration */
485 void
486 default_set_max_duration (GESTimelineObject * object, guint64 maxduration)
487 {
488   GList *tmp;
489   for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp))
490     g_object_set (tmp->data, "max-duration", maxduration, NULL);
491 }
492
493 /*
494  * default implementation of GESTimelineObjectClass::create_track_objects
495  */
496 gboolean
497 ges_timeline_object_create_track_objects_func (GESTimelineObject * object,
498     GESTrack * track)
499 {
500   GESTrackObject *result;
501
502   result = ges_timeline_object_create_track_object (object, track);
503   if (!result) {
504     GST_DEBUG ("Did not create track object");
505     return FALSE;
506   }
507
508   if (ges_timeline_object_add_track_object (object, result) == FALSE)
509     return FALSE;
510
511   return ges_track_add_object (track, result);
512 }
513
514 /**
515  * ges_timeline_object_add_track_object:
516  * @object: a #GESTimelineObject
517  * @trobj: the GESTrackObject
518  *
519  * Add a track object to the timeline object. Should only be called by
520  * subclasses implementing the create_track_objects (plural) vmethod.
521  *
522  * Takes a reference on @trobj.
523  *
524  * Returns: %TRUE on success, %FALSE on failure.
525  */
526
527 gboolean
528 ges_timeline_object_add_track_object (GESTimelineObject * object, GESTrackObject
529     * trobj)
530 {
531   ObjectMapping *mapping;
532   GList *tmp;
533   guint max_prio, min_prio;
534   GESTimelineObjectPrivate *priv = object->priv;
535   gboolean is_effect = GES_IS_TRACK_EFFECT (trobj);
536   GESTimelineObjectClass *klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
537
538   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
539   g_return_val_if_fail (GES_IS_TRACK_OBJECT (trobj), FALSE);
540
541   GST_LOG ("Got a TrackObject : %p , setting the timeline object as its"
542       "creator. Is a TrackEffect %i", trobj, is_effect);
543
544   if (!trobj)
545     return FALSE;
546
547   ges_track_object_set_timeline_object (trobj, object);
548
549   g_object_ref (trobj);
550
551   mapping = g_slice_new0 (ObjectMapping);
552   mapping->object = trobj;
553   priv->mappings = g_list_append (priv->mappings, mapping);
554
555   GST_DEBUG ("Adding TrackObject to the list of controlled track objects");
556   /* We steal the initial reference */
557
558   GST_DEBUG ("Setting properties on newly created TrackObject");
559
560   mapping->priority_offset = priv->nb_effects;
561
562   /* If the trackobject is an effect:
563    *  - We add it on top of the list of TrackEffect
564    *  - We put all TrackObject present in the TimelineObject
565    *    which are not TrackEffect on top of them
566    *
567    * FIXME: Let the full control over priorities to the user
568    */
569   if (is_effect) {
570     GST_DEBUG
571         ("Moving non on top effect under other TrackObject-s, nb effects %i",
572         priv->nb_effects);
573     for (tmp = g_list_nth (priv->trackobjects, priv->nb_effects); tmp;
574         tmp = tmp->next) {
575       GESTrackObject *tmpo = GES_TRACK_OBJECT (tmp->data);
576
577       /* We make sure not to move the entire #TimelineObject */
578       ges_track_object_set_locked (tmpo, FALSE);
579       ges_track_object_set_priority (tmpo,
580           ges_track_object_get_priority (tmpo) + 1);
581       ges_track_object_set_locked (tmpo, TRUE);
582     }
583
584     priv->nb_effects++;
585   }
586
587   object->priv->trackobjects =
588       g_list_insert_sorted_with_data (object->priv->trackobjects, trobj,
589       (GCompareDataFunc) sort_track_effects, object);
590
591   ges_track_object_set_start (trobj, object->start);
592   ges_track_object_set_duration (trobj, object->duration);
593   ges_track_object_set_inpoint (trobj, object->inpoint);
594   ges_track_object_set_max_duration (trobj, object->priv->maxduration);
595
596   if (klass->track_object_added) {
597     GST_DEBUG ("Calling track_object_added subclass method");
598     klass->track_object_added (object, trobj);
599   } else {
600     GST_DEBUG ("%s doesn't have any track_object_added vfunc implementation",
601         G_OBJECT_CLASS_NAME (klass));
602   }
603
604   /* Listen to all property changes */
605   mapping->start_notifyid =
606       g_signal_connect (G_OBJECT (trobj), "notify::start",
607       G_CALLBACK (track_object_start_changed_cb), object);
608   mapping->duration_notifyid =
609       g_signal_connect (G_OBJECT (trobj), "notify::duration",
610       G_CALLBACK (track_object_duration_changed_cb), object);
611   mapping->inpoint_notifyid =
612       g_signal_connect (G_OBJECT (trobj), "notify::inpoint",
613       G_CALLBACK (track_object_inpoint_changed_cb), object);
614   mapping->priority_notifyid =
615       g_signal_connect (G_OBJECT (trobj), "notify::priority",
616       G_CALLBACK (track_object_priority_changed_cb), object);
617
618   get_layer_priorities (priv->layer, &min_prio, &max_prio);
619   ges_track_object_set_priority (trobj, min_prio + object->priority
620       + mapping->priority_offset);
621
622   GST_DEBUG ("Returning trobj:%p", trobj);
623   if (!GES_IS_TRACK_EFFECT (trobj)) {
624     g_signal_emit (object, ges_timeline_object_signals[TRACK_OBJECT_ADDED], 0,
625         GES_TRACK_OBJECT (trobj));
626   } else {
627     /* emit 'effect-added' */
628     g_signal_emit (object, ges_timeline_object_signals[EFFECT_ADDED], 0,
629         GES_TRACK_EFFECT (trobj));
630   }
631
632   return TRUE;
633 }
634
635 /**
636  * ges_timeline_object_release_track_object:
637  * @object: a #GESTimelineObject
638  * @trackobject: the #GESTrackObject to release
639  *
640  * Release the @trackobject from the control of @object.
641  *
642  * Returns: %TRUE if the @trackobject was properly released, else %FALSE.
643  */
644 gboolean
645 ges_timeline_object_release_track_object (GESTimelineObject * object,
646     GESTrackObject * trackobject)
647 {
648   GList *tmp;
649   ObjectMapping *mapping = NULL;
650   GESTimelineObjectClass *klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
651
652   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
653   g_return_val_if_fail (GES_IS_TRACK_OBJECT (trackobject), FALSE);
654
655   GST_DEBUG ("object:%p, trackobject:%p", object, trackobject);
656
657   if (!(g_list_find (object->priv->trackobjects, trackobject))) {
658     GST_WARNING ("TrackObject isn't controlled by this object");
659     return FALSE;
660   }
661
662   for (tmp = object->priv->mappings; tmp; tmp = tmp->next) {
663     mapping = (ObjectMapping *) tmp->data;
664     if (mapping->object == trackobject)
665       break;
666   }
667
668   if (tmp && mapping) {
669
670     /* Disconnect all notify listeners */
671     g_signal_handler_disconnect (trackobject, mapping->start_notifyid);
672     g_signal_handler_disconnect (trackobject, mapping->duration_notifyid);
673     g_signal_handler_disconnect (trackobject, mapping->inpoint_notifyid);
674     g_signal_handler_disconnect (trackobject, mapping->priority_notifyid);
675
676     g_slice_free (ObjectMapping, mapping);
677
678     object->priv->mappings = g_list_delete_link (object->priv->mappings, tmp);
679   }
680
681   object->priv->trackobjects =
682       g_list_remove (object->priv->trackobjects, trackobject);
683
684   if (GES_IS_TRACK_EFFECT (trackobject)) {
685     /* emit 'object-removed' */
686     object->priv->nb_effects--;
687     g_signal_emit (object, ges_timeline_object_signals[EFFECT_REMOVED], 0,
688         GES_TRACK_EFFECT (trackobject));
689   } else
690     g_signal_emit (object, ges_timeline_object_signals[TRACK_OBJECT_REMOVED], 0,
691         GES_TRACK_OBJECT (trackobject));
692
693   ges_track_object_set_timeline_object (trackobject, NULL);
694
695   GST_DEBUG ("Removing reference to track object %p", trackobject);
696
697   if (klass->track_object_released) {
698     GST_DEBUG ("Calling track_object_released subclass method");
699     klass->track_object_released (object, trackobject);
700   }
701
702   g_object_unref (trackobject);
703
704   /* FIXME : resync properties ? */
705
706   return TRUE;
707 }
708
709 void
710 ges_timeline_object_set_layer (GESTimelineObject * object,
711     GESTimelineLayer * layer)
712 {
713   GST_DEBUG ("object:%p, layer:%p", object, layer);
714
715   object->priv->layer = layer;
716 }
717
718 gboolean
719 ges_timeline_object_fill_track_object (GESTimelineObject * object,
720     GESTrackObject * trackobj, GstElement * gnlobj)
721 {
722   GESTimelineObjectClass *class;
723   gboolean res = TRUE;
724
725   GST_DEBUG ("object:%p, trackobject:%p, gnlobject:%p",
726       object, trackobj, gnlobj);
727
728   class = GES_TIMELINE_OBJECT_GET_CLASS (object);
729
730   if (class->need_fill_track) {
731     if (G_UNLIKELY (class->fill_track_object == NULL)) {
732       GST_WARNING ("No 'fill_track_object' implementation available");
733       return FALSE;
734     }
735
736     res = class->fill_track_object (object, trackobj, gnlobj);
737   }
738
739   GST_DEBUG ("Returning res:%d", res);
740
741   return res;
742 }
743
744 gboolean
745 ges_timeline_object_fill_track_object_func (GESTimelineObject * object,
746     GESTrackObject * trackobj, GstElement * gnlobj)
747 {
748   GST_WARNING ("No 'fill_track_object' implementation !");
749
750   return FALSE;
751 }
752
753 static ObjectMapping *
754 find_object_mapping (GESTimelineObject * object, GESTrackObject * child)
755 {
756   GList *tmp;
757
758   for (tmp = object->priv->mappings; tmp; tmp = tmp->next) {
759     ObjectMapping *map = (ObjectMapping *) tmp->data;
760     if (map->object == child)
761       return map;
762   }
763
764   return NULL;
765 }
766
767 static gboolean
768 ges_timeline_object_set_start_internal (GESTimelineObject * object,
769     guint64 start)
770 {
771   GList *tmp;
772   GESTrackObject *tr;
773   ObjectMapping *map;
774
775   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
776
777   GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
778       object, GST_TIME_ARGS (start));
779
780   object->priv->ignore_notifies = TRUE;
781
782   for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
783     tr = (GESTrackObject *) tmp->data;
784     map = find_object_mapping (object, tr);
785
786     if (ges_track_object_is_locked (tr) && tr != object->priv->initiated_move) {
787       gint64 new_start = start - map->start_offset;
788
789       /* Move the child... */
790       if (new_start < 0) {
791         GST_ERROR ("Trying to set start to a negative value %" GST_TIME_FORMAT,
792             GST_TIME_ARGS (-(start + map->start_offset)));
793         continue;
794       }
795
796       ges_track_object_set_start (tr, new_start);
797     } else {
798       /* ... or update the offset */
799       map->start_offset = start - tr->start;
800     }
801   }
802
803   object->priv->ignore_notifies = FALSE;
804
805   object->start = start;
806   return TRUE;
807 }
808
809 /**
810  * ges_timeline_object_set_start:
811  * @object: a #GESTimelineObject
812  * @start: the position in #GstClockTime
813  *
814  * Set the position of the object in its containing layer
815  */
816 void
817 ges_timeline_object_set_start (GESTimelineObject * object, guint64 start)
818 {
819   if (ges_timeline_object_set_start_internal (object, start))
820 #if GLIB_CHECK_VERSION(2,26,0)
821     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
822 #else
823     g_object_notify (G_OBJECT (object), "start");
824 #endif
825 }
826
827 static gboolean
828 ges_timeline_object_set_inpoint_internal (GESTimelineObject * object,
829     guint64 inpoint)
830 {
831   GList *tmp;
832   GESTrackObject *tr;
833
834   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
835
836   GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
837       object, GST_TIME_ARGS (inpoint));
838
839   for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
840     tr = (GESTrackObject *) tmp->data;
841
842     if (ges_track_object_is_locked (tr))
843       /* call set_inpoint on each trackobject */
844       ges_track_object_set_inpoint (tr, inpoint);
845   }
846
847   object->inpoint = inpoint;
848   return TRUE;
849 }
850
851 /**
852  * ges_timeline_object_set_inpoint:
853  * @object: a #GESTimelineObject
854  * @inpoint: the in-point in #GstClockTime
855  *
856  * Set the in-point, that is the moment at which the @object will start
857  * outputting data from its contents.
858  */
859 void
860 ges_timeline_object_set_inpoint (GESTimelineObject * object, guint64 inpoint)
861 {
862   if (ges_timeline_object_set_inpoint_internal (object, inpoint))
863 #if GLIB_CHECK_VERSION(2,26,0)
864     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
865 #else
866     g_object_notify (G_OBJECT (object), "in-point");
867 #endif
868 }
869
870 static gboolean
871 ges_timeline_object_set_duration_internal (GESTimelineObject * object,
872     guint64 duration)
873 {
874   GList *tmp;
875   GESTrackObject *tr;
876
877   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
878
879   GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
880       object, GST_TIME_ARGS (duration));
881
882   for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
883     tr = (GESTrackObject *) tmp->data;
884
885     if (ges_track_object_is_locked (tr))
886       /* call set_duration on each trackobject */
887       ges_track_object_set_duration (tr, duration);
888   }
889
890   object->duration = duration;
891   return TRUE;
892 }
893
894 /**
895  * ges_timeline_object_set_duration:
896  * @object: a #GESTimelineObject
897  * @duration: the duration in #GstClockTime
898  *
899  * Set the duration of the object
900  */
901 void
902 ges_timeline_object_set_duration (GESTimelineObject * object, guint64 duration)
903 {
904   if (ges_timeline_object_set_duration_internal (object, duration))
905 #if GLIB_CHECK_VERSION(2,26,0)
906     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
907 #else
908     g_object_notify (G_OBJECT (object), "duration");
909 #endif
910 }
911
912 static gboolean
913 ges_timeline_object_set_priority_internal (GESTimelineObject * object,
914     guint priority)
915 {
916   GList *tmp;
917   GESTrackObject *tr;
918   ObjectMapping *map;
919   GESTimelineObjectPrivate *priv;
920   guint32 layer_min_gnl_prio, layer_max_gnl_prio;
921
922   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
923
924   GST_DEBUG ("object:%p, priority:%" G_GUINT32_FORMAT, object, priority);
925
926   priv = object->priv;
927   priv->ignore_notifies = TRUE;
928
929   object->priv->ignore_notifies = TRUE;
930
931   get_layer_priorities (priv->layer, &layer_min_gnl_prio, &layer_max_gnl_prio);
932
933   for (tmp = priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
934     tr = (GESTrackObject *) tmp->data;
935     map = find_object_mapping (object, tr);
936
937     if (ges_track_object_is_locked (tr)) {
938       guint32 real_tck_prio;
939
940       /* Move the child... */
941       real_tck_prio = layer_min_gnl_prio + priority + map->priority_offset;
942
943       if (real_tck_prio > layer_max_gnl_prio) {
944         GST_WARNING ("%p priority of %i, is outside of the its containing "
945             "layer space. (%d/%d) setting it to the maximum it can be", object,
946             priority, layer_min_gnl_prio, layer_max_gnl_prio);
947
948         real_tck_prio = layer_max_gnl_prio;
949       }
950
951       ges_track_object_set_priority (tr, real_tck_prio);
952
953     } else {
954       /* ... or update the offset */
955       map->priority_offset = tr->priority - layer_min_gnl_prio + priority;
956     }
957   }
958
959   priv->trackobjects = g_list_sort_with_data (priv->trackobjects,
960       (GCompareDataFunc) sort_track_effects, object);
961   priv->ignore_notifies = FALSE;
962
963   object->priority = priority;
964   return TRUE;
965 }
966
967 /**
968  * ges_timeline_object_set_moving_from_layer:
969  * @object: a #GESTimelineObject
970  * @is_moving: %TRUE if you want to start moving @object to another layer
971  * %FALSE when you finished moving it.
972  *
973  * Sets the object in a moving to layer state. You might rather use the
974  * ges_timeline_object_move_to_layer function to move #GESTimelineObject-s
975  * from a layer to another.
976  **/
977 void
978 ges_timeline_object_set_moving_from_layer (GESTimelineObject * object,
979     gboolean is_moving)
980 {
981   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
982
983   object->priv->is_moving = is_moving;
984 }
985
986 /**
987  * ges_timeline_object_is_moving_from_layer:
988  * @object: a #GESTimelineObject
989  *
990  * Tells you if the object is currently moving from a layer to another.
991  * You might rather use the ges_timeline_object_move_to_layer function to
992  * move #GESTimelineObject-s from a layer to another.
993  *
994  *
995  * Returns: %TRUE if @object is currently moving from its current layer
996  * %FALSE otherwize
997  **/
998 gboolean
999 ges_timeline_object_is_moving_from_layer (GESTimelineObject * object)
1000 {
1001   return object->priv->is_moving;
1002 }
1003
1004 /**
1005  * ges_timeline_object_move_to_layer:
1006  * @object: a #GESTimelineObject
1007  * @layer: the new #GESTimelineLayer
1008  *
1009  * Moves @object to @layer. If @object is not in any layer, it adds it to
1010  * @layer, else, it removes it from its current layer, and adds it to @layer.
1011  *
1012  * Returns: %TRUE if @object could be moved %FALSE otherwize
1013  */
1014 gboolean
1015 ges_timeline_object_move_to_layer (GESTimelineObject * object, GESTimelineLayer
1016     * layer)
1017 {
1018   gboolean ret;
1019   GESTimelineLayer *current_layer;
1020
1021   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1022   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
1023
1024   current_layer = object->priv->layer;
1025
1026   if (current_layer == NULL) {
1027     GST_DEBUG ("Not moving %p, only adding it to %p", object, layer);
1028
1029     return ges_timeline_layer_add_object (layer, object);
1030   }
1031
1032   GST_DEBUG_OBJECT (object, "moving to layer %p, priority: %d", layer,
1033       ges_timeline_layer_get_priority (layer));
1034
1035   object->priv->is_moving = TRUE;
1036   g_object_ref (object);
1037   ret = ges_timeline_layer_remove_object (current_layer, object);
1038
1039   if (!ret) {
1040     g_object_unref (object);
1041     return FALSE;
1042   }
1043
1044   ret = ges_timeline_layer_add_object (layer, object);
1045   object->priv->is_moving = FALSE;
1046
1047   g_object_unref (object);
1048
1049   return ret;
1050 }
1051
1052 /**
1053  * ges_timeline_object_set_priority:
1054  * @object: a #GESTimelineObject
1055  * @priority: the priority
1056  *
1057  * Sets the priority of the object within the containing layer
1058  */
1059 void
1060 ges_timeline_object_set_priority (GESTimelineObject * object, guint priority)
1061 {
1062   if (ges_timeline_object_set_priority_internal (object, priority))
1063 #if GLIB_CHECK_VERSION(2,26,0)
1064     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_PRIORITY]);
1065 #else
1066     g_object_notify (G_OBJECT (object), "priority");
1067 #endif
1068 }
1069
1070 /**
1071  * ges_timeline_object_find_track_object:
1072  * @object: a #GESTimelineObject
1073  * @track: a #GESTrack or NULL
1074  * @type: a #GType indicating the type of track object you are looking
1075  * for or %G_TYPE_NONE if you do not care about the track type.
1076  *
1077  * Finds the #GESTrackObject controlled by @object that is used in @track. You
1078  * may optionally specify a GType to further narrow search criteria.
1079  *
1080  * Note: If many objects match, then the one with the highest priority will be
1081  * returned.
1082  *
1083  * Returns: (transfer full): The #GESTrackObject used by @track, else %NULL.
1084  * Unref after usage.
1085  */
1086
1087 GESTrackObject *
1088 ges_timeline_object_find_track_object (GESTimelineObject * object,
1089     GESTrack * track, GType type)
1090 {
1091   GESTrackObject *ret = NULL;
1092   GList *tmp;
1093   GESTrackObject *otmp;
1094
1095   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1096   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
1097
1098   for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
1099     otmp = (GESTrackObject *) tmp->data;
1100
1101     if (ges_track_object_get_track (otmp) == track) {
1102       if ((type != G_TYPE_NONE) &&
1103           !G_TYPE_CHECK_INSTANCE_TYPE (tmp->data, type))
1104         continue;
1105
1106       ret = GES_TRACK_OBJECT (tmp->data);
1107       g_object_ref (ret);
1108       break;
1109     }
1110   }
1111
1112   return ret;
1113 }
1114
1115 /**
1116  * ges_timeline_object_get_layer:
1117  * @object: a #GESTimelineObject
1118  *
1119  * Get the #GESTimelineLayer to which this object belongs.
1120  *
1121  * Returns: (transfer full): The #GESTimelineLayer where this @object is being
1122  * used, or %NULL if it is not used on any layer. The caller should unref it
1123  * usage.
1124  */
1125 GESTimelineLayer *
1126 ges_timeline_object_get_layer (GESTimelineObject * object)
1127 {
1128   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1129
1130   if (object->priv->layer != NULL)
1131     g_object_ref (G_OBJECT (object->priv->layer));
1132
1133   return object->priv->layer;
1134 }
1135
1136 /**
1137  * ges_timeline_object_get_track_objects:
1138  * @object: a #GESTimelineObject
1139  *
1140  * Get the list of #GESTrackObject contained in @object
1141  *
1142  * Returns: (transfer full) (element-type GESTrackObject): The list of
1143  * trackobject contained in @object.
1144  * The user is responsible for unreffing the contained objects
1145  * and freeing the list.
1146  */
1147 GList *
1148 ges_timeline_object_get_track_objects (GESTimelineObject * object)
1149 {
1150   GList *ret;
1151   GList *tmp;
1152
1153   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1154
1155   ret = g_list_copy (object->priv->trackobjects);
1156
1157   for (tmp = ret; tmp; tmp = tmp->next) {
1158     g_object_ref (tmp->data);
1159   }
1160
1161   return ret;
1162 }
1163
1164 static gint
1165 sort_track_effects (gpointer a, gpointer b, GESTimelineObject * object)
1166 {
1167   guint prio_offset_a, prio_offset_b;
1168   ObjectMapping *map_a, *map_b;
1169   GESTrackObject *obj_a, *obj_b;
1170
1171   obj_a = GES_TRACK_OBJECT (a);
1172   obj_b = GES_TRACK_OBJECT (b);
1173
1174   map_a = find_object_mapping (object, obj_a);
1175   map_b = find_object_mapping (object, obj_b);
1176
1177   prio_offset_a = map_a->priority_offset;
1178   prio_offset_b = map_b->priority_offset;
1179
1180   if ((gint) prio_offset_a > (guint) prio_offset_b)
1181     return 1;
1182   if ((guint) prio_offset_a < (guint) prio_offset_b)
1183     return -1;
1184
1185   return 0;
1186 }
1187
1188 /**
1189  * ges_timeline_object_get_top_effects:
1190  * @object: The origin #GESTimelineObject
1191  *
1192  * Get effects applied on @object
1193  *
1194  * Returns: (transfer full) (element-type GESTrackObject): a #GList of the
1195  * #GESTrackEffect that are applied on @object order by ascendant priorities.
1196  * The refcount of the objects will be increased. The user will have to
1197  * unref each #GESTrackEffect and free the #GList.
1198  *
1199  * Since: 0.10.2
1200  */
1201 GList *
1202 ges_timeline_object_get_top_effects (GESTimelineObject * object)
1203 {
1204   GList *tmp, *ret;
1205   guint i;
1206
1207   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1208
1209   GST_DEBUG_OBJECT (object, "Getting the %i top effects",
1210       object->priv->nb_effects);
1211   ret = NULL;
1212
1213   for (tmp = object->priv->trackobjects, i = 0; i < object->priv->nb_effects;
1214       tmp = tmp->next, i++) {
1215     ret = g_list_append (ret, g_object_ref (tmp->data));
1216   }
1217
1218   return ret;
1219 }
1220
1221 /**
1222  * ges_timeline_object_get_top_effect_position:
1223  * @object: The origin #GESTimelineObject
1224  * @effect: The #GESTrackEffect we want to get the top position from
1225  *
1226  * Gets the top position of an effect.
1227  *
1228  * Returns: The top position of the effect, -1 if something went wrong.
1229  *
1230  * Since: 0.10.2
1231  */
1232 gint
1233 ges_timeline_object_get_top_effect_position (GESTimelineObject * object,
1234     GESTrackEffect * effect)
1235 {
1236   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), -1);
1237
1238   return find_object_mapping (object,
1239       GES_TRACK_OBJECT (effect))->priority_offset;
1240 }
1241
1242 /**
1243  * ges_timeline_object_set_top_effect_priority:
1244  * @object: The origin #GESTimelineObject
1245  * @effect: The #GESTrackEffect to move
1246  * @newpriority: the new position at which to move the @effect inside this
1247  * #GESTimelineObject
1248  *
1249  * This is a convenience method that lets you set the priority of a top effect.
1250  *
1251  * Returns: %TRUE if @effect was successfuly moved, %FALSE otherwise.
1252  *
1253  * Since: 0.10.2
1254  */
1255 gboolean
1256 ges_timeline_object_set_top_effect_priority (GESTimelineObject * object,
1257     GESTrackEffect * effect, guint newpriority)
1258 {
1259   gint inc;
1260   GList *tmp;
1261   guint current_prio;
1262   GESTrackObject *tck_obj;
1263   GESTimelineObjectPrivate *priv;
1264
1265   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1266
1267   tck_obj = GES_TRACK_OBJECT (effect);
1268   priv = object->priv;
1269   current_prio = ges_track_object_get_priority (tck_obj);
1270
1271   /*  We don't change the priority */
1272   if (current_prio == newpriority ||
1273       (G_UNLIKELY (ges_track_object_get_timeline_object (tck_obj) != object)))
1274     return FALSE;
1275
1276   if (newpriority > (object->priv->nb_effects - 1)) {
1277     GST_DEBUG ("You are trying to make %p not a top effect", effect);
1278     return FALSE;
1279   }
1280
1281   if (current_prio > object->priv->nb_effects) {
1282     GST_DEBUG ("%p is not a top effect");
1283     return FALSE;
1284   }
1285
1286   if (tck_obj->priority < newpriority)
1287     inc = -1;
1288   else
1289     inc = +1;
1290
1291   ges_track_object_set_priority (tck_obj, newpriority);
1292   for (tmp = priv->trackobjects; tmp; tmp = tmp->next) {
1293     GESTrackObject *tmpo = GES_TRACK_OBJECT (tmp->data);
1294     guint tck_priority = ges_track_object_get_priority (tmpo);
1295
1296     if ((inc == +1 && tck_priority >= newpriority) ||
1297         (inc == -1 && tck_priority <= newpriority)) {
1298       ges_track_object_set_priority (tmpo, tck_priority + inc);
1299     }
1300   }
1301
1302   priv->trackobjects = g_list_sort_with_data (priv->trackobjects,
1303       (GCompareDataFunc) sort_track_effects, object);
1304
1305   return TRUE;
1306 }
1307
1308 void
1309 tck_object_added_cb (GESTimelineObject * object,
1310     GESTrackObject * track_object, GList * track_objects)
1311 {
1312   gint64 duration, start, inpoint, position;
1313   GList *tmp;
1314   gboolean locked;
1315
1316   ges_track_object_set_locked (track_object, FALSE);
1317   g_object_get (object, "start", &position, NULL);
1318   for (tmp = track_objects; tmp; tmp = tmp->next) {
1319     if (ges_track_object_get_track (track_object)->type ==
1320         ges_track_object_get_track (tmp->data)->type) {
1321       locked = ges_track_object_is_locked (tmp->data);
1322       ges_track_object_set_locked (tmp->data, FALSE);
1323       g_object_get (tmp->data, "duration", &duration, "start", &start,
1324           "in-point", &inpoint, NULL);
1325       g_object_set (tmp->data, "duration",
1326           duration - (duration + start - position), NULL);
1327       g_object_set (track_object, "start", position, "in-point",
1328           duration - (duration + start - inpoint - position), "duration",
1329           duration + start - position, NULL);
1330       ges_track_object_set_locked (tmp->data, locked);
1331       ges_track_object_set_locked (track_object, locked);
1332     }
1333   }
1334 }
1335
1336 /**
1337  * ges_timeline_object_split:
1338  * @object: the #GESTimelineObject to split
1339  * @position: The position at which to split the @object (in nanosecond)
1340  *
1341  * The function modifies @object, and creates another #GESTimelineObject so
1342  * we have two clips at the end, splitted at the time specified by @position.
1343  *
1344  * Returns: (transfer full): The newly created #GESTimelineObject resulting from
1345  * the splitting
1346  *
1347  * Since: 0.10.XX
1348  */
1349 GESTimelineObject *
1350 ges_timeline_object_split (GESTimelineObject * object, gint64 position)
1351 {
1352   GList *track_objects, *tmp;
1353   GESTimelineLayer *layer;
1354   GESTimelineObject *new_object;
1355   gint64 duration, start, inpoint;
1356
1357   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1358
1359   g_object_get (object, "duration", &duration, "start", &start, "in-point",
1360       &inpoint, NULL);
1361
1362   track_objects = ges_timeline_object_get_track_objects (object);
1363   layer = ges_timeline_object_get_layer (object);
1364
1365   new_object = ges_timeline_object_copy (object, FALSE);
1366
1367   if (g_list_length (track_objects) == 2) {
1368     g_object_set (new_object, "start", position, NULL);
1369     g_signal_connect (G_OBJECT (new_object), "track-object-added",
1370         G_CALLBACK (tck_object_added_cb), track_objects);
1371   } else {
1372     for (tmp = track_objects; tmp; tmp = tmp->next) {
1373       g_object_set (tmp->data, "duration",
1374           duration - (duration + start - position), NULL);
1375       g_object_set (new_object, "start", position, "in-point",
1376           duration - (duration + start - position), "duration",
1377           (duration + start - position), NULL);
1378       g_object_set (object, "duration",
1379           duration - (duration + start - position), NULL);
1380     }
1381   }
1382
1383   ges_timeline_layer_add_object (layer, new_object);
1384
1385   return new_object;
1386 }
1387
1388 /* TODO implement the deep parameter, and make it public */
1389 static GESTimelineObject *
1390 ges_timeline_object_copy (GESTimelineObject * object, gboolean * deep)
1391 {
1392   GESTimelineObject *ret = NULL;
1393   GParameter *params;
1394   GParamSpec **specs;
1395   guint n, n_specs, n_params;
1396
1397   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1398
1399   specs =
1400       g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &n_specs);
1401   params = g_new0 (GParameter, n_specs);
1402   n_params = 0;
1403
1404   for (n = 0; n < n_specs; ++n) {
1405     if (strcmp (specs[n]->name, "parent") &&
1406         (specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) {
1407       params[n_params].name = g_intern_string (specs[n]->name);
1408       g_value_init (&params[n_params].value, specs[n]->value_type);
1409       g_object_get_property (G_OBJECT (object), specs[n]->name,
1410           &params[n_params].value);
1411       ++n_params;
1412     }
1413   }
1414
1415   ret = g_object_newv (G_TYPE_FROM_INSTANCE (object), n_params, params);
1416
1417   if (GES_IS_TIMELINE_FILE_SOURCE (ret)) {
1418     GList *tck_objects;
1419     tck_objects = ges_timeline_object_get_track_objects (object);
1420     if (g_list_length (tck_objects) == 1) {
1421       GESTrackType type;
1422       type = ges_track_object_get_track (tck_objects->data)->type;
1423       ges_timeline_filesource_set_supported_formats (GES_TIMELINE_FILE_SOURCE
1424           (ret), type);
1425     }
1426     g_list_free (tck_objects);
1427   }
1428
1429   g_free (specs);
1430   g_free (params);
1431
1432   return ret;
1433 }
1434
1435 /**
1436  * ges_timeline_object_set_supported_formats:
1437  * @object: the #GESTimelineObject to set supported formats on
1438  * @supportedformats: the #GESTrackType defining formats supported by @object
1439  *
1440  * Sets the formats supported by the file.
1441  *
1442  * Since: 0.10.XX
1443  */
1444 void
1445 ges_timeline_object_set_supported_formats (GESTimelineObject * object,
1446     GESTrackType supportedformats)
1447 {
1448   g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
1449
1450   object->priv->supportedformats = supportedformats;
1451 }
1452
1453 /**
1454  * ges_timeline_object_get_supported_formats:
1455  * @object: the #GESTimelineObject
1456  *
1457  * Get the formats supported by @object.
1458  *
1459  * Returns: The formats supported by @object.
1460  *
1461  * Since: 0.10.XX
1462  */
1463 GESTrackType
1464 ges_timeline_object_get_supported_formats (GESTimelineObject * object)
1465 {
1466   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object),
1467       GES_TRACK_TYPE_UNKNOWN);
1468
1469   return object->priv->supportedformats;
1470 }
1471
1472 /**
1473  * ges_timeline_object_objects_set_locked:
1474  * @object: the #GESTimelineObject
1475  * @locked: whether the #GESTrackObject contained in @object are locked to it.
1476  *
1477  * Set the locking status of all the #GESTrackObject contained in @object to @locked.
1478  * See the ges_track_object_set_locked documentation for more details.
1479  *
1480  * Since: 0.10.XX
1481  */
1482 void
1483 ges_timeline_object_objects_set_locked (GESTimelineObject * object,
1484     gboolean locked)
1485 {
1486   GList *tmp;
1487
1488   g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
1489
1490   for (tmp = object->priv->mappings; tmp; tmp = g_list_next (tmp)) {
1491     ges_track_object_set_locked (((ObjectMapping *) tmp->data)->object, locked);
1492   }
1493 }
1494
1495 /**
1496  * ges_timeline_object_get_max_duration:
1497  * @object: The #GESTimelineObject to retrieve max duration from
1498  *
1499  * Get the max duration of @object.
1500  *
1501  * Returns: The max duration of @object
1502  *
1503  * Since: 0.10.XX
1504  */
1505 guint64
1506 ges_timeline_object_get_max_duration (GESTimelineObject * object)
1507 {
1508   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), 0);
1509
1510   return object->priv->maxduration;
1511 }
1512
1513 /**
1514  * ges_timeline_object_set_max_duration:
1515  * @object: The #GESTimelineObject to retrieve max duration from
1516  * @maxduration: The maximum duration of @object
1517  *
1518  * Returns: Set the max duration of @object
1519  *
1520  * Since: 0.10.XX
1521  */
1522 void
1523 ges_timeline_object_set_max_duration (GESTimelineObject * object,
1524     guint64 maxduration)
1525 {
1526   GESTimelineObjectClass *klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
1527
1528   g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
1529
1530   object->priv->maxduration = maxduration;
1531   klass->set_max_duration (object, maxduration);
1532 }
1533
1534 static void
1535 update_height (GESTimelineObject * object)
1536 {
1537   GList *tmp;
1538   guint32 min_prio = G_MAXUINT32, max_prio = 0;
1539
1540   /* Go over all childs and check if height has changed */
1541   for (tmp = object->priv->trackobjects; tmp; tmp = tmp->next) {
1542     guint tck_priority =
1543         ges_track_object_get_priority (GES_TRACK_OBJECT (tmp->data));
1544
1545     if (tck_priority < min_prio)
1546       min_prio = tck_priority;
1547     if (tck_priority > max_prio)
1548       max_prio = tck_priority;
1549   }
1550
1551   /* FIXME : We only grow the height */
1552   if (object->height < (max_prio - min_prio + 1)) {
1553     object->height = max_prio - min_prio + 1;
1554     GST_DEBUG ("Updating height %i", object->height);
1555 #if GLIB_CHECK_VERSION(2,26,0)
1556     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_HEIGHT]);
1557 #else
1558     g_object_notify (G_OBJECT (object), "height");
1559 #endif
1560   }
1561 }
1562
1563 /*
1564  * PROPERTY NOTIFICATIONS FROM TRACK OBJECTS
1565  */
1566
1567 static void
1568 track_object_start_changed_cb (GESTrackObject * child,
1569     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
1570 {
1571   ObjectMapping *map;
1572
1573   if (object->priv->ignore_notifies)
1574     return;
1575
1576   map = find_object_mapping (object, child);
1577   if (G_UNLIKELY (map == NULL))
1578     /* something massively screwed up if we get this */
1579     return;
1580
1581   if (!ges_track_object_is_locked (child)) {
1582     /* Update the internal start_offset */
1583     map->start_offset = object->start - child->start;
1584   } else {
1585     /* Or update the parent start */
1586     object->priv->initiated_move = child;
1587     ges_timeline_object_set_start (object, child->start + map->start_offset);
1588     object->priv->initiated_move = NULL;
1589   }
1590 }
1591
1592 static void
1593 track_object_inpoint_changed_cb (GESTrackObject * child,
1594     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
1595 {
1596   ObjectMapping *map;
1597
1598   if (object->priv->ignore_notifies)
1599     return;
1600
1601   map = find_object_mapping (object, child);
1602   if (G_UNLIKELY (map == NULL))
1603     /* something massively screwed up if we get this */
1604     return;
1605
1606   if (!ges_track_object_is_locked (child)) {
1607     /* Update the internal start_offset */
1608     map->inpoint_offset = object->inpoint - child->inpoint;
1609   } else {
1610     /* Or update the parent start */
1611     object->priv->initiated_move = child;
1612     ges_timeline_object_set_inpoint (object,
1613         child->inpoint + map->inpoint_offset);
1614     object->priv->initiated_move = NULL;
1615   }
1616
1617 }
1618
1619 static void
1620 track_object_duration_changed_cb (GESTrackObject * child,
1621     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
1622 {
1623   ObjectMapping *map;
1624
1625   if (object->priv->ignore_notifies)
1626     return;
1627
1628   map = find_object_mapping (object, child);
1629   if (G_UNLIKELY (map == NULL))
1630     /* something massively screwed up if we get this */
1631     return;
1632
1633   if (!ges_track_object_is_locked (child)) {
1634     /* Update the internal start_offset */
1635     map->duration_offset = object->duration - child->duration;
1636   } else {
1637     /* Or update the parent start */
1638     object->priv->initiated_move = child;
1639     ges_timeline_object_set_duration (object,
1640         child->duration + map->duration_offset);
1641     object->priv->initiated_move = NULL;
1642   }
1643
1644 }
1645
1646 static void
1647 track_object_priority_changed_cb (GESTrackObject * child,
1648     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
1649 {
1650   ObjectMapping *map;
1651   guint32 layer_min_gnl_prio, layer_max_gnl_prio;
1652
1653   guint tck_priority = ges_track_object_get_priority (child);
1654
1655   GST_DEBUG ("TrackObject %p priority changed to %i", child,
1656       ges_track_object_get_priority (child));
1657
1658   if (object->priv->ignore_notifies)
1659     return;
1660
1661   update_height (object);
1662   map = find_object_mapping (object, child);
1663   get_layer_priorities (object->priv->layer, &layer_min_gnl_prio,
1664       &layer_max_gnl_prio);
1665
1666   if (G_UNLIKELY (map == NULL))
1667     /* something massively screwed up if we get this */
1668     return;
1669
1670   if (!ges_track_object_is_locked (child)) {
1671     if (tck_priority < layer_min_gnl_prio || tck_priority > layer_max_gnl_prio) {
1672       GST_WARNING ("%p priority of %i, is outside of its containing "
1673           "layer space. (%d/%d). This is a bug in the program.", object,
1674           tck_priority, layer_min_gnl_prio, layer_max_gnl_prio);
1675     }
1676
1677     /* Update the internal priority_offset */
1678     map->priority_offset =
1679         tck_priority - (layer_min_gnl_prio + object->priority);
1680
1681   } else if (tck_priority < layer_min_gnl_prio + object->priority) {
1682     /* Or update the parent priority, the object priority is always the
1683      * highest priority (smaller number) */
1684     if (tck_priority < layer_min_gnl_prio || layer_max_gnl_prio < tck_priority) {
1685
1686       GST_WARNING ("%p priority of %i, is outside of its containing "
1687           "layer space. (%d/%d). This is a bug in the program.", object,
1688           tck_priority, layer_min_gnl_prio, layer_max_gnl_prio);
1689       return;
1690     }
1691
1692     ges_timeline_object_set_priority (object,
1693         tck_priority - layer_min_gnl_prio);
1694   }
1695
1696   GST_DEBUG ("object %p priority %d child %p priority %d", object,
1697       object->priority, child, ges_track_object_get_priority (child));
1698 }
1699
1700 static void
1701 get_layer_priorities (GESTimelineLayer * layer, guint32 * layer_min_gnl_prio,
1702     guint32 * layer_max_gnl_prio)
1703 {
1704   if (layer) {
1705     *layer_min_gnl_prio = layer->min_gnl_priority;
1706     *layer_max_gnl_prio = layer->max_gnl_priority;
1707   } else {
1708     *layer_min_gnl_prio = 0;
1709     *layer_max_gnl_prio = G_MAXUINT32;
1710   }
1711 }