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