command-line-formatter: Stop uselessly looping over options
[platform/upstream/gstreamer.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 elements responsible for controlling
25  * other #GESTimelineElement-s
26  *
27  * A #GESContainer is a timeline element that controls other
28  * #GESTimelineElement-s, which are its children. In particular, it is
29  * responsible for maintaining the relative #GESTimelineElement:start and
30  * #GESTimelineElement:duration times of its children. Therefore, if a
31  * container is temporally adjusted or moved to a new layer, it may
32  * accordingly adjust and move its children. Similarly, a change in one of
33  * its children may prompt the parent to correspondingly change its
34  * siblings.
35  */
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include "ges-container.h"
41 #include "ges.h"
42 #include "ges-internal.h"
43
44 #include <string.h>
45
46 GST_DEBUG_CATEGORY_STATIC (ges_container_debug);
47 #undef GST_CAT_DEFAULT
48 #define GST_CAT_DEFAULT ges_container_debug
49
50 /* Mapping of relationship between a Container and the TimelineElements
51  * it controls
52  *
53  * NOTE : Does it make sense to make it public in the future ?
54  */
55 typedef struct
56 {
57   GESTimelineElement *child;
58
59   GstClockTime start_offset;
60   GstClockTime duration_offset;
61
62   gulong start_notifyid;
63   gulong duration_notifyid;
64   gulong child_property_added_notifyid;
65   gulong child_property_removed_notifyid;
66 } ChildMapping;
67
68 enum
69 {
70   CHILD_ADDED_SIGNAL,
71   CHILD_REMOVED_SIGNAL,
72   LAST_SIGNAL
73 };
74
75 static guint ges_container_signals[LAST_SIGNAL] = { 0 };
76
77 struct _GESContainerPrivate
78 {
79   /*< public > */
80   GESLayer *layer;
81
82   /*< private > */
83   /* Set to TRUE when the container is doing updates of track object
84    * properties so we don't end up in infinite property update loops
85    */
86   GHashTable *mappings;
87
88   /* List of GESTimelineElement being in the "child-added" signal
89    * emission stage */
90   GList *adding_children;
91
92   GList *copied_children;
93 };
94
95 enum
96 {
97   PROP_0,
98   PROP_HEIGHT,
99   PROP_LAST
100 };
101
102 static GParamSpec *properties[PROP_LAST];
103
104 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESContainer, ges_container,
105     GES_TYPE_TIMELINE_ELEMENT);
106
107 /************************
108  *   Private  methods   *
109  ************************/
110 static void
111 _free_mapping (ChildMapping * mapping)
112 {
113   GESTimelineElement *child = mapping->child;
114
115   /* Disconnect all notify listeners */
116   if (mapping->start_notifyid)
117     g_signal_handler_disconnect (child, mapping->start_notifyid);
118   if (mapping->duration_notifyid)
119     g_signal_handler_disconnect (child, mapping->duration_notifyid);
120   if (mapping->child_property_added_notifyid)
121     g_signal_handler_disconnect (child, mapping->child_property_added_notifyid);
122   if (mapping->child_property_removed_notifyid)
123     g_signal_handler_disconnect (child,
124         mapping->child_property_removed_notifyid);
125   if (child) {
126     ges_timeline_element_set_parent (child, NULL);
127     gst_object_unref (child);
128   }
129
130   g_slice_free (ChildMapping, mapping);
131 }
132
133 static gint
134 compare_grouping_prio (GType * a, GType * b)
135 {
136   gint ret = 0;
137   GObjectClass *aclass = g_type_class_ref (*a);
138   GObjectClass *bclass = g_type_class_ref (*b);
139
140   /* We want higher prios to be first */
141   if (GES_CONTAINER_CLASS (aclass)->grouping_priority <
142       GES_CONTAINER_CLASS (bclass)->grouping_priority)
143     ret = 1;
144   else if (GES_CONTAINER_CLASS (aclass)->grouping_priority >
145       GES_CONTAINER_CLASS (bclass)->grouping_priority)
146     ret = -1;
147
148   g_type_class_unref (aclass);
149   g_type_class_unref (bclass);
150   return ret;
151 }
152
153 static void
154 _resync_position_offsets (GESTimelineElement * child,
155     ChildMapping * map, GESContainer * container)
156 {
157   map->start_offset = _START (container) - _START (child);
158   map->duration_offset = _DURATION (container) - _DURATION (child);
159 }
160
161 /*****************************************************
162  *                                                   *
163  * GESTimelineElement virtual methods implementation *
164  *                                                   *
165  *****************************************************/
166 static gboolean
167 _set_start (GESTimelineElement * element, GstClockTime start)
168 {
169   GList *tmp;
170   ChildMapping *map;
171   GESContainer *container = GES_CONTAINER (element);
172   GESContainerPrivate *priv = container->priv;
173
174   GST_DEBUG_OBJECT (element, "Updating children offsets, (initiated_move: %"
175       GST_PTR_FORMAT ")", container->initiated_move);
176
177   for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
178     GESTimelineElement *child = (GESTimelineElement *) tmp->data;
179
180     map = g_hash_table_lookup (priv->mappings, child);
181     map->start_offset = start - _START (child);
182   }
183   container->children_control_mode = GES_CHILDREN_UPDATE;
184
185   return TRUE;
186 }
187
188 static gboolean
189 _set_duration (GESTimelineElement * element, GstClockTime duration)
190 {
191   GList *tmp;
192   GESContainer *container = GES_CONTAINER (element);
193   GESContainerPrivate *priv = container->priv;
194
195   for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
196     GESTimelineElement *child = (GESTimelineElement *) tmp->data;
197     ChildMapping *map = g_hash_table_lookup (priv->mappings, child);
198
199     map->duration_offset = duration - _DURATION (child);
200   }
201
202   return TRUE;
203 }
204
205 static void
206 _add_childs_child_property (GESTimelineElement * container_child,
207     GObject * prop_child, GParamSpec * property, GESContainer * container)
208 {
209   /* the container_child is kept as the owner of this child property when
210    * we register it on ourselves, but we use the same GObject child
211    * instance who the property comes from */
212   gboolean res =
213       ges_timeline_element_add_child_property_full (GES_TIMELINE_ELEMENT
214       (container), container_child, property, prop_child);
215   if (!res)
216     GST_INFO_OBJECT (container, "Could not register the child property '%s' "
217         "of our child %" GES_FORMAT " for the object %" GST_PTR_FORMAT,
218         property->name, GES_ARGS (container_child), prop_child);
219 }
220
221 static void
222 _ges_container_add_child_properties (GESContainer * container,
223     GESTimelineElement * child)
224 {
225   guint n_props, i;
226
227   /* use get_children_properties, rather than list_children_properties
228    * to ensure we are getting all the properties, without any interference
229    * from the ->list_children_properties vmethods */
230   GParamSpec **child_props =
231       ges_timeline_element_get_children_properties (child,
232       &n_props);
233
234   for (i = 0; i < n_props; i++) {
235     GParamSpec *property = child_props[i];
236     GObject *prop_child =
237         ges_timeline_element_get_child_from_child_property (child, property);
238     if (prop_child)
239       _add_childs_child_property (child, prop_child, property, container);
240     g_param_spec_unref (property);
241   }
242
243   g_free (child_props);
244 }
245
246 static void
247 _remove_childs_child_property (GESTimelineElement * container_child,
248     GObject * prop_child, GParamSpec * property, GESContainer * container)
249 {
250   /* NOTE: some children may share the same GParamSpec. Currently, only
251    * the first such child added will have its children properties
252    * successfully registered for the container (even though the GObject
253    * child who the properties belong to will be a different instance). As
254    * such, we only want to remove the child property if it corresponds to
255    * the same instance that the parent container has.
256    * E.g. if we add child1 and child2, that have the same (or some
257    * overlapping) children properties. And child1 is added before child2,
258    * then child2's overlapping children properties would not be registered.
259    * If we remove child2, we do *not* want to removed the child properties
260    * for child1 because they belong to a GObject instance that we still
261    * have in our control.
262    * If we remove child1, we *do* want to remove the child properties for
263    * child1, even though child2 may overlap with some of them, because we
264    * are loosing the specific GObject instance that it belongs to!
265    * We could try and register the ones that match for the other children.
266    * However, it is probably simpler to change
267    * ges_timeline_element_add_child_property_full to accept the same
268    * GParamSpec, for different instances.
269    */
270   GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
271   GObject *our_prop_child =
272       ges_timeline_element_get_child_from_child_property (element, property);
273   if (our_prop_child == prop_child)
274     ges_timeline_element_remove_child_property (element, property);
275   else
276     GST_INFO_OBJECT (container, "Not removing child property '%s' for child"
277         " %" GES_FORMAT " because it derives from the object %" GST_PTR_FORMAT
278         "(%p) rather than the object %" GST_PTR_FORMAT "(%p)", property->name,
279         GES_ARGS (container_child), prop_child, prop_child, our_prop_child,
280         our_prop_child);
281 }
282
283 static void
284 _ges_container_remove_child_properties (GESContainer * container,
285     GESTimelineElement * child)
286 {
287   guint n_props, i;
288
289   /* use get_children_properties, rather than list_children_properties
290    * to ensure we are getting all the properties, without any interference
291    * from the ->list_children_properties vmethods */
292   GParamSpec **child_props =
293       ges_timeline_element_get_children_properties (child,
294       &n_props);
295
296   for (i = 0; i < n_props; i++) {
297     GParamSpec *property = child_props[i];
298     GObject *prop_child =
299         ges_timeline_element_get_child_from_child_property (child, property);
300     if (prop_child)
301       _remove_childs_child_property (child, prop_child, property, container);
302     g_param_spec_unref (property);
303   }
304
305   g_free (child_props);
306 }
307
308 static gboolean
309 _lookup_child (GESTimelineElement * self, const gchar * prop_name,
310     GObject ** child, GParamSpec ** pspec)
311 {
312   GList *tmp;
313
314   /* FIXME Implement a syntax to precisely get properties by path */
315   for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
316     if (ges_timeline_element_lookup_child (tmp->data, prop_name, child, pspec))
317       return TRUE;
318   }
319
320   return FALSE;
321 }
322
323 static GESTrackType
324 _get_track_types (GESTimelineElement * object)
325 {
326   GESTrackType types = GES_TRACK_TYPE_UNKNOWN;
327   GList *tmp, *children = ges_container_get_children (GES_CONTAINER (object),
328       TRUE);
329
330   for (tmp = children; tmp; tmp = tmp->next) {
331     if (GES_IS_TRACK_ELEMENT (tmp->data)) {
332       types |= ges_timeline_element_get_track_types (tmp->data);
333     }
334   }
335
336   g_list_free_full (children, gst_object_unref);
337
338   return types ^ GES_TRACK_TYPE_UNKNOWN;
339 }
340
341 static void
342 _deep_copy (GESTimelineElement * element, GESTimelineElement * copy)
343 {
344   GList *tmp;
345   GESContainer *self = GES_CONTAINER (element), *ccopy = GES_CONTAINER (copy);
346
347   for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
348     ChildMapping *map, *orig_map;
349     orig_map = g_hash_table_lookup (self->priv->mappings, tmp->data);
350     map = g_slice_new0 (ChildMapping);
351     map->child = ges_timeline_element_copy (tmp->data, TRUE);
352     map->start_offset = orig_map->start_offset;
353
354     ccopy->priv->copied_children = g_list_prepend (ccopy->priv->copied_children,
355         map);
356   }
357 }
358
359 static GESTimelineElement *
360 _paste (GESTimelineElement * element, GESTimelineElement * ref,
361     GstClockTime paste_position)
362 {
363   GList *tmp;
364   ChildMapping *map;
365   GESContainer *ncontainer =
366       GES_CONTAINER (ges_timeline_element_copy (element, FALSE));
367   GESContainer *self = GES_CONTAINER (element);
368
369   for (tmp = self->priv->copied_children; tmp; tmp = tmp->next) {
370     GESTimelineElement *nchild;
371
372     map = tmp->data;
373     nchild =
374         ges_timeline_element_paste (map->child,
375         paste_position - map->start_offset);
376
377     if (!nchild) {
378       while (ncontainer->children)
379         ges_container_remove (ncontainer, ncontainer->children->data);
380
381       g_object_unref (ncontainer);
382       return NULL;
383     }
384
385     /* for GESGroups, this may register the group on the timeline */
386     if (!ges_container_add (ncontainer, nchild))
387       GST_ERROR ("%" GES_FORMAT " could not add child %p while"
388           " copying, this should never happen", GES_ARGS (ncontainer), nchild);
389   }
390
391   return GES_TIMELINE_ELEMENT (ncontainer);
392 }
393
394
395 /******************************************
396  *                                        *
397  * GObject virtual methods implementation *
398  *                                        *
399  ******************************************/
400 static void
401 _dispose (GObject * object)
402 {
403   GList *tmp;
404   GESContainer *self = GES_CONTAINER (object);
405   GList *children;
406
407   _ges_container_sort_children (self);
408   children = ges_container_get_children (self, FALSE);
409
410   for (tmp = g_list_last (children); tmp; tmp = tmp->prev)
411     ges_container_remove (self, tmp->data);
412
413   g_list_free_full (children, gst_object_unref);
414   self->children = NULL;
415
416   G_OBJECT_CLASS (ges_container_parent_class)->dispose (object);
417 }
418
419 static void
420 _finalize (GObject * object)
421 {
422   GESContainer *self = GES_CONTAINER (object);
423
424   g_list_free_full (self->priv->copied_children,
425       (GDestroyNotify) _free_mapping);
426
427   if (self->priv->mappings)
428     g_hash_table_destroy (self->priv->mappings);
429
430   G_OBJECT_CLASS (ges_container_parent_class)->finalize (object);
431 }
432
433 static void
434 _get_property (GObject * container, guint property_id,
435     GValue * value, GParamSpec * pspec)
436 {
437   GESContainer *tobj = GES_CONTAINER (container);
438
439   switch (property_id) {
440     case PROP_HEIGHT:
441       g_value_set_uint (value, tobj->height);
442       break;
443     default:
444       G_OBJECT_WARN_INVALID_PROPERTY_ID (container, property_id, pspec);
445   }
446 }
447
448 static void
449 _set_property (GObject * container, guint property_id,
450     const GValue * value, GParamSpec * pspec)
451 {
452   switch (property_id) {
453     default:
454       G_OBJECT_WARN_INVALID_PROPERTY_ID (container, property_id, pspec);
455   }
456 }
457
458 static void
459 ges_container_class_init (GESContainerClass * klass)
460 {
461   GObjectClass *object_class = G_OBJECT_CLASS (klass);
462   GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
463
464   GST_DEBUG_CATEGORY_INIT (ges_container_debug, "gescontainer",
465       GST_DEBUG_FG_YELLOW, "ges container");
466
467   object_class->get_property = _get_property;
468   object_class->set_property = _set_property;
469   object_class->dispose = _dispose;
470   object_class->finalize = _finalize;
471
472   /**
473    * GESContainer:height:
474    *
475    * The span of the container's children's #GESTimelineElement:priority
476    * values, which is the number of integers that lie between (inclusive)
477    * the minimum and maximum priorities found amongst the container's
478    * children (maximum - minimum + 1).
479    */
480   properties[PROP_HEIGHT] = g_param_spec_uint ("height", "Height",
481       "The span of priorities this container occupies", 0, G_MAXUINT, 1,
482       G_PARAM_READABLE);
483   g_object_class_install_property (object_class, PROP_HEIGHT,
484       properties[PROP_HEIGHT]);
485
486   /**
487    * GESContainer::child-added:
488    * @container: The #GESContainer
489    * @element: The child that was added
490    *
491    * Will be emitted after a child is added to the container. Usually,
492    * you should connect with g_signal_connect_after() since the signal
493    * may be stopped internally.
494    */
495   ges_container_signals[CHILD_ADDED_SIGNAL] =
496       g_signal_new ("child-added", G_TYPE_FROM_CLASS (klass),
497       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESContainerClass, child_added),
498       NULL, NULL, NULL, G_TYPE_NONE, 1, GES_TYPE_TIMELINE_ELEMENT);
499
500   /**
501    * GESContainer::child-removed:
502    * @container: The #GESContainer
503    * @element: The child that was removed
504    *
505    * Will be emitted after a child is removed from the container.
506    */
507   ges_container_signals[CHILD_REMOVED_SIGNAL] =
508       g_signal_new ("child-removed", G_TYPE_FROM_CLASS (klass),
509       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GESContainerClass, child_removed),
510       NULL, NULL, NULL, G_TYPE_NONE, 1, GES_TYPE_TIMELINE_ELEMENT);
511
512
513   element_class->set_start = _set_start;
514   element_class->set_duration = _set_duration;
515   element_class->lookup_child = _lookup_child;
516   element_class->get_track_types = _get_track_types;
517   element_class->paste = _paste;
518   element_class->deep_copy = _deep_copy;
519
520   /* No default implementations */
521   klass->remove_child = NULL;
522   klass->add_child = NULL;
523   klass->ungroup = NULL;
524   klass->group = NULL;
525   klass->grouping_priority = 0;
526   klass->edit = NULL;
527 }
528
529 static void
530 ges_container_init (GESContainer * self)
531 {
532   self->priv = ges_container_get_instance_private (self);
533
534   /* FIXME, check why default was GST_SECOND? (before the existend of
535    * ges-container)
536    *
537    * _DURATION (self) = GST_SECOND; */
538   self->height = 1;             /* FIXME Why 1 and not 0? */
539   self->children = NULL;
540
541   self->priv->mappings = g_hash_table_new_full (g_direct_hash, g_direct_equal,
542       NULL, (GDestroyNotify) _free_mapping);
543 }
544
545 /**********************************************
546  *                                            *
547  *    Property notifications from Children    *
548  *                                            *
549  **********************************************/
550
551 static void
552 _update_start_duration (GESContainer * container, GESTimelineElement * child)
553 {
554   GList *tmp;
555   GstClockTime duration, end = 0, start = G_MAXUINT64;
556   gboolean was_being_edited = GES_TIMELINE_ELEMENT_BEING_EDITED (container);
557
558   if (!container->children) {
559     /* If we are now empty, keep the same duration and start. This works
560      * well for a clip. For a group, the duration should probably be set
561      * to 0, but it gets automatically removed from the timeline when it
562      * is emptied */
563     return;
564   }
565
566   GES_TIMELINE_ELEMENT_SET_BEING_EDITED (container);
567
568   for (tmp = container->children; tmp; tmp = tmp->next) {
569     start = MIN (start, _START (tmp->data));
570     end = MAX (end, _END (tmp->data));
571   }
572
573   if (end < start)
574     duration = 0;
575   else
576     duration = end - start;
577
578   if (start != _START (container) || duration != _DURATION (container)) {
579     GstClockTime prev_dur = _DURATION (container);
580     GstClockTime prev_start = _START (container);
581
582     _DURATION (container) = duration;
583     _START (container) = start;
584
585     GST_INFO ("%" GES_FORMAT " child %" GES_FORMAT " move made us move",
586         GES_ARGS (container), GES_ARGS (child));
587
588     if (prev_start != start)
589       g_object_notify (G_OBJECT (container), "start");
590     if (prev_dur != duration)
591       g_object_notify (G_OBJECT (container), "duration");
592   }
593   if (!was_being_edited)
594     GES_TIMELINE_ELEMENT_UNSET_BEING_EDITED (container);
595
596   g_hash_table_foreach (container->priv->mappings,
597       (GHFunc) _resync_position_offsets, container);
598 }
599
600 static void
601 _child_start_changed_cb (GESTimelineElement * child,
602     GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
603 {
604   ChildMapping *map;
605
606   GESContainerPrivate *priv = container->priv;
607   GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
608   GESChildrenControlMode mode = container->children_control_mode;
609
610   if (mode == GES_CHILDREN_IGNORE_NOTIFIES)
611     return;
612
613   if (GES_TIMELINE_ELEMENT_BEING_EDITED (child))
614     mode = GES_CHILDREN_UPDATE_ALL_VALUES;
615
616   map = g_hash_table_lookup (priv->mappings, child);
617   g_assert (map);
618
619   switch (mode) {
620     case GES_CHILDREN_UPDATE_ALL_VALUES:
621       _update_start_duration (container, child);
622       break;
623     case GES_CHILDREN_UPDATE_OFFSETS:
624       map->start_offset = _START (container) - _START (child);
625       break;
626     case GES_CHILDREN_UPDATE:
627       /* We update all the children calling our set_start method */
628       container->initiated_move = child;
629       _set_start0 (element, _START (child) + map->start_offset);
630       container->initiated_move = NULL;
631       break;
632     default:
633       break;
634   }
635 }
636
637 static void
638 _child_duration_changed_cb (GESTimelineElement * child,
639     GParamSpec * arg G_GNUC_UNUSED, GESContainer * container)
640 {
641   ChildMapping *map;
642
643   GESContainerPrivate *priv = container->priv;
644   GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
645   GESChildrenControlMode mode = container->children_control_mode;
646
647   if (mode == GES_CHILDREN_IGNORE_NOTIFIES)
648     return;
649
650   if (GES_TIMELINE_ELEMENT_BEING_EDITED (child))
651     mode = GES_CHILDREN_UPDATE_ALL_VALUES;
652
653   map = g_hash_table_lookup (priv->mappings, child);
654   g_assert (map);
655
656   switch (mode) {
657     case GES_CHILDREN_UPDATE_ALL_VALUES:
658       _update_start_duration (container, child);
659       break;
660     case GES_CHILDREN_UPDATE_OFFSETS:
661       map->duration_offset = _DURATION (container) - _DURATION (child);
662       break;
663     case GES_CHILDREN_UPDATE:
664       /* We update all the children calling our set_duration method */
665       container->initiated_move = child;
666       /* FIXME: this is *not* the correct duration for a group!
667        * the ->set_duration method for GESGroup tries to hack around
668        * this by calling set_duration on itself to the actual value */
669       _set_duration0 (element, _DURATION (child) + map->duration_offset);
670       container->initiated_move = NULL;
671       break;
672     default:
673       break;
674   }
675 }
676
677 /****************************************************
678  *                                                  *
679  *         Internal methods implementation          *
680  *                                                  *
681  ****************************************************/
682
683 void
684 _ges_container_sort_children (GESContainer * container)
685 {
686   container->children = g_list_sort (container->children,
687       (GCompareFunc) element_start_compare);
688 }
689
690 void
691 _ges_container_set_height (GESContainer * container, guint32 height)
692 {
693   if (container->height != height) {
694     container->height = height;
695     GST_DEBUG_OBJECT (container, "Updating height %i", container->height);
696     g_object_notify (G_OBJECT (container), "height");
697   }
698 }
699
700 /**********************************************
701  *                                            *
702  *            API implementation              *
703  *                                            *
704  **********************************************/
705
706 /**
707  * ges_container_add:
708  * @container: A #GESContainer
709  * @child: The element to add as a child
710  *
711  * Adds a timeline element to the container. The element will now be a
712  * child of the container (and the container will be the
713  * #GESTimelineElement:parent of the added element), which means that it
714  * is now controlled by the container. This may change the properties of
715  * the child or the container, depending on the subclass.
716  *
717  * Additionally, the children properties of the newly added element will
718  * be shared with the container, meaning they can also be read and set
719  * using ges_timeline_element_get_child_property() and
720  * ges_timeline_element_set_child_property() on the container.
721  *
722  * Returns: %TRUE if @child was successfully added to @container.
723  */
724 gboolean
725 ges_container_add (GESContainer * container, GESTimelineElement * child)
726 {
727   ChildMapping *mapping;
728   gboolean ret = FALSE;
729   GESContainerClass *class;
730   GList *current_children, *tmp;
731   GESContainerPrivate *priv;
732
733   g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
734   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (child), FALSE);
735   g_return_val_if_fail (GES_TIMELINE_ELEMENT_PARENT (child) == NULL, FALSE);
736
737   class = GES_CONTAINER_GET_CLASS (container);
738   priv = container->priv;
739
740   GST_DEBUG_OBJECT (container, "adding timeline element %" GST_PTR_FORMAT,
741       child);
742
743   /* freeze all notifies */
744   g_object_freeze_notify (G_OBJECT (container));
745   /* copy to use at end, since container->children may have child
746    * added to it */
747   current_children = g_list_copy_deep (container->children,
748       (GCopyFunc) gst_object_ref, NULL);
749   for (tmp = current_children; tmp; tmp = tmp->next)
750     g_object_freeze_notify (G_OBJECT (tmp->data));
751   g_object_freeze_notify (G_OBJECT (child));
752   gst_object_ref_sink (child);
753
754   if (class->add_child) {
755     if (class->add_child (container, child) == FALSE) {
756       GST_WARNING_OBJECT (container, "Error adding child %p", child);
757       goto done;
758     }
759   }
760
761   mapping = g_slice_new0 (ChildMapping);
762   mapping->child = gst_object_ref (child);
763   g_hash_table_insert (priv->mappings, child, mapping);
764   container->children = g_list_prepend (container->children, child);
765
766   /* Listen to all property changes */
767   mapping->start_notifyid =
768       g_signal_connect (G_OBJECT (child), "notify::start",
769       G_CALLBACK (_child_start_changed_cb), container);
770   mapping->duration_notifyid =
771       g_signal_connect (G_OBJECT (child), "notify::duration",
772       G_CALLBACK (_child_duration_changed_cb), container);
773
774   if (ges_timeline_element_set_parent (child, GES_TIMELINE_ELEMENT (container))
775       == FALSE) {
776     if (class->remove_child)
777       class->remove_child (container, child);
778
779     g_hash_table_remove (priv->mappings, child);
780     container->children = g_list_remove (container->children, child);
781
782     goto done;
783   }
784
785   _update_start_duration (container, child);
786   _ges_container_sort_children (container);
787
788   _ges_container_add_child_properties (container, child);
789   mapping->child_property_added_notifyid =
790       g_signal_connect (G_OBJECT (child), "child-property-added",
791       G_CALLBACK (_add_childs_child_property), container);
792   mapping->child_property_removed_notifyid =
793       g_signal_connect (G_OBJECT (child), "child-property-removed",
794       G_CALLBACK (_remove_childs_child_property), container);
795
796   priv->adding_children = g_list_prepend (priv->adding_children, child);
797   g_signal_emit (container, ges_container_signals[CHILD_ADDED_SIGNAL], 0,
798       child);
799   priv->adding_children = g_list_remove (priv->adding_children, child);
800
801   ret = TRUE;
802
803 done:
804   /* thaw all notifies */
805   /* Ignore notifies for the start and duration since the child should
806    * already be correctly set up */
807   container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
808   g_object_thaw_notify (G_OBJECT (container));
809   for (tmp = current_children; tmp; tmp = tmp->next)
810     g_object_thaw_notify (G_OBJECT (tmp->data));
811   g_object_thaw_notify (G_OBJECT (child));
812   g_list_free_full (current_children, gst_object_unref);
813   gst_object_unref (child);
814   container->children_control_mode = GES_CHILDREN_UPDATE;
815   return ret;
816 }
817
818 /**
819  * ges_container_remove:
820  * @container: A #GESContainer
821  * @child: The child to remove
822  *
823  * Removes a timeline element from the container. The element will no
824  * longer be controlled by the container.
825  *
826  * Returns: %TRUE if @child was successfully removed from @container.
827  */
828 gboolean
829 ges_container_remove (GESContainer * container, GESTimelineElement * child)
830 {
831   GESContainerClass *klass;
832   GESContainerPrivate *priv;
833   GList *current_children, *tmp;
834   gboolean ret = FALSE;
835
836   g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
837   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (child), FALSE);
838
839   GST_DEBUG_OBJECT (container, "removing child: %" GST_PTR_FORMAT, child);
840
841   klass = GES_CONTAINER_GET_CLASS (container);
842   priv = container->priv;
843
844   if (!(g_hash_table_lookup (priv->mappings, child))) {
845     GST_WARNING_OBJECT (container, "Element isn't controlled by this "
846         "container");
847     return FALSE;
848   }
849
850   /* ref the container since it might be destroyed when the child is
851    * removed! (see GESGroup ->child_removed) */
852   gst_object_ref (container);
853   /* freeze all notifies */
854   g_object_freeze_notify (G_OBJECT (container));
855   /* copy to use at end, since container->children may have child
856    * removed from it */
857   current_children = g_list_copy_deep (container->children,
858       (GCopyFunc) gst_object_ref, NULL);
859   for (tmp = current_children; tmp; tmp = tmp->next)
860     g_object_freeze_notify (G_OBJECT (tmp->data));
861
862
863   if (klass->remove_child) {
864     if (klass->remove_child (container, child) == FALSE)
865       goto done;
866   }
867
868   container->children = g_list_remove (container->children, child);
869   g_hash_table_remove (priv->mappings, child);
870
871   _ges_container_remove_child_properties (container, child);
872
873   if (!g_list_find (container->priv->adding_children, child)) {
874     g_signal_emit (container, ges_container_signals[CHILD_REMOVED_SIGNAL], 0,
875         child);
876   } else {
877     GESContainerClass *klass = GES_CONTAINER_GET_CLASS (container);
878
879     if (klass->child_removed)
880       klass->child_removed (container, child);
881
882     GST_INFO_OBJECT (container,
883         "Not emitting 'child-removed' signal as child"
884         " removal happend during 'child-added' signal emission");
885   }
886
887   _update_start_duration (container, child);
888
889   ret = TRUE;
890
891 done:
892   /* thaw all notifies */
893   g_object_thaw_notify (G_OBJECT (container));
894   for (tmp = current_children; tmp; tmp = tmp->next)
895     g_object_thaw_notify (G_OBJECT (tmp->data));
896   g_list_free_full (current_children, gst_object_unref);
897
898   gst_object_unref (container);
899
900   return ret;
901 }
902
903 static void
904 _get_children_recursively (GESContainer * container, GList ** children)
905 {
906   GList *tmp;
907
908   *children =
909       g_list_concat (*children, g_list_copy_deep (container->children,
910           (GCopyFunc) gst_object_ref, NULL));
911
912   for (tmp = container->children; tmp; tmp = tmp->next) {
913     GESTimelineElement *element = tmp->data;
914
915     if (GES_IS_CONTAINER (element))
916       _get_children_recursively (tmp->data, children);
917   }
918 }
919
920 /**
921  * ges_container_get_children:
922  * @container: A #GESContainer
923  * @recursive:  Whether to recursively get children in @container
924  *
925  * Get the list of timeline elements contained in the container. If
926  * @recursive is %TRUE, and the container contains other containers as
927  * children, then their children will be added to the list, in addition to
928  * themselves, and so on.
929  *
930  * Returns: (transfer full) (element-type GESTimelineElement): The list of
931  * #GESTimelineElement-s contained in @container.
932  */
933 GList *
934 ges_container_get_children (GESContainer * container, gboolean recursive)
935 {
936   GList *children = NULL;
937
938   g_return_val_if_fail (GES_IS_CONTAINER (container), NULL);
939
940   if (!recursive)
941     return g_list_copy_deep (container->children, (GCopyFunc) gst_object_ref,
942         NULL);
943
944   _get_children_recursively (container, &children);
945   return children;
946 }
947
948 /**
949  * ges_container_ungroup:
950  * @container: (transfer full): The container to ungroup
951  * @recursive: Whether to recursively ungroup @container
952  *
953  * Ungroups the container by splitting it into several containers
954  * containing various children of the original. The rules for how the
955  * container splits depends on the subclass. A #GESGroup will simply split
956  * into its children. A #GESClip will split into one #GESClip per
957  * #GESTrackType it overlaps with (so an audio-video clip will split into
958  * an audio clip and a video clip), where each clip contains all the
959  * #GESTrackElement-s from the original clip with a matching
960  * #GESTrackElement:track-type.
961  *
962  * If @recursive is %TRUE, and the container contains other containers as
963  * children, then they will also be ungrouped, and so on.
964  *
965  * Returns: (transfer full) (element-type GESContainer): The list of
966  * new #GESContainer-s created from the splitting of @container.
967  */
968 GList *
969 ges_container_ungroup (GESContainer * container, gboolean recursive)
970 {
971   GESContainerClass *klass;
972
973   g_return_val_if_fail (GES_IS_CONTAINER (container), NULL);
974
975   GST_DEBUG_OBJECT (container, "Ungrouping container %s recursively",
976       recursive ? "" : "not");
977
978   klass = GES_CONTAINER_GET_CLASS (container);
979   if (klass->ungroup == NULL) {
980     GST_INFO_OBJECT (container, "No ungoup virtual method, doint nothing");
981     return NULL;
982   }
983
984   return klass->ungroup (container, recursive);
985 }
986
987 /**
988  * ges_container_group:
989  * @containers: (transfer none)(element-type GESContainer) (allow-none):
990  * The #GESContainer-s to group
991  *
992  * Groups the containers into a single container by merging them. The
993  * containers must all belong to the same #GESTimelineElement:timeline.
994  *
995  * If the elements are all #GESClip-s then this method will attempt to
996  * combine them all into a single #GESClip. This should succeed if they:
997  * share the same #GESTimelineElement:start, #GESTimelineElement:duration
998  * and #GESTimelineElement:in-point; exist in the same layer; and all of
999  * the sources share the same #GESAsset. If this fails, or one of the
1000  * elements is not a #GESClip, this method will try to create a #GESGroup
1001  * instead.
1002  *
1003  * Returns: (transfer floating): The container created by merging
1004  * @containers, or %NULL if they could not be merged into a single
1005  * container.
1006  */
1007 GESContainer *
1008 ges_container_group (GList * containers)
1009 {
1010   GList *tmp;
1011   guint n_children;
1012   GType *children_types;
1013   GESTimelineElement *element;
1014   GObjectClass *clip_class;
1015
1016   guint i = 0;
1017   GESContainer *ret = NULL;
1018   GESTimeline *timeline = NULL;
1019
1020   if (containers) {
1021     element = GES_TIMELINE_ELEMENT (containers->data);
1022     timeline = GES_TIMELINE_ELEMENT_TIMELINE (element);
1023     g_return_val_if_fail (timeline, NULL);
1024   }
1025
1026   if (g_list_length (containers) == 1) {
1027     /* FIXME: Should return a floating **copy**. API specifies that the
1028      * returned element is created. So users might expect to be able to
1029      * freely dispose of the list, without the risk of the returned
1030      * element being freed as well.
1031      * TODO 2.0: (transfer full) would have been better */
1032     return containers->data;
1033   }
1034
1035   for (tmp = containers; tmp; tmp = tmp->next) {
1036     g_return_val_if_fail (GES_IS_CONTAINER (tmp->data), NULL);
1037     g_return_val_if_fail (GES_TIMELINE_ELEMENT_PARENT (tmp->data) == NULL,
1038         NULL);
1039     g_return_val_if_fail (GES_TIMELINE_ELEMENT_TIMELINE (tmp->data) == timeline,
1040         NULL);
1041   }
1042
1043   /* FIXME: how can user sub-classes interact with this if
1044    * ->grouping_priority is private? */
1045   children_types = g_type_children (GES_TYPE_CONTAINER, &n_children);
1046   g_qsort_with_data (children_types, n_children, sizeof (GType),
1047       (GCompareDataFunc) compare_grouping_prio, NULL);
1048
1049   for (i = 0; i < n_children; i++) {
1050     clip_class = g_type_class_peek (children_types[i]);
1051     /* FIXME: handle NULL ->group */
1052     ret = GES_CONTAINER_CLASS (clip_class)->group (containers);
1053
1054     if (ret)
1055       break;
1056   }
1057
1058   g_free (children_types);
1059   return ret;
1060 }
1061
1062 /**
1063  * ges_container_edit:
1064  * @container: The #GESContainer to edit
1065  * @layers: (element-type GESLayer) (nullable): A whitelist of layers
1066  * where the edit can be performed, %NULL allows all layers in the
1067  * timeline
1068  * @new_layer_priority: The priority/index of the layer @container should
1069  * be moved to. -1 means no move
1070  * @mode: The edit mode
1071  * @edge: The edge of @container where the edit should occur
1072  * @position: The edit position: a new location for the edge of @container
1073  * (in nanoseconds)
1074  *
1075  * Edits the container within its timeline.
1076  *
1077  * Returns: %TRUE if the edit of @container completed, %FALSE on failure.
1078  *
1079  * Deprecated: 1.18: use #ges_timeline_element_edit instead.
1080  */
1081 gboolean
1082 ges_container_edit (GESContainer * container, GList * layers,
1083     gint new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
1084 {
1085   g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
1086
1087   return ges_timeline_element_edit (GES_TIMELINE_ELEMENT (container),
1088       layers, new_layer_priority, mode, edge, position);
1089 }