completion: Update to new gstreamer core helpers
[platform/upstream/gstreamer.git] / ges / ges-group.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * SECTION:gesgroup
22  * @title: GESGroup
23  * @short_description: Class that permits to group GESClip-s in a timeline,
24  * letting the user manage it a single GESTimelineElement
25  *
26  * A #GESGroup is an object which controls one or more
27  * #GESClips in one or more #GESLayer(s).
28  *
29  * To instanciate a group, you should use the ges_container_group method,
30  * this will be responsible for deciding what subclass of #GESContainer
31  * should be instaciated to group the various #GESTimelineElement passed
32  * in parametter.
33  */
34
35 #include "ges-group.h"
36 #include "ges.h"
37 #include "ges-internal.h"
38
39 #include <string.h>
40
41 #define parent_class ges_group_parent_class
42 G_DEFINE_TYPE (GESGroup, ges_group, GES_TYPE_CONTAINER);
43
44 #define GES_CHILDREN_INIBIT_SIGNAL_EMISSION (GES_CHILDREN_LAST + 1)
45 #define GES_GROUP_SIGNALS_IDS_DATA_KEY_FORMAT "ges-group-signals-ids-%p"
46
47 struct _GESGroupPrivate
48 {
49   gboolean reseting_start;
50
51   guint32 max_layer_prio;
52
53   /* This is used while were are setting ourselve a proper timing value,
54    * in this case the value should always be kept */
55   gboolean setting_value;
56 };
57
58 typedef struct
59 {
60   GESLayer *layer;
61   gulong child_clip_changed_layer_sid;
62   gulong child_priority_changed_sid;
63   gulong child_group_priority_changed_sid;
64 } ChildSignalIds;
65
66 enum
67 {
68   PROP_0,
69   PROP_START,
70   PROP_INPOINT,
71   PROP_DURATION,
72   PROP_MAX_DURATION,
73   PROP_PRIORITY,
74   PROP_LAST
75 };
76
77 static GParamSpec *properties[PROP_LAST] = { NULL, };
78
79 /****************************************************
80  *              Our listening of children           *
81  ****************************************************/
82 static void
83 _update_our_values (GESGroup * group)
84 {
85   GList *tmp;
86   GESContainer *container = GES_CONTAINER (group);
87   guint32 min_layer_prio = G_MAXINT32, max_layer_prio = 0;
88
89   for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
90     GESContainer *child = tmp->data;
91
92     if (GES_IS_CLIP (child)) {
93       GESLayer *layer = ges_clip_get_layer (GES_CLIP (child));
94       gint32 prio = ges_layer_get_priority (layer);
95
96       min_layer_prio = MIN (prio, min_layer_prio);
97       max_layer_prio = MAX (prio, max_layer_prio);
98     } else if (GES_IS_GROUP (child)) {
99       gint32 prio = _PRIORITY (child), height = GES_CONTAINER_HEIGHT (child);
100
101       min_layer_prio = MIN (prio, min_layer_prio);
102       max_layer_prio = MAX ((prio + height), max_layer_prio);
103     }
104   }
105
106   if (min_layer_prio != _PRIORITY (group)) {
107     group->priv->setting_value = TRUE;
108     _set_priority0 (GES_TIMELINE_ELEMENT (group), min_layer_prio);
109     group->priv->setting_value = FALSE;
110     for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
111       GESTimelineElement *child = tmp->data;
112       guint32 child_prio = GES_IS_CLIP (child) ?
113           ges_clip_get_layer_priority (GES_CLIP (child)) : _PRIORITY (child);
114
115       _ges_container_set_priority_offset (container,
116           child, min_layer_prio - child_prio);
117     }
118   }
119
120   group->priv->max_layer_prio = max_layer_prio;
121   _ges_container_set_height (GES_CONTAINER (group),
122       max_layer_prio - min_layer_prio + 1);
123 }
124
125 static void
126 _child_priority_changed_cb (GESLayer * layer,
127     GParamSpec * arg G_GNUC_UNUSED, GESTimelineElement * clip)
128 {
129   GESContainer *container = GES_CONTAINER (GES_TIMELINE_ELEMENT_PARENT (clip));
130
131   gint layer_prio = ges_layer_get_priority (layer);
132   gint offset = _ges_container_get_priority_offset (container, clip);
133
134   if (container->children_control_mode != GES_CHILDREN_UPDATE) {
135     GST_DEBUG_OBJECT (container, "Ignoring updated");
136     return;
137   }
138
139   if (layer_prio + offset == _PRIORITY (container))
140     return;
141
142   container->initiated_move = clip;
143   _set_priority0 (GES_TIMELINE_ELEMENT (container), layer_prio + offset);
144   container->initiated_move = NULL;
145 }
146
147 static void
148 _child_clip_changed_layer_cb (GESTimelineElement * clip,
149     GParamSpec * arg G_GNUC_UNUSED, GESGroup * group)
150 {
151   ChildSignalIds *sigids;
152   gchar *signals_ids_key;
153   GESLayer *old_layer, *new_layer;
154   gint offset, layer_prio = ges_clip_get_layer_priority (GES_CLIP (clip));
155   GESContainer *container = GES_CONTAINER (group);
156
157   offset = _ges_container_get_priority_offset (container, clip);
158   signals_ids_key =
159       g_strdup_printf (GES_GROUP_SIGNALS_IDS_DATA_KEY_FORMAT, clip);
160   sigids = g_object_get_data (G_OBJECT (group), signals_ids_key);
161   g_free (signals_ids_key);
162   old_layer = sigids->layer;
163
164   new_layer = ges_clip_get_layer (GES_CLIP (clip));
165
166   if (sigids->child_priority_changed_sid) {
167     g_signal_handler_disconnect (old_layer, sigids->child_priority_changed_sid);
168     sigids->child_priority_changed_sid = 0;
169   }
170
171   if (new_layer) {
172     sigids->child_priority_changed_sid =
173         g_signal_connect (new_layer, "notify::priority",
174         (GCallback) _child_priority_changed_cb, clip);
175   }
176   sigids->layer = new_layer;
177
178   if (container->children_control_mode != GES_CHILDREN_UPDATE) {
179     if (container->children_control_mode == GES_CHILDREN_INIBIT_SIGNAL_EMISSION) {
180       container->children_control_mode = GES_CHILDREN_UPDATE;
181       g_signal_stop_emission_by_name (clip, "notify::layer");
182     }
183     return;
184   }
185
186   if (new_layer && (layer_prio + offset < 0 ||
187           (GES_TIMELINE_ELEMENT_TIMELINE (group) &&
188               layer_prio + offset + GES_CONTAINER_HEIGHT (group) - 1 >
189               g_list_length (GES_TIMELINE_ELEMENT_TIMELINE (group)->layers)))) {
190
191     GST_INFO_OBJECT (container, "Trying to move to a layer outside of"
192         "the timeline layers, moving back to old layer (prio %i)",
193         _PRIORITY (group) - offset);
194
195     container->children_control_mode = GES_CHILDREN_INIBIT_SIGNAL_EMISSION;
196     ges_clip_move_to_layer (GES_CLIP (clip), old_layer);
197     g_signal_stop_emission_by_name (clip, "notify::layer");
198
199     return;
200   }
201
202   container->initiated_move = clip;
203   _set_priority0 (GES_TIMELINE_ELEMENT (group), layer_prio + offset);
204   container->initiated_move = NULL;
205 }
206
207 static void
208 _child_group_priority_changed (GESTimelineElement * child,
209     GParamSpec * arg G_GNUC_UNUSED, GESGroup * group)
210 {
211   gint offset;
212   GESContainer *container = GES_CONTAINER (group);
213
214   if (container->children_control_mode != GES_CHILDREN_UPDATE) {
215     GST_DEBUG_OBJECT (group, "Ignoring updated");
216     return;
217   }
218
219   offset = _ges_container_get_priority_offset (container, child);
220
221   if (_PRIORITY (group) < offset ||
222       (GES_TIMELINE_ELEMENT_TIMELINE (group) &&
223           _PRIORITY (group) + offset + GES_CONTAINER_HEIGHT (group) >
224           g_list_length (GES_TIMELINE_ELEMENT_TIMELINE (group)->layers))) {
225
226     GST_WARNING_OBJECT (container, "Trying to move to a layer outside of"
227         "the timeline layers");
228
229     return;
230   }
231
232   container->initiated_move = child;
233   _set_priority0 (GES_TIMELINE_ELEMENT (group), _PRIORITY (child) + offset);
234   container->initiated_move = NULL;
235 }
236
237 /****************************************************
238  *              GESTimelineElement vmethods         *
239  ****************************************************/
240 static gboolean
241 _trim (GESTimelineElement * group, GstClockTime start)
242 {
243   GList *tmp;
244   GstClockTime last_child_end = 0, oldstart = _START (group);
245   GESContainer *container = GES_CONTAINER (group);
246   GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (group);
247   gboolean ret = TRUE, expending = (start < _START (group));
248
249   if (timeline == NULL) {
250     GST_DEBUG ("Not in a timeline yet");
251
252     return FALSE;
253   }
254
255   container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
256   for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
257     GESTimelineElement *child = tmp->data;
258
259     if (expending) {
260       /* If the start is bigger, we do not touch it (in case we are expending) */
261       if (_START (child) > oldstart) {
262         GST_DEBUG_OBJECT (child, "Skipping as not at begining of the group");
263         continue;
264       }
265
266       ret &= ges_timeline_element_trim (child, start);
267     } else {
268       if (start > _END (child))
269         ret &= ges_timeline_element_trim (child, _END (child));
270       else if (_START (child) < start && _DURATION (child))
271         ret &= ges_timeline_element_trim (child, start);
272
273     }
274   }
275
276   for (tmp = GES_CONTAINER_CHILDREN (group); tmp; tmp = tmp->next) {
277     if (_DURATION (tmp->data))
278       last_child_end =
279           MAX (GES_TIMELINE_ELEMENT_END (tmp->data), last_child_end);
280   }
281
282   GES_GROUP (group)->priv->setting_value = TRUE;
283   _set_start0 (group, start);
284   _set_duration0 (group, last_child_end - start);
285   GES_GROUP (group)->priv->setting_value = FALSE;
286   container->children_control_mode = GES_CHILDREN_UPDATE;
287
288   return ret;
289 }
290
291 static gboolean
292 _set_priority (GESTimelineElement * element, guint32 priority)
293 {
294   GList *tmp, *layers;
295   gint diff = priority - _PRIORITY (element);
296   GESContainer *container = GES_CONTAINER (element);
297
298   if (GES_GROUP (element)->priv->setting_value == TRUE)
299     return TRUE;
300
301   container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
302   layers = GES_TIMELINE_ELEMENT_TIMELINE (element) ?
303       GES_TIMELINE_ELEMENT_TIMELINE (element)->layers : NULL;
304
305   if (layers == NULL) {
306     GST_WARNING_OBJECT (element, "Not any layer in the timeline, not doing"
307         "anything, timeline: %" GST_PTR_FORMAT,
308         GES_TIMELINE_ELEMENT_TIMELINE (element));
309
310     return FALSE;
311   } else if (priority + GES_CONTAINER_HEIGHT (container) - 1 >
312       g_list_length (layers)) {
313     GST_WARNING_OBJECT (container, "Trying to move to a layer outside of"
314         "the timeline layers");
315     return FALSE;
316   }
317
318   for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
319     GESTimelineElement *child = tmp->data;
320
321     if (child != container->initiated_move) {
322       if (GES_IS_CLIP (child)) {
323         guint32 layer_prio =
324             ges_clip_get_layer_priority (GES_CLIP (child)) + diff;
325
326         GST_DEBUG_OBJECT (child, "moving from layer: %i to %i",
327             ges_clip_get_layer_priority (GES_CLIP (child)), layer_prio);
328         ges_clip_move_to_layer (GES_CLIP (child),
329             g_list_nth_data (layers, layer_prio));
330       } else if (GES_IS_GROUP (child)) {
331         GST_DEBUG_OBJECT (child, "moving from %i to %i",
332             _PRIORITY (child), diff + _PRIORITY (child));
333         ges_timeline_element_set_priority (child, diff + _PRIORITY (child));
334       }
335     }
336   }
337   container->children_control_mode = GES_CHILDREN_UPDATE;
338
339   return TRUE;
340 }
341
342 static gboolean
343 _set_start (GESTimelineElement * element, GstClockTime start)
344 {
345   GList *tmp;
346   gint64 diff = start - _START (element);
347   GESContainer *container = GES_CONTAINER (element);
348
349   if (GES_GROUP (element)->priv->setting_value == TRUE)
350     /* Let GESContainer update itself */
351     return GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_start (element,
352         start);
353
354
355   container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
356   for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
357     GESTimelineElement *child = (GESTimelineElement *) tmp->data;
358
359     if (child != container->initiated_move &&
360         (_END (child) > _START (element) || _END (child) > start)) {
361       _set_start0 (child, _START (child) + diff);
362     }
363   }
364   container->children_control_mode = GES_CHILDREN_UPDATE;
365
366   return TRUE;
367 }
368
369 static gboolean
370 _set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
371 {
372   return FALSE;
373 }
374
375 static gboolean
376 _set_duration (GESTimelineElement * element, GstClockTime duration)
377 {
378   GList *tmp;
379   GstClockTime last_child_end = 0, new_end;
380   GESContainer *container = GES_CONTAINER (element);
381   GESGroupPrivate *priv = GES_GROUP (element)->priv;
382
383   if (priv->setting_value == TRUE)
384     /* Let GESContainer update itself */
385     return GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_duration (element,
386         duration);
387
388   if (container->initiated_move == NULL) {
389     gboolean expending = (_DURATION (element) < duration);
390
391     new_end = _START (element) + duration;
392     container->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
393     for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
394       GESTimelineElement *child = tmp->data;
395       GstClockTime n_dur;
396
397       if ((!expending && _END (child) > new_end) ||
398           (expending && (_END (child) >= _END (element)))) {
399         n_dur = MAX (0, ((gint64) (new_end - _START (child))));
400         _set_duration0 (child, n_dur);
401       }
402     }
403     container->children_control_mode = GES_CHILDREN_UPDATE;
404   }
405
406   for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
407     if (_DURATION (tmp->data))
408       last_child_end =
409           MAX (GES_TIMELINE_ELEMENT_END (tmp->data), last_child_end);
410   }
411
412   priv->setting_value = TRUE;
413   _set_duration0 (element, last_child_end - _START (element));
414   priv->setting_value = FALSE;
415
416   return FALSE;
417 }
418
419 /****************************************************
420  *                                                  *
421  *  GESContainer virtual methods implementation     *
422  *                                                  *
423  ****************************************************/
424
425 static gboolean
426 _add_child (GESContainer * group, GESTimelineElement * child)
427 {
428   g_return_val_if_fail (GES_IS_CONTAINER (child), FALSE);
429
430   return TRUE;
431 }
432
433 static void
434 _child_added (GESContainer * group, GESTimelineElement * child)
435 {
436   GList *children, *tmp;
437   gchar *signals_ids_key;
438   ChildSignalIds *signals_ids;
439
440   GESGroupPrivate *priv = GES_GROUP (group)->priv;
441   GstClockTime last_child_end = 0, first_child_start = G_MAXUINT64;
442
443   if (!GES_TIMELINE_ELEMENT_TIMELINE (group)) {
444     timeline_add_group (GES_TIMELINE_ELEMENT_TIMELINE (child),
445         GES_GROUP (group));
446     timeline_emit_group_added (GES_TIMELINE_ELEMENT_TIMELINE (child),
447         GES_GROUP (group));
448   }
449
450   children = GES_CONTAINER_CHILDREN (group);
451
452   for (tmp = children; tmp; tmp = tmp->next) {
453     last_child_end = MAX (GES_TIMELINE_ELEMENT_END (tmp->data), last_child_end);
454     first_child_start =
455         MIN (GES_TIMELINE_ELEMENT_START (tmp->data), first_child_start);
456   }
457
458   priv->setting_value = TRUE;
459   if (first_child_start != GES_TIMELINE_ELEMENT_START (group)) {
460     group->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
461     _set_start0 (GES_TIMELINE_ELEMENT (group), first_child_start);
462   }
463
464   if (last_child_end != GES_TIMELINE_ELEMENT_END (group)) {
465     _set_duration0 (GES_TIMELINE_ELEMENT (group),
466         last_child_end - first_child_start);
467   }
468   priv->setting_value = FALSE;
469
470   group->children_control_mode = GES_CHILDREN_UPDATE;
471   _update_our_values (GES_GROUP (group));
472
473   signals_ids_key =
474       g_strdup_printf (GES_GROUP_SIGNALS_IDS_DATA_KEY_FORMAT, child);
475   signals_ids = g_malloc0 (sizeof (ChildSignalIds));
476   g_object_set_data_full (G_OBJECT (group), signals_ids_key,
477       signals_ids, g_free);
478   g_free (signals_ids_key);
479   if (GES_IS_CLIP (child)) {
480     GESLayer *layer = ges_clip_get_layer (GES_CLIP (child));
481
482     signals_ids->child_clip_changed_layer_sid =
483         g_signal_connect (child, "notify::layer",
484         (GCallback) _child_clip_changed_layer_cb, group);
485
486     if (layer) {
487       signals_ids->child_priority_changed_sid = g_signal_connect (layer,
488           "notify::priority", (GCallback) _child_priority_changed_cb, child);
489     }
490     signals_ids->layer = layer;
491
492   } else if (GES_IS_GROUP (child), group) {
493     signals_ids->child_group_priority_changed_sid =
494         g_signal_connect (child, "notify::priority",
495         (GCallback) _child_group_priority_changed, group);
496   }
497 }
498
499 static void
500 _disconnect_signals (GESGroup * group, GESTimelineElement * child,
501     ChildSignalIds * sigids)
502 {
503   if (sigids->child_group_priority_changed_sid) {
504     g_signal_handler_disconnect (child,
505         sigids->child_group_priority_changed_sid);
506     sigids->child_group_priority_changed_sid = 0;
507   }
508
509   if (sigids->child_clip_changed_layer_sid) {
510     g_signal_handler_disconnect (child, sigids->child_clip_changed_layer_sid);
511     sigids->child_clip_changed_layer_sid = 0;
512   }
513
514   if (sigids->child_priority_changed_sid) {
515     g_signal_handler_disconnect (sigids->layer,
516         sigids->child_priority_changed_sid);
517     sigids->child_priority_changed_sid = 0;
518   }
519 }
520
521
522 static void
523 _child_removed (GESContainer * group, GESTimelineElement * child)
524 {
525   GList *children;
526   GstClockTime first_child_start;
527   gchar *signals_ids_key;
528   ChildSignalIds *sigids;
529   GESGroupPrivate *priv = GES_GROUP (group)->priv;
530
531   _ges_container_sort_children (group);
532
533   children = GES_CONTAINER_CHILDREN (group);
534
535   signals_ids_key =
536       g_strdup_printf (GES_GROUP_SIGNALS_IDS_DATA_KEY_FORMAT, child);
537   sigids = g_object_get_data (G_OBJECT (group), signals_ids_key);
538   _disconnect_signals (GES_GROUP (group), child, sigids);
539   g_free (signals_ids_key);
540   if (children == NULL) {
541     GST_FIXME_OBJECT (group, "Auto destroy myself?");
542     timeline_remove_group (GES_TIMELINE_ELEMENT_TIMELINE (group),
543         GES_GROUP (group));
544     return;
545   }
546
547   priv->setting_value = TRUE;
548   first_child_start = GES_TIMELINE_ELEMENT_START (children->data);
549   if (first_child_start > GES_TIMELINE_ELEMENT_START (group)) {
550     group->children_control_mode = GES_CHILDREN_IGNORE_NOTIFIES;
551     _set_start0 (GES_TIMELINE_ELEMENT (group), first_child_start);
552     group->children_control_mode = GES_CHILDREN_UPDATE;
553   }
554   priv->setting_value = FALSE;
555 }
556
557 static GList *
558 _ungroup (GESContainer * group, gboolean recursive)
559 {
560   GPtrArray *children_array;
561   GESTimeline *timeline;
562   GList *children, *tmp, *ret = NULL;
563
564   /* We choose 16 just as an arbitrary value */
565   children_array = g_ptr_array_sized_new (16);
566   timeline = GES_TIMELINE_ELEMENT_TIMELINE (group);
567
568   children = ges_container_get_children (group, FALSE);
569   for (tmp = children; tmp; tmp = tmp->next) {
570     GESTimelineElement *child = tmp->data;
571
572     gst_object_ref (child);
573     ges_container_remove (group, child);
574     g_ptr_array_add (children_array, child);
575     ret = g_list_append (ret, child);
576   }
577
578   if (timeline)
579     timeline_emit_group_removed (timeline, (GESGroup *) group, children_array);
580   g_ptr_array_free (children_array, TRUE);
581   g_list_free_full (children, gst_object_unref);
582
583   /* No need to remove from the timeline here, this will be done in _child_removed */
584
585   return ret;
586 }
587
588 static GESContainer *
589 _group (GList * containers)
590 {
591   GList *tmp;
592   GESTimeline *timeline = NULL;
593   GESContainer *ret = g_object_new (GES_TYPE_GROUP, NULL);
594
595   if (!containers)
596     return ret;
597
598   for (tmp = containers; tmp; tmp = tmp->next) {
599     if (!timeline) {
600       timeline = GES_TIMELINE_ELEMENT_TIMELINE (tmp->data);
601     } else if (timeline != GES_TIMELINE_ELEMENT_TIMELINE (tmp->data)) {
602       g_object_unref (ret);
603
604       return NULL;
605     }
606
607     ges_container_add (ret, tmp->data);
608   }
609
610   /* No need to add to the timeline here, this will be done in _child_added */
611
612   return ret;
613 }
614
615 static GESTimelineElement *
616 _paste (GESTimelineElement * element, GESTimelineElement * ref,
617     GstClockTime paste_position)
618 {
619   GESTimelineElement *ngroup =
620       GES_TIMELINE_ELEMENT_CLASS (parent_class)->paste (element, ref,
621       paste_position);
622
623   if (ngroup) {
624     if (GES_CONTAINER_CHILDREN (ngroup)) {
625       timeline_add_group (GES_TIMELINE_ELEMENT_TIMELINE (GES_CONTAINER_CHILDREN
626               (ngroup)->data), GES_GROUP (element));
627       timeline_emit_group_added (GES_TIMELINE_ELEMENT_TIMELINE
628           (GES_CONTAINER_CHILDREN (ngroup)->data), GES_GROUP (element));
629     }
630   }
631
632   return ngroup;
633 }
634
635
636 /****************************************************
637  *                                                  *
638  *    GObject virtual methods implementation        *
639  *                                                  *
640  ****************************************************/
641 static void
642 ges_group_get_property (GObject * object, guint property_id,
643     GValue * value, GParamSpec * pspec)
644 {
645   GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
646
647   switch (property_id) {
648     case PROP_START:
649       g_value_set_uint64 (value, self->start);
650       break;
651     case PROP_INPOINT:
652       g_value_set_uint64 (value, self->inpoint);
653       break;
654     case PROP_DURATION:
655       g_value_set_uint64 (value, self->duration);
656       break;
657     case PROP_MAX_DURATION:
658       g_value_set_uint64 (value, self->maxduration);
659       break;
660     case PROP_PRIORITY:
661       g_value_set_uint (value, self->priority);
662       break;
663     default:
664       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
665   }
666 }
667
668 static void
669 ges_group_set_property (GObject * object, guint property_id,
670     const GValue * value, GParamSpec * pspec)
671 {
672   GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
673
674   switch (property_id) {
675     case PROP_START:
676       ges_timeline_element_set_start (self, g_value_get_uint64 (value));
677       break;
678     case PROP_INPOINT:
679       ges_timeline_element_set_inpoint (self, g_value_get_uint64 (value));
680       break;
681     case PROP_DURATION:
682       ges_timeline_element_set_duration (self, g_value_get_uint64 (value));
683       break;
684     case PROP_PRIORITY:
685       ges_timeline_element_set_priority (self, g_value_get_uint (value));
686       break;
687     case PROP_MAX_DURATION:
688       ges_timeline_element_set_max_duration (self, g_value_get_uint64 (value));
689       break;
690     default:
691       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
692   }
693 }
694
695 static void
696 ges_group_class_init (GESGroupClass * klass)
697 {
698   GObjectClass *object_class = G_OBJECT_CLASS (klass);
699   GESContainerClass *container_class = GES_CONTAINER_CLASS (klass);
700   GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
701
702   g_type_class_add_private (klass, sizeof (GESGroupPrivate));
703
704   object_class->get_property = ges_group_get_property;
705   object_class->set_property = ges_group_set_property;
706
707   element_class->trim = _trim;
708   element_class->set_duration = _set_duration;
709   element_class->set_inpoint = _set_inpoint;
710   element_class->set_start = _set_start;
711   element_class->set_priority = _set_priority;
712   element_class->paste = _paste;
713
714   /* We override start, inpoint, duration and max-duration from GESTimelineElement
715    * in order to makes sure those fields are not serialized.
716    */
717   /**
718    * GESGroup:start:
719    *
720    * The position of the object in its container (in nanoseconds).
721    */
722   properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
723       "The position in the container", 0, G_MAXUINT64, 0,
724       G_PARAM_READWRITE | GES_PARAM_NO_SERIALIZATION);
725
726   /**
727    * GESGroup:in-point:
728    *
729    * The in-point at which this #GESGroup will start outputting data
730    * from its contents (in nanoseconds).
731    *
732    * Ex : an in-point of 5 seconds means that the first outputted buffer will
733    * be the one located 5 seconds in the controlled resource.
734    */
735   properties[PROP_INPOINT] =
736       g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
737       G_MAXUINT64, 0, G_PARAM_READWRITE | GES_PARAM_NO_SERIALIZATION);
738
739   /**
740    * GESGroup:duration:
741    *
742    * The duration (in nanoseconds) which will be used in the container
743    */
744   properties[PROP_DURATION] =
745       g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
746       G_MAXUINT64, GST_CLOCK_TIME_NONE,
747       G_PARAM_READWRITE | GES_PARAM_NO_SERIALIZATION);
748
749   /**
750    * GESGroup:max-duration:
751    *
752    * The maximum duration (in nanoseconds) of the #GESGroup.
753    */
754   properties[PROP_MAX_DURATION] =
755       g_param_spec_uint64 ("max-duration", "Maximum duration",
756       "The maximum duration of the object", 0, G_MAXUINT64, GST_CLOCK_TIME_NONE,
757       G_PARAM_READWRITE | G_PARAM_CONSTRUCT | GES_PARAM_NO_SERIALIZATION);
758
759   /**
760    * GESTGroup:priority:
761    *
762    * The priority of the object.
763    */
764   properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
765       "The priority of the object", 0, G_MAXUINT, 0,
766       G_PARAM_READWRITE | GES_PARAM_NO_SERIALIZATION);
767
768   g_object_class_install_properties (object_class, PROP_LAST, properties);
769
770   container_class->add_child = _add_child;
771   container_class->child_added = _child_added;
772   container_class->child_removed = _child_removed;
773   container_class->ungroup = _ungroup;
774   container_class->group = _group;
775   container_class->grouping_priority = 0;
776 }
777
778 static void
779 ges_group_init (GESGroup * self)
780 {
781   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
782       GES_TYPE_GROUP, GESGroupPrivate);
783
784   self->priv->setting_value = FALSE;
785 }
786
787 /****************************************************
788  *                                                  *
789  *              API implementation                  *
790  *                                                  *
791  ****************************************************/
792
793 /**
794  * ges_group_new:
795  *
796  * Created a new empty #GESGroup, if you want to group several container
797  * together, it is recommanded to use the #ges_container_group method so the
798  * proper subclass is selected.
799  *
800  * Returns: (transfer floating): The new empty group.
801  */
802 GESGroup *
803 ges_group_new (void)
804 {
805   return g_object_new (GES_TYPE_GROUP, NULL);
806 }