Merge remote-tracking branch 'origin/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 static gint sort_track_effects (gpointer a, gpointer b,
63     GESTimelineObject * object);
64 static void
65 get_layer_priorities (GESTimelineLayer * layer, guint32 * layer_min_gnl_prio,
66     guint32 * layer_max_gnl_prio);
67
68 static gboolean
69 ges_timeline_object_set_start_internal (GESTimelineObject * object,
70     guint64 start);
71 static gboolean ges_timeline_object_set_inpoint_internal (GESTimelineObject *
72     object, guint64 inpoint);
73 static gboolean ges_timeline_object_set_duration_internal (GESTimelineObject *
74     object, guint64 duration);
75 static gboolean ges_timeline_object_set_priority_internal (GESTimelineObject *
76     object, guint32 priority);
77
78 static GESTimelineObject *ges_timeline_object_copy (GESTimelineObject * object,
79     gboolean * deep);
80
81 G_DEFINE_ABSTRACT_TYPE (GESTimelineObject, ges_timeline_object,
82     G_TYPE_INITIALLY_UNOWNED);
83
84 /* Mapping of relationship between a TimelineObject and the TrackObjects
85  * it controls
86  *
87  * NOTE : how do we make this public in the future ?
88  */
89 typedef struct
90 {
91   GESTrackObject *object;
92   gint64 start_offset;
93   gint64 duration_offset;
94   gint64 inpoint_offset;
95   gint32 priority_offset;
96
97   guint start_notifyid;
98   guint duration_notifyid;
99   guint inpoint_notifyid;
100   guint priority_notifyid;
101
102   /* track mapping ?? */
103 } ObjectMapping;
104
105 enum
106 {
107   EFFECT_ADDED,
108   EFFECT_REMOVED,
109   TRACK_OBJECT_ADDED,
110   TRACK_OBJECT_REMOVED,
111   LAST_SIGNAL
112 };
113
114 static guint ges_timeline_object_signals[LAST_SIGNAL] = { 0 };
115
116 struct _GESTimelineObjectPrivate
117 {
118   /*< public > */
119   GESTimelineLayer *layer;
120
121   /*< private > */
122   /* A list of TrackObject controlled by this TimelineObject sorted by
123    * priority */
124   GList *trackobjects;
125
126   /* Set to TRUE when the timelineobject is doing updates of track object
127    * properties so we don't end up in infinite property update loops
128    */
129   gboolean ignore_notifies;
130   gboolean is_moving;
131
132   guint64 maxduration;
133
134   GList *mappings;
135
136   guint nb_effects;
137
138   GESTrackObject *initiated_move;
139
140   /* The formats supported by this TimelineObject */
141   GESTrackType supportedformats;
142 };
143
144 enum
145 {
146   PROP_0,
147   PROP_START,
148   PROP_INPOINT,
149   PROP_DURATION,
150   PROP_PRIORITY,
151   PROP_HEIGHT,
152   PROP_LAYER,
153   PROP_SUPPORTED_FORMATS,
154   PROP_MAX_DURATION,
155   PROP_LAST
156 };
157
158 static GParamSpec *properties[PROP_LAST];
159
160 static void
161 ges_timeline_object_get_property (GObject * object, guint property_id,
162     GValue * value, GParamSpec * pspec)
163 {
164   GESTimelineObject *tobj = GES_TIMELINE_OBJECT (object);
165
166   switch (property_id) {
167     case PROP_START:
168       g_value_set_uint64 (value, tobj->start);
169       break;
170     case PROP_INPOINT:
171       g_value_set_uint64 (value, tobj->inpoint);
172       break;
173     case PROP_DURATION:
174       g_value_set_uint64 (value, tobj->duration);
175       break;
176     case PROP_PRIORITY:
177       g_value_set_uint (value, tobj->priority);
178       break;
179     case PROP_HEIGHT:
180       g_value_set_uint (value, tobj->height);
181       break;
182     case PROP_LAYER:
183       g_value_set_object (value, tobj->priv->layer);
184       break;
185     case PROP_SUPPORTED_FORMATS:
186       g_value_set_flags (value, tobj->priv->supportedformats);
187       break;
188     case PROP_MAX_DURATION:
189       g_value_set_uint64 (value, tobj->priv->maxduration);
190       break;
191     default:
192       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
193   }
194 }
195
196 static void
197 ges_timeline_object_set_property (GObject * object, guint property_id,
198     const GValue * value, GParamSpec * pspec)
199 {
200   GESTimelineObject *tobj = GES_TIMELINE_OBJECT (object);
201
202   switch (property_id) {
203     case PROP_START:
204       ges_timeline_object_set_start_internal (tobj, g_value_get_uint64 (value));
205       break;
206     case PROP_INPOINT:
207       ges_timeline_object_set_inpoint_internal (tobj,
208           g_value_get_uint64 (value));
209       break;
210     case PROP_DURATION:
211       ges_timeline_object_set_duration_internal (tobj,
212           g_value_get_uint64 (value));
213       break;
214     case PROP_PRIORITY:
215       ges_timeline_object_set_priority_internal (tobj,
216           g_value_get_uint (value));
217       break;
218     case PROP_SUPPORTED_FORMATS:
219       ges_timeline_object_set_supported_formats (tobj,
220           g_value_get_flags (value));
221       break;
222     case PROP_MAX_DURATION:
223       ges_timeline_object_set_max_duration (tobj, g_value_get_uint64 (value));
224       break;
225     default:
226       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
227   }
228 }
229
230 static void
231 ges_timeline_object_class_init (GESTimelineObjectClass * klass)
232 {
233   GObjectClass *object_class = G_OBJECT_CLASS (klass);
234
235   g_type_class_add_private (klass, sizeof (GESTimelineObjectPrivate));
236
237   object_class->get_property = ges_timeline_object_get_property;
238   object_class->set_property = ges_timeline_object_set_property;
239   klass->create_track_objects = ges_timeline_object_create_track_objects_func;
240   klass->set_max_duration = default_set_max_duration;
241   klass->track_object_added = NULL;
242   klass->track_object_released = NULL;
243
244   /**
245    * GESTimelineObject:start
246    *
247    * The position of the object in the #GESTimelineLayer (in nanoseconds).
248    */
249   properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
250       "The position in the container", 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
251   g_object_class_install_property (object_class, PROP_START,
252       properties[PROP_START]);
253
254   /**
255    * GESTimelineObject:in-point
256    *
257    * The in-point at which this #GESTimelineObject will start outputting data
258    * from its contents (in nanoseconds).
259    *
260    * Ex : an in-point of 5 seconds means that the first outputted buffer will
261    * be the one located 5 seconds in the controlled resource.
262    */
263   properties[PROP_INPOINT] =
264       g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
265       G_MAXUINT64, 0, G_PARAM_READWRITE);
266   g_object_class_install_property (object_class, PROP_INPOINT,
267       properties[PROP_INPOINT]);
268
269   /**
270    * GESTimelineObject:duration
271    *
272    * The duration (in nanoseconds) which will be used in the container #GESTrack
273    * starting from 'in-point'.
274    */
275   properties[PROP_DURATION] =
276       g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
277       G_MAXUINT64, GST_CLOCK_TIME_NONE, G_PARAM_READWRITE);
278   g_object_class_install_property (object_class, PROP_DURATION,
279       properties[PROP_DURATION]);
280
281   /**
282    * GESTimelineObject:priority
283    *
284    * The layer priority of the timeline object.
285    */
286   properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
287       "The priority of the object", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
288   g_object_class_install_property (object_class, PROP_PRIORITY,
289       properties[PROP_PRIORITY]);
290
291   /**
292    * GESTimelineObject:height
293    *
294    * The span of layer priorities which this object occupies.
295    */
296   properties[PROP_HEIGHT] = g_param_spec_uint ("height", "Height",
297       "The span of priorities this object occupies", 0, G_MAXUINT, 1,
298       G_PARAM_READABLE);
299   g_object_class_install_property (object_class, PROP_HEIGHT,
300       properties[PROP_HEIGHT]);
301
302   /**
303    * GESTimelineObject:supported-formats:
304    *
305    * The formats supported by the object.
306    *
307    * Since: 0.10.XX
308    */
309   properties[PROP_SUPPORTED_FORMATS] = g_param_spec_flags ("supported-formats",
310       "Supported formats", "Formats supported by the file",
311       GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_UNKNOWN,
312       G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
313
314   g_object_class_install_property (object_class, PROP_SUPPORTED_FORMATS,
315       properties[PROP_SUPPORTED_FORMATS]);
316
317   /**
318    * GESTimelineObject:layer
319    *
320    * The GESTimelineLayer where this object is being used.
321    */
322   properties[PROP_LAYER] = g_param_spec_object ("layer", "Layer",
323       "The GESTimelineLayer where this object is being used.",
324       GES_TYPE_TIMELINE_LAYER, G_PARAM_READABLE);
325   g_object_class_install_property (object_class, PROP_LAYER,
326       properties[PROP_LAYER]);
327
328   /**
329    * GESTimelineObject::effect-added
330    * @object: the #GESTimelineObject
331    * @effect: the #GESTrackEffect that was added.
332    *
333    * Will be emitted after an effect was added to the object.
334    *
335    * Since: 0.10.2
336    */
337   ges_timeline_object_signals[EFFECT_ADDED] =
338       g_signal_new ("effect-added", G_TYPE_FROM_CLASS (klass),
339       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
340       G_TYPE_NONE, 1, GES_TYPE_TRACK_EFFECT);
341
342   /**
343    * GESTimelineObject::effect-removed
344    * @object: the #GESTimelineObject
345    * @effect: the #GESTrackEffect that was added.
346    *
347    * Will be emitted after an effect was remove from the object.
348    *
349    * Since: 0.10.2
350    */
351   ges_timeline_object_signals[EFFECT_REMOVED] =
352       g_signal_new ("effect-removed", G_TYPE_FROM_CLASS (klass),
353       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
354       G_TYPE_NONE, 1, GES_TYPE_TRACK_EFFECT);
355
356   /**
357    * GESTimelineObject::track-object-added
358    * @object: the #GESTimelineObject
359    * @tckobj: the #GESTrackObject that was added.
360    *
361    * Will be emitted after a track object was added to the object.
362    *
363    * Since: 0.10.2
364    */
365   ges_timeline_object_signals[TRACK_OBJECT_ADDED] =
366       g_signal_new ("track-object-added", G_TYPE_FROM_CLASS (klass),
367       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
368       G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
369
370   /**
371    * GESTimelineObject::track-object-removed
372    * @object: the #GESTimelineObject
373    * @tckobj: the #GESTrackObject that was removed.
374    *
375    * Will be emitted after a track object was removed from @object.
376    *
377    * Since: 0.10.2
378    */
379   ges_timeline_object_signals[TRACK_OBJECT_REMOVED] =
380       g_signal_new ("track-object-removed", G_TYPE_FROM_CLASS (klass),
381       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
382       G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
383
384   /**
385    * GESTimelineObject:max-duration:
386    *
387    * The maximum duration (in nanoseconds) of the #GESTimelineObject.
388    *
389    * Since: 0.10.XX
390    */
391   g_object_class_install_property (object_class, PROP_MAX_DURATION,
392       g_param_spec_uint64 ("max-duration", "Maximum duration",
393           "The duration of the object", 0, G_MAXUINT64, G_MAXUINT64,
394           G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
395
396   klass->need_fill_track = TRUE;
397   klass->snaps = FALSE;
398 }
399
400 static void
401 ges_timeline_object_init (GESTimelineObject * self)
402 {
403   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
404       GES_TYPE_TIMELINE_OBJECT, GESTimelineObjectPrivate);
405   self->duration = GST_SECOND;
406   self->height = 1;
407   self->priv->trackobjects = NULL;
408   self->priv->layer = NULL;
409   self->priv->nb_effects = 0;
410   self->priv->is_moving = FALSE;
411   self->priv->maxduration = G_MAXUINT64;
412 }
413
414 /**
415  * ges_timeline_object_create_track_object:
416  * @object: The origin #GESTimelineObject
417  * @track: The #GESTrack to create a #GESTrackObject for.
418  *
419  * Creates a #GESTrackObject for the provided @track. The timeline object
420  * keep a reference to the newly created trackobject, you therefore need to
421  * call @ges_timeline_object_release_track_object when you are done with it.
422  *
423  * Returns: (transfer none): A #GESTrackObject. Returns NULL if the #GESTrackObject could not
424  * be created.
425  */
426
427 GESTrackObject *
428 ges_timeline_object_create_track_object (GESTimelineObject * object,
429     GESTrack * track)
430 {
431   GESTimelineObjectClass *class;
432   GESTrackObject *res;
433
434   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
435   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
436
437   class = GES_TIMELINE_OBJECT_GET_CLASS (object);
438
439   if (G_UNLIKELY (class->create_track_object == NULL)) {
440     GST_ERROR ("No 'create_track_object' implementation available");
441     return NULL;
442   }
443
444   res = class->create_track_object (object, track);
445   return res;
446
447 }
448
449 /**
450  * ges_timeline_object_create_track_objects:
451  * @object: The origin #GESTimelineObject
452  * @track: The #GESTrack to create each #GESTrackObject for.
453  *
454  * Creates all #GESTrackObjects supported by this object and adds them to the
455  * provided track. The track is responsible for calling
456  * #ges_timeline_release_track_object on these objects when it is finished
457  * with them.
458  *
459  * Returns: %TRUE if each track object was created successfully, or %FALSE if an
460  * error occured.
461  */
462
463 gboolean
464 ges_timeline_object_create_track_objects (GESTimelineObject * object,
465     GESTrack * track)
466 {
467   GESTimelineObjectClass *klass;
468
469   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
470   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
471
472   klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
473
474   if (!(klass->create_track_objects)) {
475     GST_WARNING ("no GESTimelineObject::create_track_objects implentation");
476     return FALSE;
477   }
478
479   return klass->create_track_objects (object, track);
480 }
481
482 /* Default implementation of default_set_max_duration */
483 void
484 default_set_max_duration (GESTimelineObject * object, guint64 maxduration)
485 {
486   GList *tmp;
487   for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp))
488     g_object_set (tmp->data, "max-duration", maxduration, NULL);
489 }
490
491 /*
492  * default implementation of GESTimelineObjectClass::create_track_objects
493  */
494 gboolean
495 ges_timeline_object_create_track_objects_func (GESTimelineObject * object,
496     GESTrack * track)
497 {
498   GESTrackObject *result;
499
500   result = ges_timeline_object_create_track_object (object, track);
501   if (!result) {
502     GST_DEBUG ("Did not create track object");
503     return FALSE;
504   }
505
506   if (ges_timeline_object_add_track_object (object, result) == FALSE)
507     return FALSE;
508
509   return ges_track_add_object (track, result);
510 }
511
512 /**
513  * ges_timeline_object_add_track_object:
514  * @object: a #GESTimelineObject
515  * @trobj: the GESTrackObject
516  *
517  * Add a track object to the timeline object. Should only be called by
518  * subclasses implementing the create_track_objects (plural) vmethod.
519  *
520  * Takes a reference on @trobj.
521  *
522  * Returns: %TRUE on success, %FALSE on failure.
523  */
524
525 gboolean
526 ges_timeline_object_add_track_object (GESTimelineObject * object, GESTrackObject
527     * trobj)
528 {
529   ObjectMapping *mapping;
530   GList *tmp;
531   guint max_prio, min_prio;
532   GESTimelineObjectPrivate *priv = object->priv;
533   gboolean is_effect = GES_IS_TRACK_EFFECT (trobj);
534   GESTimelineObjectClass *klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
535
536   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
537   g_return_val_if_fail (GES_IS_TRACK_OBJECT (trobj), FALSE);
538
539   GST_LOG ("Got a TrackObject : %p , setting the timeline object as its"
540       "creator. Is a TrackEffect %i", trobj, is_effect);
541
542   if (!trobj)
543     return FALSE;
544
545   ges_track_object_set_timeline_object (trobj, object);
546
547   g_object_ref (trobj);
548
549   mapping = g_slice_new0 (ObjectMapping);
550   mapping->object = trobj;
551   priv->mappings = g_list_append (priv->mappings, mapping);
552
553   GST_DEBUG ("Adding TrackObject to the list of controlled track objects");
554   /* We steal the initial reference */
555
556   GST_DEBUG ("Setting properties on newly created TrackObject");
557
558   mapping->priority_offset = priv->nb_effects;
559
560   /* If the trackobject is an effect:
561    *  - We add it on top of the list of TrackEffect
562    *  - We put all TrackObject present in the TimelineObject
563    *    which are not TrackEffect on top of them
564    *
565    * FIXME: Let the full control over priorities to the user
566    */
567   if (is_effect) {
568     GST_DEBUG
569         ("Moving non on top effect under other TrackObject-s, nb effects %i",
570         priv->nb_effects);
571     for (tmp = g_list_nth (priv->trackobjects, priv->nb_effects); tmp;
572         tmp = tmp->next) {
573       GESTrackObject *tmpo = GES_TRACK_OBJECT (tmp->data);
574
575       /* We make sure not to move the entire #TimelineObject */
576       ges_track_object_set_locked (tmpo, FALSE);
577       ges_track_object_set_priority (tmpo,
578           ges_track_object_get_priority (tmpo) + 1);
579       ges_track_object_set_locked (tmpo, TRUE);
580     }
581
582     priv->nb_effects++;
583   }
584
585   object->priv->trackobjects =
586       g_list_insert_sorted_with_data (object->priv->trackobjects, trobj,
587       (GCompareDataFunc) sort_track_effects, object);
588
589   ges_track_object_set_start (trobj, object->start);
590   ges_track_object_set_duration (trobj, object->duration);
591   ges_track_object_set_inpoint (trobj, object->inpoint);
592   ges_track_object_set_max_duration (trobj, object->priv->maxduration);
593
594   if (klass->track_object_added) {
595     GST_DEBUG ("Calling track_object_added subclass method");
596     klass->track_object_added (object, trobj);
597   } else {
598     GST_DEBUG ("%s doesn't have any track_object_added vfunc implementation",
599         G_OBJECT_CLASS_NAME (klass));
600   }
601
602   /* Listen to all property changes */
603   mapping->start_notifyid =
604       g_signal_connect (G_OBJECT (trobj), "notify::start",
605       G_CALLBACK (track_object_start_changed_cb), object);
606   mapping->duration_notifyid =
607       g_signal_connect (G_OBJECT (trobj), "notify::duration",
608       G_CALLBACK (track_object_duration_changed_cb), object);
609   mapping->inpoint_notifyid =
610       g_signal_connect (G_OBJECT (trobj), "notify::in-point",
611       G_CALLBACK (track_object_inpoint_changed_cb), object);
612   mapping->priority_notifyid =
613       g_signal_connect (G_OBJECT (trobj), "notify::priority",
614       G_CALLBACK (track_object_priority_changed_cb), object);
615
616   get_layer_priorities (priv->layer, &min_prio, &max_prio);
617   ges_track_object_set_priority (trobj, min_prio + object->priority
618       + mapping->priority_offset);
619
620   GST_DEBUG ("Returning trobj:%p", trobj);
621   if (!GES_IS_TRACK_EFFECT (trobj)) {
622     g_signal_emit (object, ges_timeline_object_signals[TRACK_OBJECT_ADDED], 0,
623         GES_TRACK_OBJECT (trobj));
624   } else {
625     /* emit 'effect-added' */
626     g_signal_emit (object, ges_timeline_object_signals[EFFECT_ADDED], 0,
627         GES_TRACK_EFFECT (trobj));
628   }
629
630   return TRUE;
631 }
632
633 /**
634  * ges_timeline_object_release_track_object:
635  * @object: a #GESTimelineObject
636  * @trackobject: the #GESTrackObject to release
637  *
638  * Release the @trackobject from the control of @object.
639  *
640  * Returns: %TRUE if the @trackobject was properly released, else %FALSE.
641  */
642 gboolean
643 ges_timeline_object_release_track_object (GESTimelineObject * object,
644     GESTrackObject * trackobject)
645 {
646   GList *tmp;
647   ObjectMapping *mapping = NULL;
648   GESTimelineObjectClass *klass;
649
650   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
651   g_return_val_if_fail (GES_IS_TRACK_OBJECT (trackobject), FALSE);
652
653   GST_DEBUG ("object:%p, trackobject:%p", object, trackobject);
654   klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
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   GESTimeline *timeline = NULL;
774   GESTimelineObjectPrivate *priv = object->priv;
775   gboolean snap = FALSE;
776
777   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
778
779   GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
780       object, GST_TIME_ARGS (start));
781
782   /* If the class has snapping enabled and the object is in a timeline,
783    * we snap */
784   if (priv->layer && GES_TIMELINE_OBJECT_GET_CLASS (object)->snaps)
785     timeline = ges_timeline_layer_get_timeline (object->priv->layer);
786   snap = timeline && priv->initiated_move == NULL ? TRUE : FALSE;
787
788   object->priv->ignore_notifies = TRUE;
789
790   for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
791     tr = (GESTrackObject *) tmp->data;
792     map = find_object_mapping (object, tr);
793
794     if (ges_track_object_is_locked (tr) && tr != object->priv->initiated_move) {
795       gint64 new_start = start - map->start_offset;
796
797       /* Move the child... */
798       if (new_start < 0) {
799         GST_ERROR ("Trying to set start to a negative value %" GST_TIME_FORMAT,
800             GST_TIME_ARGS (-(start + map->start_offset)));
801         continue;
802       }
803
804       /* Make the snapping happen if in a timeline */
805       if (snap)
806         ges_timeline_move_object_simple (timeline, tr, NULL, GES_EDGE_NONE,
807             start);
808       else
809         ges_track_object_set_start (tr, start);
810     } else {
811       /* ... or update the offset */
812       map->start_offset = start - tr->start;
813     }
814   }
815
816   object->priv->ignore_notifies = FALSE;
817
818   object->start = start;
819   return TRUE;
820 }
821
822 /**
823  * ges_timeline_object_set_start:
824  * @object: a #GESTimelineObject
825  * @start: the position in #GstClockTime
826  *
827  * Set the position of the object in its containing layer
828  *
829  * Note that if the timeline snap-distance property of the timeline containing
830  * @object is set, @object will properly snap to its neighboors.
831  */
832 void
833 ges_timeline_object_set_start (GESTimelineObject * object, guint64 start)
834 {
835   if (ges_timeline_object_set_start_internal (object, start))
836 #if GLIB_CHECK_VERSION(2,26,0)
837     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
838 #else
839     g_object_notify (G_OBJECT (object), "start");
840 #endif
841 }
842
843 static gboolean
844 ges_timeline_object_set_inpoint_internal (GESTimelineObject * object,
845     guint64 inpoint)
846 {
847   GList *tmp;
848   GESTrackObject *tr;
849
850   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
851
852   GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
853       object, GST_TIME_ARGS (inpoint));
854
855   for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
856     tr = (GESTrackObject *) tmp->data;
857
858     if (ges_track_object_is_locked (tr))
859       /* call set_inpoint on each trackobject */
860       ges_track_object_set_inpoint (tr, inpoint);
861   }
862
863   object->inpoint = inpoint;
864   return TRUE;
865 }
866
867 /**
868  * ges_timeline_object_set_inpoint:
869  * @object: a #GESTimelineObject
870  * @inpoint: the in-point in #GstClockTime
871  *
872  * Set the in-point, that is the moment at which the @object will start
873  * outputting data from its contents.
874  */
875 void
876 ges_timeline_object_set_inpoint (GESTimelineObject * object, guint64 inpoint)
877 {
878   if (ges_timeline_object_set_inpoint_internal (object, inpoint))
879 #if GLIB_CHECK_VERSION(2,26,0)
880     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
881 #else
882     g_object_notify (G_OBJECT (object), "in-point");
883 #endif
884 }
885
886 static gboolean
887 ges_timeline_object_set_duration_internal (GESTimelineObject * object,
888     guint64 duration)
889 {
890   GList *tmp;
891   GESTrackObject *tr;
892   GESTimeline *timeline = NULL;
893   GESTimelineObjectPrivate *priv = object->priv;
894   gboolean snap = FALSE;
895
896   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
897
898   GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
899       object, GST_TIME_ARGS (duration));
900
901   if (priv->layer && GES_TIMELINE_OBJECT_GET_CLASS (object)->snaps)
902     timeline = ges_timeline_layer_get_timeline (object->priv->layer);
903
904   /* If the class has snapping enabled, the object is in a timeline,
905    * and we are not following a moved TrackObject, we snap */
906   snap = timeline && priv->initiated_move == NULL ? TRUE : FALSE;
907
908   object->priv->ignore_notifies = TRUE;
909   for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
910     tr = (GESTrackObject *) tmp->data;
911
912     if (ges_track_object_is_locked (tr)) {
913       /* call set_duration on each trackobject
914        * and make the snapping happen if in a timeline */
915       if (G_LIKELY (snap))
916         ges_timeline_trim_object_simple (timeline, tr, NULL, GES_EDGE_END,
917             tr->start + duration, TRUE);
918       else
919         ges_track_object_set_duration (tr, duration);
920     }
921   }
922   object->priv->ignore_notifies = FALSE;
923
924   object->duration = duration;
925   return TRUE;
926 }
927
928 /**
929  * ges_timeline_object_set_duration:
930  * @object: a #GESTimelineObject
931  * @duration: the duration in #GstClockTime
932  *
933  * Set the duration of the object
934  *
935  * Note that if the timeline snap-distance property of the timeline containing
936  * @object is set, @object will properly snap to its neighboors.
937  */
938 void
939 ges_timeline_object_set_duration (GESTimelineObject * object, guint64 duration)
940 {
941   if (ges_timeline_object_set_duration_internal (object, duration))
942 #if GLIB_CHECK_VERSION(2,26,0)
943     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
944 #else
945     g_object_notify (G_OBJECT (object), "duration");
946 #endif
947 }
948
949 static gboolean
950 ges_timeline_object_set_priority_internal (GESTimelineObject * object,
951     guint priority)
952 {
953   GList *tmp;
954   GESTrackObject *tr;
955   ObjectMapping *map;
956   GESTimelineObjectPrivate *priv;
957   guint32 layer_min_gnl_prio, layer_max_gnl_prio;
958
959   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
960
961   GST_DEBUG ("object:%p, priority:%" G_GUINT32_FORMAT, object, priority);
962
963   priv = object->priv;
964
965   get_layer_priorities (priv->layer, &layer_min_gnl_prio, &layer_max_gnl_prio);
966
967   priv->ignore_notifies = TRUE;
968   for (tmp = priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
969     tr = (GESTrackObject *) tmp->data;
970     map = find_object_mapping (object, tr);
971
972     if (ges_track_object_is_locked (tr)) {
973       guint32 real_tck_prio;
974
975       /* Move the child... */
976       real_tck_prio = layer_min_gnl_prio + priority + map->priority_offset;
977
978       if (real_tck_prio > layer_max_gnl_prio) {
979         GST_WARNING ("%p priority of %i, is outside of the its containing "
980             "layer space. (%d/%d) setting it to the maximum it can be", object,
981             priority, layer_min_gnl_prio, layer_max_gnl_prio);
982
983         real_tck_prio = layer_max_gnl_prio;
984       }
985
986       ges_track_object_set_priority (tr, real_tck_prio);
987
988     } else {
989       /* ... or update the offset */
990       map->priority_offset = tr->priority - layer_min_gnl_prio + priority;
991     }
992   }
993
994   priv->trackobjects = g_list_sort_with_data (priv->trackobjects,
995       (GCompareDataFunc) sort_track_effects, object);
996   priv->ignore_notifies = FALSE;
997
998   object->priority = priority;
999   return TRUE;
1000 }
1001
1002 /**
1003  * ges_timeline_object_set_moving_from_layer:
1004  * @object: a #GESTimelineObject
1005  * @is_moving: %TRUE if you want to start moving @object to another layer
1006  * %FALSE when you finished moving it.
1007  *
1008  * Sets the object in a moving to layer state. You might rather use the
1009  * ges_timeline_object_move_to_layer function to move #GESTimelineObject-s
1010  * from a layer to another.
1011  **/
1012 void
1013 ges_timeline_object_set_moving_from_layer (GESTimelineObject * object,
1014     gboolean is_moving)
1015 {
1016   g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
1017
1018   object->priv->is_moving = is_moving;
1019 }
1020
1021 /**
1022  * ges_timeline_object_is_moving_from_layer:
1023  * @object: a #GESTimelineObject
1024  *
1025  * Tells you if the object is currently moving from a layer to another.
1026  * You might rather use the ges_timeline_object_move_to_layer function to
1027  * move #GESTimelineObject-s from a layer to another.
1028  *
1029  *
1030  * Returns: %TRUE if @object is currently moving from its current layer
1031  * %FALSE otherwize
1032  **/
1033 gboolean
1034 ges_timeline_object_is_moving_from_layer (GESTimelineObject * object)
1035 {
1036   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1037
1038   return object->priv->is_moving;
1039 }
1040
1041 /**
1042  * ges_timeline_object_move_to_layer:
1043  * @object: a #GESTimelineObject
1044  * @layer: the new #GESTimelineLayer
1045  *
1046  * Moves @object to @layer. If @object is not in any layer, it adds it to
1047  * @layer, else, it removes it from its current layer, and adds it to @layer.
1048  *
1049  * Returns: %TRUE if @object could be moved %FALSE otherwize
1050  */
1051 gboolean
1052 ges_timeline_object_move_to_layer (GESTimelineObject * object, GESTimelineLayer
1053     * layer)
1054 {
1055   gboolean ret;
1056   GESTimelineLayer *current_layer;
1057
1058   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1059   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
1060
1061   current_layer = object->priv->layer;
1062
1063   if (current_layer == NULL) {
1064     GST_DEBUG ("Not moving %p, only adding it to %p", object, layer);
1065
1066     return ges_timeline_layer_add_object (layer, object);
1067   }
1068
1069   GST_DEBUG_OBJECT (object, "moving to layer %p, priority: %d", layer,
1070       ges_timeline_layer_get_priority (layer));
1071
1072   object->priv->is_moving = TRUE;
1073   g_object_ref (object);
1074   ret = ges_timeline_layer_remove_object (current_layer, object);
1075
1076   if (!ret) {
1077     g_object_unref (object);
1078     return FALSE;
1079   }
1080
1081   ret = ges_timeline_layer_add_object (layer, object);
1082   object->priv->is_moving = FALSE;
1083
1084   g_object_unref (object);
1085
1086   return ret;
1087 }
1088
1089 /**
1090  * ges_timeline_object_set_priority:
1091  * @object: a #GESTimelineObject
1092  * @priority: the priority
1093  *
1094  * Sets the priority of the object within the containing layer
1095  */
1096 void
1097 ges_timeline_object_set_priority (GESTimelineObject * object, guint priority)
1098 {
1099   if (ges_timeline_object_set_priority_internal (object, priority))
1100 #if GLIB_CHECK_VERSION(2,26,0)
1101     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_PRIORITY]);
1102 #else
1103     g_object_notify (G_OBJECT (object), "priority");
1104 #endif
1105 }
1106
1107 /**
1108  * ges_timeline_object_find_track_object:
1109  * @object: a #GESTimelineObject
1110  * @track: a #GESTrack or NULL
1111  * @type: a #GType indicating the type of track object you are looking
1112  * for or %G_TYPE_NONE if you do not care about the track type.
1113  *
1114  * Finds the #GESTrackObject controlled by @object that is used in @track. You
1115  * may optionally specify a GType to further narrow search criteria.
1116  *
1117  * Note: If many objects match, then the one with the highest priority will be
1118  * returned.
1119  *
1120  * Returns: (transfer full): The #GESTrackObject used by @track, else %NULL.
1121  * Unref after usage.
1122  */
1123
1124 GESTrackObject *
1125 ges_timeline_object_find_track_object (GESTimelineObject * object,
1126     GESTrack * track, GType type)
1127 {
1128   GESTrackObject *ret = NULL;
1129   GList *tmp;
1130   GESTrackObject *otmp;
1131
1132   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1133   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
1134
1135   for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
1136     otmp = (GESTrackObject *) tmp->data;
1137
1138     if (ges_track_object_get_track (otmp) == track) {
1139       if ((type != G_TYPE_NONE) &&
1140           !G_TYPE_CHECK_INSTANCE_TYPE (tmp->data, type))
1141         continue;
1142
1143       ret = GES_TRACK_OBJECT (tmp->data);
1144       g_object_ref (ret);
1145       break;
1146     }
1147   }
1148
1149   return ret;
1150 }
1151
1152 /**
1153  * ges_timeline_object_get_layer:
1154  * @object: a #GESTimelineObject
1155  *
1156  * Get the #GESTimelineLayer to which this object belongs.
1157  *
1158  * Returns: (transfer full): The #GESTimelineLayer where this @object is being
1159  * used, or %NULL if it is not used on any layer. The caller should unref it
1160  * usage.
1161  */
1162 GESTimelineLayer *
1163 ges_timeline_object_get_layer (GESTimelineObject * object)
1164 {
1165   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1166
1167   if (object->priv->layer != NULL)
1168     g_object_ref (G_OBJECT (object->priv->layer));
1169
1170   return object->priv->layer;
1171 }
1172
1173 /**
1174  * ges_timeline_object_get_track_objects:
1175  * @object: a #GESTimelineObject
1176  *
1177  * Get the list of #GESTrackObject contained in @object
1178  *
1179  * Returns: (transfer full) (element-type GESTrackObject): The list of
1180  * trackobject contained in @object.
1181  * The user is responsible for unreffing the contained objects
1182  * and freeing the list.
1183  */
1184 GList *
1185 ges_timeline_object_get_track_objects (GESTimelineObject * object)
1186 {
1187   GList *ret;
1188   GList *tmp;
1189
1190   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1191
1192   ret = g_list_copy (object->priv->trackobjects);
1193
1194   for (tmp = ret; tmp; tmp = tmp->next) {
1195     g_object_ref (tmp->data);
1196   }
1197
1198   return ret;
1199 }
1200
1201 static gint
1202 sort_track_effects (gpointer a, gpointer b, GESTimelineObject * object)
1203 {
1204   guint prio_offset_a, prio_offset_b;
1205   ObjectMapping *map_a, *map_b;
1206   GESTrackObject *obj_a, *obj_b;
1207
1208   obj_a = GES_TRACK_OBJECT (a);
1209   obj_b = GES_TRACK_OBJECT (b);
1210
1211   map_a = find_object_mapping (object, obj_a);
1212   map_b = find_object_mapping (object, obj_b);
1213
1214   prio_offset_a = map_a->priority_offset;
1215   prio_offset_b = map_b->priority_offset;
1216
1217   if ((gint) prio_offset_a > (guint) prio_offset_b)
1218     return 1;
1219   if ((guint) prio_offset_a < (guint) prio_offset_b)
1220     return -1;
1221
1222   return 0;
1223 }
1224
1225 /**
1226  * ges_timeline_object_get_top_effects:
1227  * @object: The origin #GESTimelineObject
1228  *
1229  * Get effects applied on @object
1230  *
1231  * Returns: (transfer full) (element-type GESTrackObject): a #GList of the
1232  * #GESTrackEffect that are applied on @object order by ascendant priorities.
1233  * The refcount of the objects will be increased. The user will have to
1234  * unref each #GESTrackEffect and free the #GList.
1235  *
1236  * Since: 0.10.2
1237  */
1238 GList *
1239 ges_timeline_object_get_top_effects (GESTimelineObject * object)
1240 {
1241   GList *tmp, *ret;
1242   guint i;
1243
1244   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1245
1246   GST_DEBUG_OBJECT (object, "Getting the %i top effects",
1247       object->priv->nb_effects);
1248   ret = NULL;
1249
1250   for (tmp = object->priv->trackobjects, i = 0; i < object->priv->nb_effects;
1251       tmp = tmp->next, i++) {
1252     ret = g_list_append (ret, g_object_ref (tmp->data));
1253   }
1254
1255   return ret;
1256 }
1257
1258 /**
1259  * ges_timeline_object_get_top_effect_position:
1260  * @object: The origin #GESTimelineObject
1261  * @effect: The #GESTrackEffect we want to get the top position from
1262  *
1263  * Gets the top position of an effect.
1264  *
1265  * Returns: The top position of the effect, -1 if something went wrong.
1266  *
1267  * Since: 0.10.2
1268  */
1269 gint
1270 ges_timeline_object_get_top_effect_position (GESTimelineObject * object,
1271     GESTrackEffect * effect)
1272 {
1273   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), -1);
1274
1275   return find_object_mapping (object,
1276       GES_TRACK_OBJECT (effect))->priority_offset;
1277 }
1278
1279 /**
1280  * ges_timeline_object_set_top_effect_priority:
1281  * @object: The origin #GESTimelineObject
1282  * @effect: The #GESTrackEffect to move
1283  * @newpriority: the new position at which to move the @effect inside this
1284  * #GESTimelineObject
1285  *
1286  * This is a convenience method that lets you set the priority of a top effect.
1287  *
1288  * Returns: %TRUE if @effect was successfuly moved, %FALSE otherwise.
1289  *
1290  * Since: 0.10.2
1291  */
1292 gboolean
1293 ges_timeline_object_set_top_effect_priority (GESTimelineObject * object,
1294     GESTrackEffect * effect, guint newpriority)
1295 {
1296   gint inc;
1297   GList *tmp;
1298   guint current_prio;
1299   GESTrackObject *tck_obj;
1300   GESTimelineObjectPrivate *priv;
1301
1302   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1303
1304   tck_obj = GES_TRACK_OBJECT (effect);
1305   priv = object->priv;
1306   current_prio = ges_track_object_get_priority (tck_obj);
1307
1308   /*  We don't change the priority */
1309   if (current_prio == newpriority ||
1310       (G_UNLIKELY (ges_track_object_get_timeline_object (tck_obj) != object)))
1311     return FALSE;
1312
1313   if (newpriority > (object->priv->nb_effects - 1)) {
1314     GST_DEBUG ("You are trying to make %p not a top effect", effect);
1315     return FALSE;
1316   }
1317
1318   if (current_prio > object->priv->nb_effects) {
1319     GST_DEBUG ("%p is not a top effect");
1320     return FALSE;
1321   }
1322
1323   if (tck_obj->priority < newpriority)
1324     inc = -1;
1325   else
1326     inc = +1;
1327
1328   ges_track_object_set_priority (tck_obj, newpriority);
1329   for (tmp = priv->trackobjects; tmp; tmp = tmp->next) {
1330     GESTrackObject *tmpo = GES_TRACK_OBJECT (tmp->data);
1331     guint tck_priority = ges_track_object_get_priority (tmpo);
1332
1333     if ((inc == +1 && tck_priority >= newpriority) ||
1334         (inc == -1 && tck_priority <= newpriority)) {
1335       ges_track_object_set_priority (tmpo, tck_priority + inc);
1336     }
1337   }
1338
1339   priv->trackobjects = g_list_sort_with_data (priv->trackobjects,
1340       (GCompareDataFunc) sort_track_effects, object);
1341
1342   return TRUE;
1343 }
1344
1345 /**
1346  * ges_timeline_object_edit:
1347  * @object: the #GESTimelineObject to edit
1348  * @layers: (element-type GESTimelineLayer): The layers you want the edit to
1349  *  happen in, %NULL means that the edition is done in all the
1350  *  #GESTimelineLayers contained in the current timeline.
1351  * @new_layer_priority: The priority of the layer @object should land in.
1352  *  If the layer you're trying to move the object to doesn't exist, it will
1353  *  be created automatically. -1 means no move.
1354  * @mode: The #GESEditMode in which the editition will happen.
1355  * @edge: The #GESEdge the edit should happen on.
1356  * @position: The position at which to edit @object (in nanosecond)
1357  *
1358  * Edit @object in the different exisiting #GESEditMode modes. In the case of
1359  * slide, and roll, you need to specify a #GESEdge
1360  *
1361  * Returns: %TRUE if the object as been edited properly, %FALSE if an error
1362  * occured
1363  *
1364  * Since: 0.10.XX
1365  */
1366 gboolean
1367 ges_timeline_object_edit (GESTimelineObject * object, GList * layers,
1368     gint new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
1369 {
1370   GList *tmp;
1371   gboolean ret = TRUE;
1372   GESTimelineLayer *layer;
1373
1374   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1375
1376   if (!G_UNLIKELY (object->priv->trackobjects)) {
1377     GST_WARNING_OBJECT (object, "Trying to edit, but not containing"
1378         "any TrackObject yet.");
1379     return FALSE;
1380   } else if (position < 0) {
1381     GST_DEBUG_OBJECT (object, "Trying to move before 0, not moving");
1382   }
1383
1384   for (tmp = object->priv->trackobjects; tmp; tmp = g_list_next (tmp)) {
1385     if (ges_track_object_is_locked (tmp->data)) {
1386       ret &= ges_track_object_edit (tmp->data, layers, mode, edge, position);
1387       break;
1388     }
1389   }
1390
1391   /* Moving to layer */
1392   if (new_layer_priority == -1) {
1393     GST_DEBUG_OBJECT (object, "Not moving new prio %d", new_layer_priority);
1394   } else {
1395     gint priority_offset;
1396
1397     layer = object->priv->layer;
1398     if (layer == NULL) {
1399       GST_WARNING_OBJECT (object, "Not in any layer yet, not moving");
1400
1401       return FALSE;
1402     }
1403     priority_offset = new_layer_priority -
1404         ges_timeline_layer_get_priority (layer);
1405
1406     ret &= timeline_context_to_layer (layer->timeline, priority_offset);
1407   }
1408
1409   return ret;
1410 }
1411
1412 /**
1413  * ges_timeline_object_split:
1414  * @object: the #GESTimelineObject to split
1415  * @position: a #GstClockTime representing the position at which to split
1416  * @object
1417  *
1418  * The function modifies @object, and creates another #GESTimelineObject so
1419  * we have two clips at the end, splitted at the time specified by @position.
1420  *
1421  * Returns: (transfer full): The newly created #GESTimelineObject resulting from
1422  * the splitting
1423  *
1424  * Since: 0.10.XX
1425  */
1426 GESTimelineObject *
1427 ges_timeline_object_split (GESTimelineObject * object, guint64 position)
1428 {
1429   GList *tmp;
1430   gboolean locked;
1431   GESTimelineObject *new_object;
1432   GESTimelineObjectPrivate *priv;
1433
1434   GstClockTime start, inpoint, duration;
1435
1436   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1437   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (position), NULL);
1438
1439   priv = object->priv;
1440
1441   duration = GES_TIMELINE_OBJECT_DURATION (object);
1442   start = GES_TIMELINE_OBJECT_START (object);
1443   inpoint = GES_TIMELINE_OBJECT_INPOINT (object);
1444
1445   if (position >= start + duration || position <= start) {
1446     GST_WARNING_OBJECT (object, "Can not split %" GST_TIME_FORMAT
1447         " out of boundaries", GST_TIME_ARGS (position));
1448     return NULL;
1449   }
1450
1451   GST_DEBUG_OBJECT (object, "Spliting at %" GST_TIME_FORMAT,
1452       GST_TIME_ARGS (position));
1453
1454   /* Create the new TimelineObject */
1455   new_object = ges_timeline_object_copy (object, FALSE);
1456
1457   /* Set new timing properties on the TimelineObject */
1458   ges_timeline_object_set_start (new_object, position);
1459   ges_timeline_object_set_inpoint (new_object, object->inpoint +
1460       duration - (duration + start - position));
1461   ges_timeline_object_set_duration (new_object, duration + start - position);
1462
1463   if (object->priv->layer) {
1464     /* We do not want the timeline to create again TrackObject-s */
1465     ges_timeline_object_set_moving_from_layer (new_object, TRUE);
1466     ges_timeline_layer_add_object (object->priv->layer, new_object);
1467     ges_timeline_object_set_moving_from_layer (new_object, FALSE);
1468   }
1469
1470   /* We first set the new duration and the child mapping will be updated
1471    * properly in the following loop */
1472   object->duration = position - object->start;
1473   for (tmp = priv->trackobjects; tmp; tmp = tmp->next) {
1474     GESTrack *track;
1475
1476     GESTrackObject *new_tckobj, *tckobj = GES_TRACK_OBJECT (tmp->data);
1477
1478     duration = ges_track_object_get_duration (tckobj);
1479     start = ges_track_object_get_start (tckobj);
1480     inpoint = ges_track_object_get_inpoint (tckobj);
1481
1482     if (position <= start || position >= (start + duration)) {
1483       GST_DEBUG_OBJECT (tckobj, "Outside %" GST_TIME_FORMAT "the boundaries "
1484           "not copying it ( start %" GST_TIME_FORMAT ", end %" GST_TIME_FORMAT
1485           ")", GST_TIME_ARGS (position), GST_TIME_ARGS (tckobj->start),
1486           GST_TIME_ARGS (tckobj->start + tckobj->duration));
1487       continue;
1488     }
1489
1490     new_tckobj = ges_track_object_copy (tckobj, TRUE);
1491     if (new_tckobj == NULL) {
1492       GST_WARNING_OBJECT (tckobj, "Could not create a copy");
1493       continue;
1494     }
1495
1496     ges_timeline_object_add_track_object (new_object, new_tckobj);
1497
1498     track = ges_track_object_get_track (tckobj);
1499     if (track == NULL)
1500       GST_DEBUG_OBJECT (tckobj, "Was not in a track, not adding %p to"
1501           "any track", new_tckobj);
1502     else
1503       ges_track_add_object (track, new_tckobj);
1504
1505     /* Unlock TrackObject-s as we do not want the container to move
1506      * syncronously */
1507     locked = ges_track_object_is_locked (tckobj);
1508     ges_track_object_set_locked (new_tckobj, FALSE);
1509     ges_track_object_set_locked (tckobj, FALSE);
1510
1511     /* Set 'new' track object timing propeties */
1512     ges_track_object_set_start (new_tckobj, position);
1513     ges_track_object_set_inpoint (new_tckobj, inpoint + duration - (duration +
1514             start - position));
1515     ges_track_object_set_duration (new_tckobj, duration + start - position);
1516
1517     /* Set 'old' track object duration */
1518     ges_track_object_set_duration (tckobj, position - start);
1519
1520     /* And let track objects in the same locking state as before. */
1521     ges_track_object_set_locked (tckobj, locked);
1522     ges_track_object_set_locked (new_tckobj, locked);
1523   }
1524
1525   return new_object;
1526 }
1527
1528 /* TODO implement the deep parameter, and make it public */
1529 static GESTimelineObject *
1530 ges_timeline_object_copy (GESTimelineObject * object, gboolean * deep)
1531 {
1532   GESTimelineObject *ret = NULL;
1533   GParameter *params;
1534   GParamSpec **specs;
1535   guint n, n_specs, n_params;
1536
1537   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), NULL);
1538
1539   specs =
1540       g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &n_specs);
1541   params = g_new0 (GParameter, n_specs);
1542   n_params = 0;
1543
1544   for (n = 0; n < n_specs; ++n) {
1545     if (strcmp (specs[n]->name, "parent") &&
1546         (specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) {
1547       params[n_params].name = g_intern_string (specs[n]->name);
1548       g_value_init (&params[n_params].value, specs[n]->value_type);
1549       g_object_get_property (G_OBJECT (object), specs[n]->name,
1550           &params[n_params].value);
1551       ++n_params;
1552     }
1553   }
1554
1555   ret = g_object_newv (G_TYPE_FROM_INSTANCE (object), n_params, params);
1556
1557   g_free (specs);
1558   g_free (params);
1559
1560   return ret;
1561 }
1562
1563 /**
1564  * ges_timeline_object_set_supported_formats:
1565  * @object: the #GESTimelineObject to set supported formats on
1566  * @supportedformats: the #GESTrackType defining formats supported by @object
1567  *
1568  * Sets the formats supported by the file.
1569  *
1570  * Since: 0.10.XX
1571  */
1572 void
1573 ges_timeline_object_set_supported_formats (GESTimelineObject * object,
1574     GESTrackType supportedformats)
1575 {
1576   g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
1577
1578   object->priv->supportedformats = supportedformats;
1579 }
1580
1581 /**
1582  * ges_timeline_object_get_supported_formats:
1583  * @object: the #GESTimelineObject
1584  *
1585  * Get the formats supported by @object.
1586  *
1587  * Returns: The formats supported by @object.
1588  *
1589  * Since: 0.10.XX
1590  */
1591 GESTrackType
1592 ges_timeline_object_get_supported_formats (GESTimelineObject * object)
1593 {
1594   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object),
1595       GES_TRACK_TYPE_UNKNOWN);
1596
1597   return object->priv->supportedformats;
1598 }
1599
1600 /**
1601  * ges_timeline_object_objects_set_locked:
1602  * @object: the #GESTimelineObject
1603  * @locked: whether the #GESTrackObject contained in @object are locked to it.
1604  *
1605  * Set the locking status of all the #GESTrackObject contained in @object to @locked.
1606  * See the ges_track_object_set_locked documentation for more details.
1607  *
1608  * Since: 0.10.XX
1609  */
1610 void
1611 ges_timeline_object_objects_set_locked (GESTimelineObject * object,
1612     gboolean locked)
1613 {
1614   GList *tmp;
1615
1616   g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
1617
1618   for (tmp = object->priv->mappings; tmp; tmp = g_list_next (tmp)) {
1619     ges_track_object_set_locked (((ObjectMapping *) tmp->data)->object, locked);
1620   }
1621 }
1622
1623 /**
1624  * ges_timeline_object_get_max_duration:
1625  * @object: The #GESTimelineObject to retrieve max duration from
1626  *
1627  * Get the max duration of @object.
1628  *
1629  * Returns: The max duration of @object
1630  *
1631  * Since: 0.10.XX
1632  */
1633 guint64
1634 ges_timeline_object_get_max_duration (GESTimelineObject * object)
1635 {
1636   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), 0);
1637
1638   return object->priv->maxduration;
1639 }
1640
1641 /**
1642  * ges_timeline_object_set_max_duration:
1643  * @object: The #GESTimelineObject to retrieve max duration from
1644  * @maxduration: The maximum duration of @object
1645  *
1646  * Returns: Set the max duration of @object
1647  *
1648  * Since: 0.10.XX
1649  */
1650 void
1651 ges_timeline_object_set_max_duration (GESTimelineObject * object,
1652     guint64 maxduration)
1653 {
1654   GESTimelineObjectClass *klass = GES_TIMELINE_OBJECT_GET_CLASS (object);
1655
1656   g_return_if_fail (GES_IS_TIMELINE_OBJECT (object));
1657
1658   object->priv->maxduration = maxduration;
1659   klass->set_max_duration (object, maxduration);
1660 }
1661
1662 /**
1663  * ges_timeline_object_ripple:
1664  * @object: The #GESTimeline to ripple.
1665  * @start: The new start of @object in ripple mode.
1666  *
1667  * Edits @object in ripple mode. It allows you to modify the
1668  * start of @object and move the following neighbours accordingly.
1669  * This will change the overall timeline duration.
1670  *
1671  * You could also use:
1672  *
1673  *    #ges_timeline_object_edit (@object, @layers,
1674  *        new_layer_priority=-1, GES_EDIT_MODE_RIPPLE, GES_EDGE_NONE,
1675  *        @position);
1676  *
1677  * Which lets you more control over layer management.
1678  *
1679  * Returns: %TRUE if the object as been rippled properly, %FALSE if an error
1680  * occured
1681  */
1682 gboolean
1683 ges_timeline_object_ripple (GESTimelineObject * object, guint64 start)
1684 {
1685   GList *tmp, *tckobjs;
1686   gboolean ret = TRUE;
1687   GESTimeline *timeline;
1688
1689   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1690
1691   timeline = ges_timeline_layer_get_timeline (object->priv->layer);
1692
1693   if (timeline == NULL) {
1694     GST_DEBUG ("Not in a timeline yet");
1695     return FALSE;
1696   }
1697
1698   tckobjs = ges_timeline_object_get_track_objects (object);
1699   for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
1700     if (ges_track_object_is_locked (tmp->data)) {
1701       ret = timeline_ripple_object (timeline, GES_TRACK_OBJECT (tmp->data),
1702           NULL, GES_EDGE_NONE, start);
1703       /* As we work only with locked objects, the changes will be reflected
1704        * to others controlled TrackObjects */
1705       break;
1706     }
1707   }
1708   g_list_free_full (tckobjs, g_object_unref);
1709
1710   return ret;
1711 }
1712
1713 /**
1714  * ges_timeline_object_ripple_end:
1715  * @object: The #GESTimeline to ripple.
1716  * @end: The new end (start + duration) of @object in ripple mode. It will
1717  *       basically only change the duration of @object.
1718  *
1719  * Edits @object in ripple mode. It allows you to modify the
1720  * duration of a @object and move the following neighbours accordingly.
1721  * This will change the overall timeline duration.
1722  *
1723  * You could also use:
1724  *
1725  *    #ges_timeline_object_edit (@object, @layers,
1726  *        new_layer_priority=-1, GES_EDIT_MODE_RIPPLE, GES_EDGE_END, @end);
1727  *
1728  * Which lets you more control over layer management.
1729  *
1730  * Returns: %TRUE if the object as been rippled properly, %FALSE if an error
1731  * occured
1732  */
1733 gboolean
1734 ges_timeline_object_ripple_end (GESTimelineObject * object, guint64 end)
1735 {
1736   GList *tmp, *tckobjs;
1737   gboolean ret = TRUE;
1738   GESTimeline *timeline;
1739
1740   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1741
1742   timeline = ges_timeline_layer_get_timeline (object->priv->layer);
1743
1744   if (timeline == NULL) {
1745     GST_DEBUG ("Not in a timeline yet");
1746     return FALSE;
1747   }
1748
1749   tckobjs = ges_timeline_object_get_track_objects (object);
1750   for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
1751     if (ges_track_object_is_locked (tmp->data)) {
1752       ret = timeline_ripple_object (timeline, GES_TRACK_OBJECT (tmp->data),
1753           NULL, GES_EDGE_END, end);
1754       /* As we work only with locked objects, the changes will be reflected
1755        * to others controlled TrackObjects */
1756       break;
1757     }
1758   }
1759   g_list_free_full (tckobjs, g_object_unref);
1760
1761   return ret;
1762 }
1763
1764 /**
1765  * ges_timeline_object_roll_start:
1766  * @start: The new start of @object in roll mode, it will also adapat
1767  * the in-point of @object according
1768  *
1769  * Edits @object in roll mode. It allows you to modify the
1770  * start and inpoint of a @object and "resize" (basicly change the duration
1771  * in this case) of the previous neighbours accordingly.
1772  * This will not change the overall timeline duration.
1773  *
1774  * You could also use:
1775  *
1776  *    #ges_timeline_object_edit (@object, @layers,
1777  *        new_layer_priority=-1, GES_EDIT_MODE_ROLL, GES_EDGE_START, @start);
1778  *
1779  * Which lets you more control over layer management.
1780  *
1781  * Returns: %TRUE if the object as been roll properly, %FALSE if an error
1782  * occured
1783  */
1784 gboolean
1785 ges_timeline_object_roll_start (GESTimelineObject * object, guint64 start)
1786 {
1787   GList *tmp, *tckobjs;
1788   gboolean ret = TRUE;
1789   GESTimeline *timeline;
1790
1791   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1792
1793   timeline = ges_timeline_layer_get_timeline (object->priv->layer);
1794
1795   if (timeline == NULL) {
1796     GST_DEBUG ("Not in a timeline yet");
1797     return FALSE;
1798   }
1799
1800   tckobjs = ges_timeline_object_get_track_objects (object);
1801   for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
1802     if (ges_track_object_is_locked (tmp->data)) {
1803       ret = timeline_roll_object (timeline, GES_TRACK_OBJECT (tmp->data),
1804           NULL, GES_EDGE_START, start);
1805       /* As we work only with locked objects, the changes will be reflected
1806        * to others controlled TrackObjects */
1807       break;
1808     }
1809   }
1810   g_list_free_full (tckobjs, g_object_unref);
1811
1812   return ret;
1813 }
1814
1815 /**
1816  * ges_timeline_object_roll_end:
1817  * @object: The #GESTimeline to roll.
1818  * @end: The new end (start + duration) of @object in roll mode
1819  *
1820  * Edits @object in roll mode. It allows you to modify the
1821  * duration of a @object and trim (basicly change the start + inpoint
1822  * in this case) the following neighbours accordingly.
1823  * This will not change the overall timeline duration.
1824  *
1825  * You could also use:
1826  *
1827  *    #ges_timeline_object_edit (@object, @layers,
1828  *        new_layer_priority=-1, GES_EDIT_MODE_ROLL, GES_EDGE_END, @end);
1829  *
1830  * Which lets you more control over layer management.
1831  *
1832  * Returns: %TRUE if the object as been rolled properly, %FALSE if an error
1833  * occured
1834  */
1835 gboolean
1836 ges_timeline_object_roll_end (GESTimelineObject * object, guint64 end)
1837 {
1838   GList *tmp, *tckobjs;
1839   gboolean ret = TRUE;
1840   GESTimeline *timeline;
1841
1842   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1843
1844   timeline = ges_timeline_layer_get_timeline (object->priv->layer);
1845
1846   if (timeline == NULL) {
1847     GST_DEBUG ("Not in a timeline yet");
1848     return FALSE;
1849   }
1850
1851
1852   tckobjs = ges_timeline_object_get_track_objects (object);
1853   for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
1854     if (ges_track_object_is_locked (tmp->data)) {
1855       ret = timeline_roll_object (timeline, GES_TRACK_OBJECT (tmp->data),
1856           NULL, GES_EDGE_END, end);
1857       /* As we work only with locked objects, the changes will be reflected
1858        * to others controlled TrackObjects */
1859       break;
1860     }
1861   }
1862   g_list_free_full (tckobjs, g_object_unref);
1863
1864   return ret;
1865 }
1866
1867 /**
1868  * ges_timeline_object_trim_start:
1869  * @object: The #GESTimeline to trim.
1870  * @start: The new start of @object in trim mode, will adapt the inpoint
1871  * of @object accordingly
1872  *
1873  * Edits @object in trim mode. It allows you to modify the
1874  * inpoint and start of @object.
1875  * This will not change the overall timeline duration.
1876  *
1877  * You could also use:
1878  *
1879  *    #ges_timeline_object_edit (@object, @layers,
1880  *        new_layer_priority=-1, GES_EDIT_MODE_TRIM, GES_EDGE_START, @start);
1881  *
1882  * Which lets you more control over layer management.
1883  *
1884  * Note that to trim the end of an object you can just set its duration. The same way
1885  * as this method, it will take into account the snapping-distance property of the
1886  * timeline in which @object is.
1887  *
1888  * Returns: %TRUE if the object as been trimmed properly, %FALSE if an error
1889  * occured
1890  */
1891 gboolean
1892 ges_timeline_object_trim_start (GESTimelineObject * object, guint64 start)
1893 {
1894   GList *tmp, *tckobjs;
1895   gboolean ret = TRUE;
1896   GESTimeline *timeline;
1897
1898   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
1899
1900   timeline = ges_timeline_layer_get_timeline (object->priv->layer);
1901
1902   if (timeline == NULL) {
1903     GST_DEBUG ("Not in a timeline yet");
1904     return FALSE;
1905   }
1906
1907   tckobjs = ges_timeline_object_get_track_objects (object);
1908   for (tmp = tckobjs; tmp; tmp = g_list_next (tmp)) {
1909     if (ges_track_object_is_locked (tmp->data)) {
1910       ret = timeline_trim_object (timeline, GES_TRACK_OBJECT (tmp->data),
1911           NULL, GES_EDGE_START, start);
1912       break;
1913     }
1914   }
1915   g_list_free_full (tckobjs, g_object_unref);
1916
1917   return ret;
1918 }
1919
1920 static void
1921 update_height (GESTimelineObject * object)
1922 {
1923   GList *tmp;
1924   guint32 min_prio = G_MAXUINT32, max_prio = 0;
1925
1926   /* Go over all childs and check if height has changed */
1927   for (tmp = object->priv->trackobjects; tmp; tmp = tmp->next) {
1928     guint tck_priority =
1929         ges_track_object_get_priority (GES_TRACK_OBJECT (tmp->data));
1930
1931     if (tck_priority < min_prio)
1932       min_prio = tck_priority;
1933     if (tck_priority > max_prio)
1934       max_prio = tck_priority;
1935   }
1936
1937   /* FIXME : We only grow the height */
1938   if (object->height < (max_prio - min_prio + 1)) {
1939     object->height = max_prio - min_prio + 1;
1940     GST_DEBUG ("Updating height %i", object->height);
1941 #if GLIB_CHECK_VERSION(2,26,0)
1942     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_HEIGHT]);
1943 #else
1944     g_object_notify (G_OBJECT (object), "height");
1945 #endif
1946   }
1947 }
1948
1949 /*
1950  * PROPERTY NOTIFICATIONS FROM TRACK OBJECTS
1951  */
1952
1953 static void
1954 track_object_start_changed_cb (GESTrackObject * child,
1955     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
1956 {
1957   ObjectMapping *map;
1958
1959   if (object->priv->ignore_notifies)
1960     return;
1961
1962   map = find_object_mapping (object, child);
1963   if (G_UNLIKELY (map == NULL))
1964     /* something massively screwed up if we get this */
1965     return;
1966
1967   if (!ges_track_object_is_locked (child)) {
1968     /* Update the internal start_offset */
1969     map->start_offset = object->start - child->start;
1970   } else {
1971     /* Or update the parent start */
1972     object->priv->initiated_move = child;
1973     ges_timeline_object_set_start (object, child->start + map->start_offset);
1974     object->priv->initiated_move = NULL;
1975   }
1976 }
1977
1978 static void
1979 track_object_inpoint_changed_cb (GESTrackObject * child,
1980     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
1981 {
1982   ObjectMapping *map;
1983
1984   if (object->priv->ignore_notifies)
1985     return;
1986
1987   map = find_object_mapping (object, child);
1988   if (G_UNLIKELY (map == NULL))
1989     /* something massively screwed up if we get this */
1990     return;
1991
1992   if (!ges_track_object_is_locked (child)) {
1993     /* Update the internal start_offset */
1994     map->inpoint_offset = object->inpoint - child->inpoint;
1995   } else {
1996     /* Or update the parent start */
1997     object->priv->initiated_move = child;
1998     ges_timeline_object_set_inpoint (object,
1999         child->inpoint + map->inpoint_offset);
2000     object->priv->initiated_move = NULL;
2001   }
2002
2003 }
2004
2005 static void
2006 track_object_duration_changed_cb (GESTrackObject * child,
2007     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
2008 {
2009   ObjectMapping *map;
2010
2011   if (object->priv->ignore_notifies)
2012     return;
2013
2014   map = find_object_mapping (object, child);
2015   if (G_UNLIKELY (map == NULL))
2016     /* something massively screwed up if we get this */
2017     return;
2018
2019   if (!ges_track_object_is_locked (child)) {
2020     /* Update the internal start_offset */
2021     map->duration_offset = object->duration - child->duration;
2022   } else {
2023     /* Or update the parent start */
2024     object->priv->initiated_move = child;
2025     ges_timeline_object_set_duration (object,
2026         child->duration + map->duration_offset);
2027     object->priv->initiated_move = NULL;
2028   }
2029
2030 }
2031
2032 static void
2033 track_object_priority_changed_cb (GESTrackObject * child,
2034     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
2035 {
2036   ObjectMapping *map;
2037   guint32 layer_min_gnl_prio, layer_max_gnl_prio;
2038
2039   guint tck_priority = ges_track_object_get_priority (child);
2040
2041   GST_DEBUG ("TrackObject %p priority changed to %i", child,
2042       ges_track_object_get_priority (child));
2043
2044   if (object->priv->ignore_notifies)
2045     return;
2046
2047   update_height (object);
2048   map = find_object_mapping (object, child);
2049   get_layer_priorities (object->priv->layer, &layer_min_gnl_prio,
2050       &layer_max_gnl_prio);
2051
2052   if (G_UNLIKELY (map == NULL))
2053     /* something massively screwed up if we get this */
2054     return;
2055
2056   if (!ges_track_object_is_locked (child)) {
2057     if (tck_priority < layer_min_gnl_prio || tck_priority > layer_max_gnl_prio) {
2058       GST_WARNING ("%p priority of %i, is outside of its containing "
2059           "layer space. (%d/%d). This is a bug in the program.", object,
2060           tck_priority, layer_min_gnl_prio, layer_max_gnl_prio);
2061     }
2062
2063     /* Update the internal priority_offset */
2064     map->priority_offset =
2065         tck_priority - (layer_min_gnl_prio + object->priority);
2066
2067   } else if (tck_priority < layer_min_gnl_prio + object->priority) {
2068     /* Or update the parent priority, the object priority is always the
2069      * highest priority (smaller number) */
2070     if (tck_priority < layer_min_gnl_prio || layer_max_gnl_prio < tck_priority) {
2071
2072       GST_WARNING ("%p priority of %i, is outside of its containing "
2073           "layer space. (%d/%d). This is a bug in the program.", object,
2074           tck_priority, layer_min_gnl_prio, layer_max_gnl_prio);
2075       return;
2076     }
2077
2078     ges_timeline_object_set_priority (object,
2079         tck_priority - layer_min_gnl_prio);
2080   }
2081
2082   GST_DEBUG ("object %p priority %d child %p priority %d", object,
2083       object->priority, child, ges_track_object_get_priority (child));
2084 }
2085
2086 static void
2087 get_layer_priorities (GESTimelineLayer * layer, guint32 * layer_min_gnl_prio,
2088     guint32 * layer_max_gnl_prio)
2089 {
2090   if (layer) {
2091     *layer_min_gnl_prio = layer->min_gnl_priority;
2092     *layer_max_gnl_prio = layer->max_gnl_priority;
2093   } else {
2094     *layer_min_gnl_prio = 0;
2095     *layer_max_gnl_prio = G_MAXUINT32;
2096   }
2097 }