034fae22851887be3e60be51f4ab444dc2a3f16c
[platform/upstream/gst-editing-services.git] / ges / ges-container.c
1 /* GStreamer Editing Services
2  * Copyright (C) <2013> Thibault Saunier <thibault.saunier@collabora.com>
3  *               <2013> Collabora Ltd.
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., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:gescontainer
23  * @title: GESContainer
24  * @short_description: Base Class for objects responsible for controlling other
25  * GESTimelineElement-s
26  */
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include "ges-container.h"
32 #include "ges.h"
33 #include "ges-internal.h"
34
35 #include <string.h>
36
37 GST_DEBUG_CATEGORY_STATIC (ges_container_debug);
38 #undef GST_CAT_DEFAULT
39 #define GST_CAT_DEFAULT ges_container_debug
40
41 /* Mapping of relationship between a Container and the TimelineElements
42  * it controls
43  *
44  * NOTE : Does it make sense to make it public in the future ?
45  */
46 typedef struct
47 {
48   GESTimelineElement *child;
49
50   GstClockTime start_offset;
51   GstClockTime duration_offset;
52   GstClockTime inpoint_offset;
53   gint32 priority_offset;
54
55   guint start_notifyid;
56   guint duration_notifyid;
57   guint inpoint_notifyid;
58 } ChildMapping;
59
60 enum
61 {
62   CHILD_ADDED_SIGNAL,
63   CHILD_REMOVED_SIGNAL,
64   LAST_SIGNAL
65 };
66
67 static guint ges_container_signals[LAST_SIGNAL] = { 0 };
68
69 struct _GESContainerPrivate
70 {
71   /*< public > */
72   GESLayer *layer;
73
74   /*< private > */
75   /* Set to TRUE when the container is doing updates of track object
76    * properties so we don't end up in infinite property update loops
77    */
78   GHashTable *mappings;
79   guint nb_effects;
80
81   /* List of GESTimelineElement being in the "child-added" signal
82    * emission stage */
83   GList *adding_children;
84
85   GList *copied_children;
86 };
87
88 enum
89 {
90   PROP_0,
91   PROP_HEIGHT,
92   PROP_LAST
93 };
94
95 static GParamSpec *properties[PROP_LAST];
96
97 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESContainer, ges_container,
98     GES_TYPE_TIMELINE_ELEMENT);
99
100 /************************
101  *   Private  methods   *
102  ************************/
103 static void
104 _free_mapping (ChildMapping * mapping)
105 {
106   GESTimelineElement *child = mapping->child;
107
108   /* Disconnect all notify listeners */
109   if (mapping->start_notifyid)
110     g_signal_handler_disconnect (child, mapping->start_notifyid);
111   if (mapping->duration_notifyid)
112     g_signal_handler_disconnect (child, mapping->duration_notifyid);
113   if (mapping->inpoint_notifyid)
114     g_signal_handler_disconnect (child, mapping->inpoint_notifyid);
115
116   ges_timeline_element_set_parent (child, NULL);
117   g_slice_free (ChildMapping, mapping);
118 }
119
120 static gint
121 compare_grouping_prio (GType * a, GType * b)
122 {
123   gint ret = 0;
124   GObjectClass *aclass = g_type_class_ref (*a);
125   GObjectClass *bclass = g_type_class_ref (*b);
126
127   /* We want higher prios to be first */
128   if (GES_CONTAINER_CLASS (aclass)->grouping_priority <
129       GES_CONTAINER_CLASS (bclass)->grouping_priority)
130     ret = 1;
131   else if (GES_CONTAINER_CLASS (aclass)->grouping_priority >
132       GES_CONTAINER_CLASS (bclass)->grouping_priority)
133     ret = -1;
134
135   g_type_class_unref (aclass);
136   g_type_class_unref (bclass);
137   return ret;
138 }
139
140 static void
141 _resync_start_offsets (GESTimelineElement * child,
142     ChildMapping * map, GESContainer * container)
143 {
144   map->start_offset = _START (container) - _START (child);
145 }
146
147 /*****************************************************
148  *                                                   *
149  * GESTimelineElement virtual methods implementation *
150  *                                                   *
151  *****************************************************/
152 static gboolean
153 _set_start (GESTimelineElement * element, GstClockTime start)
154 {
155   GList *tmp;
156   ChildMapping *map;
157   GESContainer *container = GES_CONTAINER (element);
158   GESContainerPrivate *priv = container->priv;
159
160   GST_DEBUG_OBJECT (element, "Updating children offsets, (initiated_move: %"
161       GST_PTR_FORMAT ")", container->initiated_move);
162
163   for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
164     GESTimelineElement *child = (GESTimelineElement *) tmp->data;
165
166     map = g_hash_table_lookup (priv->mappings, child);
167     map->start_offset = start - _START (child);
168   }
169   container->children_control_mode = GES_CHILDREN_UPDATE;
170
171   return TRUE;
172 }
173
174 static gboolean
175 _set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
176 {
177   GList *tmp;
178   GESContainer *container = GES_CONTAINER (element);
179
180   for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
181     GESTimelineElement *child = (GESTimelineElement *) tmp->data;
182     ChildMapping *map = g_hash_table_lookup (container->priv->mappings, child);
183
184     map->inpoint_offset = inpoint - _INPOINT (child);
185   }
186
187   return TRUE;
188 }
189
190 static gboolean
191 _set_duration (GESTimelineElement * element, GstClockTime duration)
192 {
193   GList *tmp;
194   GESContainer *container = GES_CONTAINER (element);
195   GESContainerPrivate *priv = container->priv;
196
197   for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
198     GESTimelineElement *child = (GESTimelineElement *) tmp->data;
199     ChildMapping *map = g_hash_table_lookup (priv->mappings, child);
200
201     map->duration_offset = duration - _DURATION (child);
202   }
203
204   return TRUE;
205 }
206
207 static void
208 _ges_container_add_child_properties (GESContainer * container,
209     GESTimelineElement * child)
210 {
211   guint n_props, i;
212
213   GParamSpec **child_props =
214       ges_timeline_element_list_children_properties (child,
215       &n_props);
216
217   for (i = 0; i < n_props; i++) {
218     GObject *prop_child;
219     gchar *prop_name = g_strdup_printf ("%s::%s",
220         g_type_name (child_props[i]->owner_type),
221         child_props[i]->name);
222
223     if (ges_timeline_element_lookup_child (child, prop_name, &prop_child, NULL)) {
224       ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT (container),
225           child_props[i], prop_child);
226       gst_object_unref (prop_child);
227
228     }
229     g_free (prop_name);
230     g_param_spec_unref (child_props[i]);
231   }
232
233   g_free (child_props);
234 }
235
236 static void
237 _ges_container_remove_child_properties (GESContainer * container,
238     GESTimelineElement * child)
239 {
240   guint n_props, i;
241
242   GParamSpec **child_props =
243       ges_timeline_element_list_children_properties (child,
244       &n_props);
245
246   for (i = 0; i < n_props; i++) {
247     GObject *prop_child;
248     gchar *prop_name = g_strdup_printf ("%s::%s",
249         g_type_name (child_props[i]->owner_type),
250         child_props[i]->name);
251
252     if (ges_timeline_element_lookup_child (child, prop_name, &prop_child, NULL)) {
253       ges_timeline_element_remove_child_property (GES_TIMELINE_ELEMENT
254           (container), child_props[i]);
255       gst_object_unref (prop_child);
256
257     }
258
259     g_free (prop_name);
260     g_param_spec_unref (child_props[i]);
261   }
262
263   g_free (child_props);
264 }
265
266 static GParamSpec **
267 _list_children_properties (GESTimelineElement * self, guint * n_properties)
268 {
269   GList *tmp;
270
271   for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next)
272     _ges_container_add_child_properties (GES_CONTAINER (self), tmp->data);
273
274   return
275       GES_TIMELINE_ELEMENT_CLASS
276       (ges_container_parent_class)->list_children_properties (self,
277       n_properties);
278 }
279
280 static gboolean
281 _lookup_child (GESTimelineElement * self, const gchar * prop_name,
282     GObject ** child, GParamSpec ** pspec)
283 {
284   GList *tmp;
285
286   /* FIXME Implement a syntax to precisely get properties by path */
287   for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
288     if (ges_timeline_element_lookup_child (tmp->data, prop_name, child, pspec))
289       return TRUE;
290   }
291
292   return FALSE;
293 }
294
295 static GESTrackType
296 _get_track_types (GESTimelineElement * object)
297 {
298   GESTrackType types = GES_TRACK_TYPE_UNKNOWN;
299   GList *tmp, *children = ges_container_get_children (GES_CONTAINER (object),
300       TRUE);
301
302   for (tmp = children; tmp; tmp = tmp->next) {
303     if (GES_IS_TRACK_ELEMENT (tmp->data)) {
304       types |= ges_timeline_element_get_track_types (tmp->data);
305     }
306   }
307
308   g_list_free_full (children, gst_object_unref);
309
310   return types ^ GES_TRACK_TYPE_UNKNOWN;
311 }
312
313 static void
314 _deep_copy (GESTimelineElement * element, GESTimelineElement * copy)
315 {
316   GList *tmp;
317   GESContainer *self = GES_CONTAINER (element), *ccopy = GES_CONTAINER (copy);
318
319   for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
320     ChildMapping *map;
321
322     map =
323         g_slice_dup (ChildMapping, g_hash_table_lookup (self->priv->mappings,
324             tmp->data));
325     map->child = ges_timeline_element_copy (tmp->data, TRUE);
326     map->start_notifyid = 0;
327     map->inpoint_notifyid = 0;
328     map->duration_notifyid = 0;
329
330     ccopy->priv->copied_children = g_list_prepend (ccopy->priv->copied_children,
331         map);
332   }
333 }
334
335 static GESTimelineElement *
336 _paste (GESTimelineElement * element, GESTimelineElement * ref,
337     GstClockTime paste_position)
338 {
339   GList *tmp;
340   ChildMapping *map;
341   GESContainer *ncontainer =
342       GES_CONTAINER (ges_timeline_element_copy (element, FALSE));
343   GESContainer *self = GES_CONTAINER (element);
344
345   for (tmp = self->priv->copied_children; tmp; tmp = tmp->next) {
346     GESTimelineElement *nchild;
347
348     map = tmp->data;
349     nchild =
350         ges_timeline_element_paste (map->child,
351         paste_position - map->start_offset);
352     ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (ncontainer),
353         GES_TIMELINE_ELEMENT_TIMELINE (ref));
354     ges_container_add (ncontainer, nchild);
355   }
356
357   return GES_TIMELINE_ELEMENT (ncontainer);
358 }
359
360
361 /******************************************
362  *                                        *
363  * GObject virtual methods implementation *
364  *                                        *
365  ******************************************/
366 static void
367 _dispose (GObject * object)
368 {
369   GList *tmp;
370   GESContainer *self = GES_CONTAINER (object);
371   GList *children;
372
373   _ges_container_sort_children (self);
374   children = ges_container_get_children (self, FALSE);
375
376   for (tmp = g_list_last (children); tmp; tmp = tmp->prev)
377     ges_container_remove (self, tmp->data);
378
379   g_list_free_full (children, gst_object_unref);
380   self->children = NULL;
381
382   G_OBJECT_CLASS (ges_container_parent_class)->dispose (object);
383 }
384
385 static void
386 _finalize (GObject * object)
387 {
388   GESContainer *self = GES_CONTAINER (object);
389
390   g_list_free_full (self->priv->copied_children,
391       (GDestroyNotify) _free_mapping);
392
393   if (self->priv->mappings)
394     g_hash_table_destroy (self->priv->mappings);
395
396   G_OBJECT_CLASS (ges_container_parent_class)->finalize (object);
397 }
398
399 static void
400 _get_property (GObject * container, guint property_id,
401     GValue * value, GParamSpec * pspec)
402 {
403   GESContainer *tobj = GES_CONTAINER (container);
404
405   switch (property_id) {
406     case PROP_HEIGHT:
407       g_value_set_uint (value, tobj->height);
408       break;
409     default:
410       G_OBJECT_WARN_INVALID_PROPERTY_ID (container, property_id, pspec);
411   }
412 }
413
414 static void
415 _set_property (GObject * container, guint property_id,
416     const GValue * value, GParamSpec * pspec)
417 {
418   switch (property_id) {
419     default:
420       G_OBJECT_WARN_INVALID_PROPERTY_ID (container, property_id, pspec);
421   }
422 }
423
424 static void
425 ges_container_class_init (GESContainerClass * klass)
426 {
427   GObjectClass *object_class = G_OBJECT_CLASS (klass);
428   GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
429
430   GST_DEBUG_CATEGORY_INIT (ges_container_debug, "gescontainer",
431       GST_DEBUG_FG_YELLOW, "ges container");
432
433   object_class->get_property = _get_property;
434   object_class->set_property = _set_property;
435   object_class->dispose = _dispose;
436   object_class->finalize = _finalize;
437
438   /**
439    * GESContainer:height:
440    *
441    * The span of priorities which this container occupies.
442    */
443   properties[PROP_HEIGHT] = g_param_spec_uint ("height", "Height",
444       "The span of priorities this container occupies", 0, G_MAXUINT, 1,
445       G_PARAM_READABLE);
446   g_object_class_install_property (object_class, PROP_HEIGHT,
447       properties[PROP_HEIGHT]);
448
449   /**
450    * GESContainer::child-added:
451    * @container: the #GESContainer
452    * @element: the #GESTimelineElement that was added.
453    *
454    * Will be emitted after a child was added to @container.
455    * Usually you should connect with #g_signal_connect_after
456    * as in the first emission stage, the signal emission might
457    * get stopped internally.
458    */
459   ges_container_signals[CHILD_ADDED_SIGNAL] =
460       g_signal_new ("child-added", G_TYPE_FROM_CLASS (klass),
461       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESContainerClass, child_added),
462       NULL, NULL, g_cclosure_marshal_generic,
463       G_TYPE_NONE, 1, GES_TYPE_TIMELINE_ELEMENT);
464
465   /**
466    * GESContainer::child-removed:
467    * @container: the #GESContainer
468    * @element: the #GESTimelineElement that was removed.
469    *
470    * Will be emitted after a child was removed from @container.
471    */
472   ges_container_signals[CHILD_REMOVED_SIGNAL] =
473       g_signal_new ("child-removed", G_TYPE_FROM_CLASS (klass),
474       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GESContainerClass, child_removed),
475       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
476       GES_TYPE_TIMELINE_ELEMENT);
477
478
479   element_class->set_start = _set_start;
480   element_class->set_duration = _set_duration;
481   element_class->set_inpoint = _set_inpoint;
482   element_class->list_children_properties = _list_children_properties;
483   element_class->lookup_child = _lookup_child;
484   element_class->get_track_types = _get_track_types;
485   element_class->paste = _paste;
486   element_class->deep_copy = _deep_copy;
487
488   /* No default implementations */
489   klass->remove_child = NULL;
490   klass->add_child = NULL;
491   klass->ungroup = NULL;
492   klass->group = NULL;
493   klass->grouping_priority = 0;
494   klass->edit = NULL;
495 }
496
497 static void
498 ges_container_init (GESContainer * self)
499 {
500   self->priv = ges_container_get_instance_private (self);
501
502   /* FIXME, check why default was GST_SECOND? (before the existend of
503    * ges-container)
504    *
505    * _DURATION (self) = GST_SECOND; */
506   self->height = 1;             /* FIXME Why 1 and not 0? */
507   self->children = NULL;
508
509   self->priv->mappings = g_hash_table_new_full (g_direct_hash, g_direct_equal,
510       NULL, (GDestroyNotify) _free_mapping);
511 }
512
513 /**********************************************
514  *                                            *
515  *    Property notifications from Children    *
516  *                                            *
517  **********************************************/
518 static void
519 _child_start_changed_cb (GESTimelineElement * child,
520     GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
521 {
522   ChildMapping *map;
523   GstClockTime start;
524
525   GESContainerPrivate *priv = container->priv;
526   GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
527
528   map = g_hash_table_lookup (priv->mappings, child);
529   g_assert (map);
530
531   switch (container->children_control_mode) {
532     case GES_CHILDREN_IGNORE_NOTIFIES:
533       return;
534     case GES_CHILDREN_UPDATE_ALL_VALUES:
535       _ges_container_sort_children (container);
536       start = container->children ?
537           _START (container->children->data) : _START (container);
538
539       if (start != _START (container)) {
540         _DURATION (container) = _END (container) - start;
541         _START (container) = start;
542
543         GST_DEBUG_OBJECT (container, "Child move made us "
544             "move to %" GST_TIME_FORMAT, GST_TIME_ARGS (_START (container)));
545
546         g_object_notify (G_OBJECT (container), "start");
547       }
548
549       /* Falltrough! */
550     case GES_CHILDREN_UPDATE_OFFSETS:
551       map->start_offset = _START (container) - _START (child);
552       break;
553
554     case GES_CHILDREN_UPDATE:
555       /* We update all the children calling our set_start method */
556       container->initiated_move = child;
557       _set_start0 (element, _START (child) + map->start_offset);
558       container->initiated_move = NULL;
559       break;
560     default:
561       break;
562   }
563 }
564
565 static void
566 _child_inpoint_changed_cb (GESTimelineElement * child,
567     GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
568 {
569   ChildMapping *map;
570
571   GESContainerPrivate *priv = container->priv;
572   GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
573
574   if (container->children_control_mode == GES_CHILDREN_IGNORE_NOTIFIES)
575     return;
576
577   map = g_hash_table_lookup (priv->mappings, child);
578   g_assert (map);
579
580   if (container->children_control_mode == GES_CHILDREN_UPDATE_OFFSETS) {
581     map->inpoint_offset = _START (container) - _START (child);
582
583     return;
584   }
585
586   /* We update all the children calling our set_inpoint method */
587   container->initiated_move = child;
588   _set_inpoint0 (element, _INPOINT (child) + map->inpoint_offset);
589   container->initiated_move = NULL;
590 }
591
592 static void
593 _child_duration_changed_cb (GESTimelineElement * child,
594     GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
595 {
596   ChildMapping *map;
597
598   GList *tmp;
599   GstClockTime end = 0;
600   GESContainerPrivate *priv = container->priv;
601   GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
602
603   if (container->children_control_mode == GES_CHILDREN_IGNORE_NOTIFIES)
604     return;
605
606   map = g_hash_table_lookup (priv->mappings, child);
607   g_assert (map);
608
609   switch (container->children_control_mode) {
610     case GES_CHILDREN_IGNORE_NOTIFIES:
611       break;
612     case GES_CHILDREN_UPDATE_ALL_VALUES:
613       _ges_container_sort_children_by_end (container);
614
615       for (tmp = container->children; tmp; tmp = tmp->next)
616         end = MAX (end, _END (tmp->data));
617
618       if (end != _END (container)) {
619         _DURATION (container) = end - _START (container);
620         g_object_notify (G_OBJECT (container), "duration");
621       }
622       /* Falltrough */
623     case GES_CHILDREN_UPDATE_OFFSETS:
624       map->inpoint_offset = _START (container) - _START (child);
625       break;
626     case GES_CHILDREN_UPDATE:
627       /* We update all the children calling our set_duration method */
628       container->initiated_move = child;
629       _set_duration0 (element, _DURATION (child) + map->duration_offset);
630       container->initiated_move = NULL;
631       break;
632     default:
633       break;
634   }
635 }
636
637 /****************************************************
638  *                                                  *
639  *         Internal methods implementation          *
640  *                                                  *
641  ****************************************************/
642
643 void
644 _ges_container_sort_children (GESContainer * container)
645 {
646   container->children = g_list_sort (container->children,
647       (GCompareFunc) element_start_compare);
648 }
649
650 void
651 _ges_container_sort_children_by_end (GESContainer * container)
652 {
653   container->children = g_list_sort (container->children,
654       (GCompareFunc) element_end_compare);
655 }
656
657 void
658 _ges_container_set_height (GESContainer * container, guint32 height)
659 {
660   if (container->height != height) {
661     container->height = height;
662     GST_DEBUG_OBJECT (container, "Updating height %i", container->height);
663     g_object_notify (G_OBJECT (container), "height");
664   }
665 }
666
667 gint
668 _ges_container_get_priority_offset (GESContainer * container,
669     GESTimelineElement * elem)
670 {
671   ChildMapping *map = g_hash_table_lookup (container->priv->mappings, elem);
672
673   g_return_val_if_fail (map, 0);
674
675   return map->priority_offset;
676 }
677
678 void
679 _ges_container_set_priority_offset (GESContainer * container,
680     GESTimelineElement * elem, gint32 priority_offset)
681 {
682   ChildMapping *map = g_hash_table_lookup (container->priv->mappings, elem);
683
684   g_return_if_fail (map);
685
686   map->priority_offset = priority_offset;
687 }
688
689 /**********************************************
690  *                                            *
691  *            API implementation              *
692  *                                            *
693  **********************************************/
694
695 /**
696  * ges_container_add:
697  * @container: a #GESContainer
698  * @child: the #GESTimelineElement
699  *
700  * Add the #GESTimelineElement to the container.
701  *
702  * Returns: %TRUE on success, %FALSE on failure.
703  */
704 gboolean
705 ges_container_add (GESContainer * container, GESTimelineElement * child)
706 {
707   ChildMapping *mapping;
708   gboolean notify_start = FALSE;
709   GESContainerClass *class;
710   GESContainerPrivate *priv;
711
712   g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
713   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (child), FALSE);
714   g_return_val_if_fail (GES_TIMELINE_ELEMENT_PARENT (child) == NULL, FALSE);
715
716   class = GES_CONTAINER_GET_CLASS (container);
717   priv = container->priv;
718
719   GST_DEBUG_OBJECT (container, "adding timeline element %" GST_PTR_FORMAT,
720       child);
721
722   container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
723   if (class->add_child) {
724     if (class->add_child (container, child) == FALSE) {
725       container->children_control_mode = GES_CHILDREN_UPDATE;
726       GST_WARNING_OBJECT (container, "Erreur adding child %p", child);
727       return FALSE;
728     }
729   }
730   container->children_control_mode = GES_CHILDREN_UPDATE;
731
732   if (_START (container) > _START (child)) {
733     _START (container) = _START (child);
734
735     g_hash_table_foreach (priv->mappings, (GHFunc) _resync_start_offsets,
736         container);
737     notify_start = TRUE;
738   }
739
740   mapping = g_slice_new0 (ChildMapping);
741   mapping->child = gst_object_ref (child);
742   mapping->start_offset = _START (container) - _START (child);
743   mapping->duration_offset = _DURATION (container) - _DURATION (child);
744   mapping->inpoint_offset = _INPOINT (container) - _INPOINT (child);
745
746   g_hash_table_insert (priv->mappings, child, mapping);
747
748   container->children = g_list_prepend (container->children, child);
749
750   _ges_container_sort_children (container);
751
752   /* Listen to all property changes */
753   mapping->start_notifyid =
754       g_signal_connect (G_OBJECT (child), "notify::start",
755       G_CALLBACK (_child_start_changed_cb), container);
756   mapping->duration_notifyid =
757       g_signal_connect (G_OBJECT (child), "notify::duration",
758       G_CALLBACK (_child_duration_changed_cb), container);
759   mapping->inpoint_notifyid =
760       g_signal_connect (G_OBJECT (child), "notify::in-point",
761       G_CALLBACK (_child_inpoint_changed_cb), container);
762
763   if (ges_timeline_element_set_parent (child, GES_TIMELINE_ELEMENT (container))
764       == FALSE) {
765
766     g_hash_table_remove (priv->mappings, child);
767     container->children = g_list_remove (container->children, child);
768     _ges_container_sort_children (container);
769
770     return FALSE;
771   }
772
773   _ges_container_add_child_properties (container, child);
774
775   priv->adding_children = g_list_prepend (priv->adding_children, child);
776   g_signal_emit (container, ges_container_signals[CHILD_ADDED_SIGNAL], 0,
777       child);
778   priv->adding_children = g_list_remove (priv->adding_children, child);
779
780   if (notify_start)
781     g_object_notify (G_OBJECT (container), "start");
782
783   return TRUE;
784 }
785
786 /**
787  * ges_container_remove:
788  * @container: a #GESContainer
789  * @child: the #GESTimelineElement to release
790  *
791  * Release the @child from the control of @container.
792  *
793  * Returns: %TRUE if the @child was properly released, else %FALSE.
794  */
795 gboolean
796 ges_container_remove (GESContainer * container, GESTimelineElement * child)
797 {
798   GESContainerClass *klass;
799   GESContainerPrivate *priv;
800
801   g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
802   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (child), FALSE);
803
804   GST_DEBUG_OBJECT (container, "removing child: %" GST_PTR_FORMAT, child);
805
806   klass = GES_CONTAINER_GET_CLASS (container);
807   priv = container->priv;
808
809   if (!(g_hash_table_lookup (priv->mappings, child))) {
810     GST_WARNING_OBJECT (container, "Element isn't controlled by this "
811         "container");
812     return FALSE;
813   }
814
815   if (klass->remove_child) {
816     if (klass->remove_child (container, child) == FALSE)
817       return FALSE;
818   }
819
820   container->children = g_list_remove (container->children, child);
821   /* Let it live removing from our mappings */
822   g_hash_table_remove (priv->mappings, child);
823
824   _ges_container_remove_child_properties (container, child);
825
826   if (!g_list_find (container->priv->adding_children, child)) {
827     g_signal_emit (container, ges_container_signals[CHILD_REMOVED_SIGNAL], 0,
828         child);
829   } else {
830     GST_INFO_OBJECT (container, "Not emitting 'child-removed' signal as child"
831         " removal happend during 'child-added' signal emission");
832   }
833   gst_object_unref (child);
834
835   return TRUE;
836 }
837
838 static void
839 _get_children_recursively (GESContainer * container, GList ** children)
840 {
841   GList *tmp;
842
843   *children =
844       g_list_concat (*children, g_list_copy_deep (container->children,
845           (GCopyFunc) gst_object_ref, NULL));
846
847   for (tmp = container->children; tmp; tmp = tmp->next) {
848     GESTimelineElement *element = tmp->data;
849
850     if (GES_IS_CONTAINER (element))
851       _get_children_recursively (tmp->data, children);
852   }
853 }
854
855 /**
856  * ges_container_get_children:
857  * @container: a #GESContainer
858  * @recursive:  Whether to recursively get children in @container
859  *
860  * Get the list of #GESTimelineElement contained in @container
861  * The user is responsible for unreffing the contained objects
862  * and freeing the list.
863  *
864  * Returns: (transfer full) (element-type GESTimelineElement): The list of
865  * timeline element contained in @container.
866  */
867 GList *
868 ges_container_get_children (GESContainer * container, gboolean recursive)
869 {
870   GList *children = NULL;
871
872   g_return_val_if_fail (GES_IS_CONTAINER (container), NULL);
873
874   if (!recursive)
875     return g_list_copy_deep (container->children, (GCopyFunc) gst_object_ref,
876         NULL);
877
878   _get_children_recursively (container, &children);
879   return children;
880 }
881
882 /**
883  * ges_container_ungroup:
884  * @container: (transfer full): The #GESContainer to ungroup
885  * @recursive: Wether to recursively ungroup @container
886  *
887  * Ungroups the #GESTimelineElement contained in this GESContainer,
888  * creating new #GESContainer containing those #GESTimelineElement
889  * apropriately.
890  *
891  * Returns: (transfer full) (element-type GESContainer): The list of
892  * #GESContainer resulting from the ungrouping operation
893  * The user is responsible for unreffing the contained objects
894  * and freeing the list.
895  */
896 GList *
897 ges_container_ungroup (GESContainer * container, gboolean recursive)
898 {
899   GESContainerClass *klass;
900
901   g_return_val_if_fail (GES_IS_CONTAINER (container), NULL);
902
903   GST_DEBUG_OBJECT (container, "Ungrouping container %s recursively",
904       recursive ? "" : "not");
905
906   klass = GES_CONTAINER_GET_CLASS (container);
907   if (klass->ungroup == NULL) {
908     GST_INFO_OBJECT (container, "No ungoup virtual method, doint nothing");
909     return NULL;
910   }
911
912   return klass->ungroup (container, recursive);
913 }
914
915 /**
916  * ges_container_group:
917  * @containers: (transfer none)(element-type GESContainer) (allow-none): The
918  * #GESContainer to group, they must all be in a same #GESTimeline
919  *
920  * Groups the #GESContainer-s provided in @containers. It creates a subclass
921  * of #GESContainer, depending on the containers provided in @containers.
922  * Basically, if all the containers in @containers should be contained in a same
923  * clip (all the #GESTrackElement they contain have the exact same
924  * start/inpoint/duration and are in the same layer), it will create a #GESClip
925  * otherwise a #GESGroup will be created
926  *
927  * Returns: (transfer none): The #GESContainer (subclass) resulting of the
928  * grouping
929  */
930 GESContainer *
931 ges_container_group (GList * containers)
932 {
933   GList *tmp;
934   guint n_children;
935   GType *children_types;
936   GESTimelineElement *element;
937   GObjectClass *clip_class;
938
939   guint i = 0;
940   GESContainer *ret = NULL;
941   GESTimeline *timeline = NULL;
942
943   if (containers) {
944     element = GES_TIMELINE_ELEMENT (containers->data);
945     timeline = GES_TIMELINE_ELEMENT_TIMELINE (element);
946     g_return_val_if_fail (timeline, NULL);
947   }
948
949   if (g_list_length (containers) == 1)
950     return containers->data;
951
952   for (tmp = containers; tmp; tmp = tmp->next) {
953     g_return_val_if_fail (GES_IS_CONTAINER (tmp->data), NULL);
954     g_return_val_if_fail (GES_TIMELINE_ELEMENT_PARENT (tmp->data) == NULL,
955         NULL);
956     g_return_val_if_fail (GES_TIMELINE_ELEMENT_TIMELINE (tmp->data) == timeline,
957         NULL);
958   }
959
960   children_types = g_type_children (GES_TYPE_CONTAINER, &n_children);
961   g_qsort_with_data (children_types, n_children, sizeof (GType),
962       (GCompareDataFunc) compare_grouping_prio, NULL);
963
964   for (i = 0; i < n_children; i++) {
965     clip_class = g_type_class_peek (children_types[i]);
966     ret = GES_CONTAINER_CLASS (clip_class)->group (containers);
967
968     if (ret)
969       break;
970   }
971
972   g_free (children_types);
973   return ret;
974 }
975
976 /**
977  * ges_container_edit:
978  * @container: the #GESClip to edit
979  * @layers: (element-type GESLayer): The layers you want the edit to
980  *  happen in, %NULL means that the edition is done in all the
981  *  #GESLayers contained in the current timeline.
982  * @new_layer_priority: The priority of the layer @container should land in.
983  *  If the layer you're trying to move the container to doesn't exist, it will
984  *  be created automatically. -1 means no move.
985  * @mode: The #GESEditMode in which the editition will happen.
986  * @edge: The #GESEdge the edit should happen on.
987  * @position: The position at which to edit @container (in nanosecond)
988  *
989  * Edit @container in the different exisiting #GESEditMode modes. In the case of
990  * slide, and roll, you need to specify a #GESEdge
991  *
992  * Returns: %TRUE if the container as been edited properly, %FALSE if an error
993  * occured
994  */
995 gboolean
996 ges_container_edit (GESContainer * container, GList * layers,
997     gint new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
998 {
999   g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
1000
1001   if (G_UNLIKELY (GES_CONTAINER_GET_CLASS (container)->edit == NULL)) {
1002     GST_WARNING_OBJECT (container, "No edit vmethod implementation");
1003     return FALSE;
1004   }
1005
1006   return GES_CONTAINER_GET_CLASS (container)->edit (container, layers,
1007       new_layer_priority, mode, edge, position);
1008 }