diff --git a/ges/ges-auto-transition.c b/ges/ges-auto-transition.c index f28f37aee8..057267cb12 100644 --- a/ges/ges-auto-transition.c +++ b/ges/ges-auto-transition.c @@ -91,9 +91,11 @@ neighbour_changed_cb (GESClip * clip, GParamSpec * arg G_GNUC_UNUSED, } self->positioning = TRUE; + ELEMENT_SET_FLAG (self->transition_clip, GES_TIMELINE_ELEMENT_SET_SIMPLE); _set_start0 (GES_TIMELINE_ELEMENT (self->transition_clip), _START (self->next_source)); _set_duration0 (GES_TIMELINE_ELEMENT (self->transition_clip), new_duration); + ELEMENT_SET_FLAG (self->transition_clip, GES_TIMELINE_ELEMENT_SET_SIMPLE); self->positioning = FALSE; } diff --git a/ges/ges-clip.c b/ges/ges-clip.c index bdb5989a29..faaf9974d9 100644 --- a/ges/ges-clip.c +++ b/ges/ges-clip.c @@ -218,8 +218,11 @@ _set_duration (GESTimelineElement * element, GstClockTime duration) for (tmp = container->children; tmp; tmp = g_list_next (tmp)) { GESTimelineElement *child = (GESTimelineElement *) tmp->data; - if (child != container->initiated_move) + if (child != container->initiated_move) { + ELEMENT_SET_FLAG (child, GES_TIMELINE_ELEMENT_SET_SIMPLE); _set_duration0 (GES_TIMELINE_ELEMENT (child), duration); + ELEMENT_UNSET_FLAG (child, GES_TIMELINE_ELEMENT_SET_SIMPLE); + } } container->children_control_mode = GES_CHILDREN_UPDATE; diff --git a/ges/ges-source-clip.c b/ges/ges-source-clip.c index 7f5903c14d..21c9f9562b 100644 --- a/ges/ges-source-clip.c +++ b/ges/ges-source-clip.c @@ -46,49 +46,6 @@ enum G_DEFINE_TYPE_WITH_PRIVATE (GESSourceClip, ges_source_clip, GES_TYPE_CLIP); -static gboolean -_set_start (GESTimelineElement * element, GstClockTime start) -{ - GESTimelineElement *toplevel = - ges_timeline_element_get_toplevel_parent (element); - - gst_object_unref (toplevel); - if (element->timeline - && !ELEMENT_FLAG_IS_SET (element, GES_TIMELINE_ELEMENT_SET_SIMPLE) - && !ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) { - if (!ges_timeline_move_object_simple (element->timeline, element, NULL, - GES_EDGE_NONE, start)) - return FALSE; - return -1; - } - - return - GES_TIMELINE_ELEMENT_CLASS (ges_source_clip_parent_class)->set_start - (element, start); -} - -static gboolean -_set_duration (GESTimelineElement * element, GstClockTime duration) -{ - GESTimelineElement *toplevel = - ges_timeline_element_get_toplevel_parent (element); - - gst_object_unref (toplevel); - if (element->timeline - && !ELEMENT_FLAG_IS_SET (element, GES_TIMELINE_ELEMENT_SET_SIMPLE) - && !ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) { - if (!timeline_trim_object (element->timeline, element, - GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element), NULL, GES_EDGE_END, - element->start + duration)) - return FALSE; - return -1; - } - - return - GES_TIMELINE_ELEMENT_CLASS (ges_source_clip_parent_class)->set_duration - (element, duration); -} - static void ges_source_clip_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) @@ -119,14 +76,10 @@ static void ges_source_clip_class_init (GESSourceClipClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass); object_class->get_property = ges_source_clip_get_property; object_class->set_property = ges_source_clip_set_property; object_class->finalize = ges_source_clip_finalize; - - element_class->set_start = _set_start; - element_class->set_duration = _set_duration; } static void diff --git a/ges/ges-timeline-element.c b/ges/ges-timeline-element.c index ecf58a2f9b..b4c89d9f4d 100644 --- a/ges/ges-timeline-element.c +++ b/ges/ges-timeline-element.c @@ -972,6 +972,7 @@ ges_timeline_element_get_timeline (GESTimelineElement * self) gboolean ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start) { + gboolean emit_notify = TRUE; GESTimelineElementClass *klass; GESTimelineElement *toplevel_container, *parent; @@ -980,13 +981,24 @@ ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start) if (self->start == start) return TRUE; - klass = GES_TIMELINE_ELEMENT_GET_CLASS (self); - GST_DEBUG_OBJECT (self, "current start: %" GST_TIME_FORMAT " new start: %" GST_TIME_FORMAT, GST_TIME_ARGS (GES_TIMELINE_ELEMENT_START (self)), GST_TIME_ARGS (start)); toplevel_container = ges_timeline_element_get_toplevel_parent (self); + + if (self->timeline + && !ELEMENT_FLAG_IS_SET (self, GES_TIMELINE_ELEMENT_SET_SIMPLE) + && !ELEMENT_FLAG_IS_SET (toplevel_container, + GES_TIMELINE_ELEMENT_SET_SIMPLE)) { + if (!ges_timeline_move_object_simple (self->timeline, self, NULL, + GES_EDGE_NONE, start)) { + gst_object_unref (toplevel_container); + return FALSE; + } + + emit_notify = FALSE; + } parent = self->parent; /* FIXME This should not belong to GESTimelineElement */ @@ -1003,9 +1015,10 @@ ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start) } gst_object_unref (toplevel_container); + klass = GES_TIMELINE_ELEMENT_GET_CLASS (self); if (klass->set_start) { gint res = klass->set_start (self, start); - if (res == TRUE) { + if (res == TRUE && emit_notify) { self->start = start; g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_START]); } @@ -1126,19 +1139,40 @@ ges_timeline_element_set_duration (GESTimelineElement * self, GstClockTime duration) { GESTimelineElementClass *klass; + gboolean emit_notify = TRUE; + GESTimelineElement *toplevel; g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE); - klass = GES_TIMELINE_ELEMENT_GET_CLASS (self); + toplevel = ges_timeline_element_get_toplevel_parent (self); + if (self->timeline && + !ELEMENT_FLAG_IS_SET (self, GES_TIMELINE_ELEMENT_SET_SIMPLE) && + !ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) { + gboolean res; + + res = timeline_trim_object (self->timeline, self, + GES_TIMELINE_ELEMENT_LAYER_PRIORITY (self), NULL, + GES_EDGE_END, self->start + duration); + + if (!res) { + gst_object_unref (toplevel); + + return FALSE; + } + + emit_notify = res == -1; + } + gst_object_unref (toplevel); GST_DEBUG_OBJECT (self, "current duration: %" GST_TIME_FORMAT " new duration: %" GST_TIME_FORMAT, GST_TIME_ARGS (GES_TIMELINE_ELEMENT_DURATION (self)), GST_TIME_ARGS (duration)); + klass = GES_TIMELINE_ELEMENT_GET_CLASS (self); if (klass->set_duration) { gint res = klass->set_duration (self, duration); - if (res == TRUE) { + if (res == TRUE && emit_notify) { self->duration = duration; g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION]); } diff --git a/ges/ges-timeline-tree.c b/ges/ges-timeline-tree.c index 6f875dcf80..8956ff74b3 100644 --- a/ges/ges-timeline-tree.c +++ b/ges/ges-timeline-tree.c @@ -75,6 +75,10 @@ struct _TreeIterationData GHashTable *moved_clips; GList *neighbours; + + /* Data related to trimming groups */ + GstClockTime trim_group_start; + GstClockTime trim_group_end; } tree_iteration_data_init = { .root = NULL, .res = TRUE, @@ -91,6 +95,8 @@ struct _TreeIterationData .edge = GES_EDGE_NONE, .moved_clips = NULL, .neighbours = NULL, + .trim_group_start = GST_CLOCK_TIME_NONE, + .trim_group_end = GST_CLOCK_TIME_NONE, }; /* *INDENT-ON* */ @@ -783,6 +789,47 @@ error: goto done; } +static gboolean +trim_group_get_vals (TreeIterationData * data, GESTimelineElement * e, + GstClockTimeDiff * n_start, GstClockTimeDiff * n_inpoint, + GstClockTimeDiff * n_duration) +{ + GstClockTimeDiff offset; + GstClockTimeDiff group_nstart = + GST_CLOCK_DIFF (data->start_diff, data->trim_group_start); + GstClockTimeDiff group_nend = + GST_CLOCK_DIFF (data->duration_diff, data->trim_group_end); + + if (data->edge == GES_EDGE_START && + ((group_nstart >= e->start) || (e->start == data->trim_group_start))) { + + offset = GST_CLOCK_DIFF (group_nstart, e->start); + *n_start = group_nstart; + *n_inpoint = GST_CLOCK_DIFF (offset, e->inpoint); + *n_duration = + GST_CLOCK_DIFF (*n_start, (GstClockTimeDiff) e->start + e->duration); + + GST_DEBUG_OBJECT (data->element, "Trimming %" GES_FORMAT " start", + GES_ARGS (e)); + return TRUE; + } else if (data->edge == GES_EDGE_END && + ((group_nend <= _END (e)) || (_END (e) == data->trim_group_end))) { + + offset = GST_CLOCK_DIFF (group_nend, _END (e)); + *n_start = e->start; + *n_inpoint = e->inpoint; + *n_duration = GST_CLOCK_DIFF (offset, e->duration); + + GST_DEBUG_OBJECT (data->element, "Trimming %" GES_FORMAT " end", + GES_ARGS (e)); + + return TRUE; + } + + /* Ignoring child */ + return FALSE; +} + static gboolean check_trim_child (GNode * node, TreeIterationData * data) { @@ -793,6 +840,12 @@ check_trim_child (GNode * node, TreeIterationData * data) GST_CLOCK_DIFF (data->duration_diff, e->duration) : GST_CLOCK_DIFF (n_start, (GstClockTimeDiff) e->start + e->duration); + if (GST_CLOCK_TIME_IS_VALID (data->trim_group_start) + && !trim_group_get_vals (data, e, &n_start, &n_inpoint, &n_duration)) { + GST_DEBUG_OBJECT (data->element, "Not trimming"); + return FALSE; + } + if (!timeline_tree_can_move_element_internal (data->root, e, (gint64) ges_timeline_element_get_layer_priority (e) - data->priority_diff, n_start, n_inpoint, n_duration, NULL, @@ -823,21 +876,33 @@ timeline_tree_can_trim_element_internal (GNode * root, TreeIterationData * data) static void trim_simple (GESTimelineElement * element, GstClockTimeDiff offset, - GESEdge edge) + GESEdge edge, TreeIterationData * data) { + GESTimelineElement *toplevel = + ges_timeline_element_get_toplevel_parent (element); + + GstClockTimeDiff n_start = GST_CLOCK_DIFF (offset, element->start); + GstClockTimeDiff n_inpoint = GST_CLOCK_DIFF (offset, element->inpoint); + GstClockTimeDiff n_duration = edge == GES_EDGE_END + ? GST_CLOCK_DIFF (offset, element->duration) + : element->duration + offset; + + if (data && GST_CLOCK_TIME_IS_VALID (data->trim_group_start)) + g_assert (trim_group_get_vals (data, element, &n_start, &n_inpoint, + &n_duration)); + ELEMENT_SET_FLAG (element, GES_TIMELINE_ELEMENT_SET_SIMPLE); - if (edge == GES_EDGE_END) { - ges_timeline_element_set_duration (element, GST_CLOCK_DIFF (offset, - element->duration)); - } else { - ges_timeline_element_set_start (element, GST_CLOCK_DIFF (offset, - element->start)); - ges_timeline_element_set_inpoint (element, GST_CLOCK_DIFF (offset, - element->inpoint)); - ges_timeline_element_set_duration (element, element->duration + offset); + ELEMENT_SET_FLAG (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE); + if (edge != GES_EDGE_END) { + ges_timeline_element_set_start (element, n_start); + ges_timeline_element_set_inpoint (element, n_inpoint); } + ges_timeline_element_set_duration (element, n_duration); + GST_LOG ("Trimmed %" GES_FORMAT, GES_ARGS (element)); ELEMENT_UNSET_FLAG (element, GES_TIMELINE_ELEMENT_SET_SIMPLE); + ELEMENT_UNSET_FLAG (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE); + gst_object_unref (toplevel); } #define SET_TRIMMING_DATA(data, _edge, offset) G_STMT_START { \ @@ -845,6 +910,10 @@ trim_simple (GESTimelineElement * element, GstClockTimeDiff offset, data.start_diff = (_edge) == GES_EDGE_END ? 0 : (offset); \ data.inpoint_diff = (_edge) == GES_EDGE_END ? 0 : (offset); \ data.duration_diff = (_edge) == GES_EDGE_END ? (offset) : -(offset); \ + if (GES_IS_GROUP (data.element)) {\ + data.trim_group_start = data.element->start;\ + data.trim_group_end = _END (data.element); \ + } \ } G_STMT_END @@ -869,6 +938,10 @@ timeline_tree_trim (GNode * root, GESTimelineElement * element, }; TreeIterationData data = tree_iteration_data_init; + /* Make sure to check all children of clips */ + if (GES_IS_TRACK_ELEMENT (element) && element->parent) + element = element->parent; + data.root = root; data.element = element; data.priority_diff = @@ -876,13 +949,12 @@ timeline_tree_trim (GNode * root, GESTimelineElement * element, new_layer_priority; data.snapping = snapping_distance ? &snapping : NULL; data.moved_clips = g_hash_table_new (g_direct_hash, g_direct_equal); - SET_TRIMMING_DATA (data, edge, offset); GST_INFO ("%" GES_FORMAT " trimming %s with offset %" G_GINT64_FORMAT "", GES_ARGS (element), edge == GES_EDGE_END ? "end" : "start", offset); - g_node_traverse (find_node (root, element), G_IN_ORDER, - G_TRAVERSE_LEAVES, -1, (GNodeTraverseFunc) add_element_to_list, - &data.movings); + g_node_traverse (find_node (root, get_toplevel_container (element)), + G_IN_ORDER, G_TRAVERSE_LEAVES, -1, + (GNodeTraverseFunc) add_element_to_list, &data.movings); if (!timeline_tree_can_trim_element_internal (root, &data)) { GST_INFO ("Can not trim object."); @@ -909,7 +981,7 @@ timeline_tree_trim (GNode * root, GESTimelineElement * element, g_hash_table_iter_init (&iter, data.moved_clips); while (g_hash_table_iter_next (&iter, (gpointer *) & elem, NULL)) - trim_simple (elem, offset, edge); + trim_simple (elem, offset, edge, &data); timeline_tree_create_transitions (root, ges_timeline_find_auto_transition); timeline_update_transition (root->data); @@ -1155,9 +1227,9 @@ timeline_tree_roll (GNode * root, GESTimelineElement * element, } } - trim_simple (element, offset, edge); + trim_simple (element, offset, edge, NULL); for (tmp = data.neighbours; tmp; tmp = tmp->next) - trim_simple (tmp->data, offset, data.edge); + trim_simple (tmp->data, offset, data.edge, NULL); done: timeline_update_duration (root->data); diff --git a/ges/ges-timeline.c b/ges/ges-timeline.c index bf086b07f1..f8cb45c6d6 100644 --- a/ges/ges-timeline.c +++ b/ges/ges-timeline.c @@ -1096,8 +1096,10 @@ _trim_transition (GESTimeline * timeline, GESTimelineElement * element, GESLayer *layer = ges_timeline_get_layer (timeline, GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element)); - if (!ges_layer_get_auto_transition (layer)) - goto fail; + if (!ges_layer_get_auto_transition (layer)) { + gst_object_unref (layer); + return -1; + } gst_object_unref (layer); for (tmp = timeline->priv->auto_transitions; tmp; tmp = tmp->next) { @@ -1123,10 +1125,6 @@ _trim_transition (GESTimeline * timeline, GESTimelineElement * element, } return FALSE; - -fail: - gst_object_unref (layer); - return FALSE; } diff --git a/tests/check/ges/group.c b/tests/check/ges/group.c index edbd9117b0..091601c417 100644 --- a/tests/check/ges/group.c +++ b/tests/check/ges/group.c @@ -195,40 +195,67 @@ GST_START_TEST (test_move_group) * |----------------------------------| * | 7--------- 2----------| * layer1: | | clip1 | | clip2 | - * | 22--------30 62----------| + * | 20--------30 60----------| * |----------------------------------| */ ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 12); CHECK_OBJECT_PROPS (clip, 12, 2, 3); - CHECK_OBJECT_PROPS (clip1, 22, 7, 8); - CHECK_OBJECT_PROPS (clip2, 62, 2, 48); + CHECK_OBJECT_PROPS (clip1, 20, 5, 10); + CHECK_OBJECT_PROPS (clip2, 60, 0, 50); CHECK_OBJECT_PROPS (group, 12, 0, 98); ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2); /* Setting the duration would lead to overlaps */ - ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), 10); + fail_if (ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), + 10)); CHECK_OBJECT_PROPS (clip, 12, 2, 3); - CHECK_OBJECT_PROPS (clip1, 22, 7, 8); - CHECK_OBJECT_PROPS (clip2, 62, 2, 48); + CHECK_OBJECT_PROPS (clip1, 20, 5, 10); + CHECK_OBJECT_PROPS (clip2, 60, 0, 50); CHECK_OBJECT_PROPS (group, 12, 0, 98); ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), 100); CHECK_OBJECT_PROPS (clip, 12, 2, 3); - CHECK_OBJECT_PROPS (clip1, 22, 7, 8); - CHECK_OBJECT_PROPS (clip2, 62, 2, 50); + CHECK_OBJECT_PROPS (clip1, 20, 5, 10); + CHECK_OBJECT_PROPS (clip2, 60, 0, 52); CHECK_OBJECT_PROPS (group, 12, 0, 100); ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (group), 20); CHECK_OBJECT_PROPS (clip, 20, 2, 3); - CHECK_OBJECT_PROPS (clip1, 30, 7, 8); - CHECK_OBJECT_PROPS (clip2, 70, 2, 50); + CHECK_OBJECT_PROPS (clip1, 28, 5, 10); + CHECK_OBJECT_PROPS (clip2, 68, 0, 52); CHECK_OBJECT_PROPS (group, 20, 0, 100); + /* Trim fails because clip inpoint would become negative */ fail_if (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 10)); CHECK_OBJECT_PROPS (clip, 20, 2, 3); - CHECK_OBJECT_PROPS (clip1, 30, 7, 8); - CHECK_OBJECT_PROPS (clip2, 70, 2, 50); + CHECK_OBJECT_PROPS (clip1, 28, 5, 10); + CHECK_OBJECT_PROPS (clip2, 68, 0, 52); CHECK_OBJECT_PROPS (group, 20, 0, 100); + fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 18)); + CHECK_OBJECT_PROPS (clip, 18, 0, 5); + CHECK_OBJECT_PROPS (clip1, 28, 5, 10); + CHECK_OBJECT_PROPS (clip2, 68, 0, 52); + CHECK_OBJECT_PROPS (group, 18, 0, 102); + + fail_unless (ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip), + 17)); + CHECK_OBJECT_PROPS (clip, 18, 0, 17); + CHECK_OBJECT_PROPS (clip1, 28, 5, 10); + CHECK_OBJECT_PROPS (clip2, 68, 0, 52); + CHECK_OBJECT_PROPS (group, 18, 0, 102); + + fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 30)); + CHECK_OBJECT_PROPS (clip, 30, 12, 5); + CHECK_OBJECT_PROPS (clip1, 30, 7, 8); + CHECK_OBJECT_PROPS (clip2, 68, 0, 52); + CHECK_OBJECT_PROPS (group, 30, 0, 90); + + fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 25)); + CHECK_OBJECT_PROPS (clip, 25, 7, 10); + CHECK_OBJECT_PROPS (clip1, 25, 2, 13); + CHECK_OBJECT_PROPS (clip2, 68, 0, 52); + CHECK_OBJECT_PROPS (group, 25, 0, 95); + ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2); check_destroyed (G_OBJECT (timeline), G_OBJECT (group), NULL); gst_object_unref (asset); diff --git a/tests/check/python/common.py b/tests/check/python/common.py index 81a439c6c4..0f55c929e6 100644 --- a/tests/check/python/common.py +++ b/tests/check/python/common.py @@ -188,8 +188,8 @@ class GESSimpleTimelineTest(GESTest): len(self.track_types)) self.layer = self.timeline.append_layer() - def add_clip(self, start, in_point, duration): - clip = GES.TestClip() + def add_clip(self, start, in_point, duration, asset_type=GES.TestClip): + clip = GES.Asset.request(asset_type, None).extract() clip.props.start = start clip.props.in_point = in_point clip.props.duration = duration @@ -197,11 +197,11 @@ class GESSimpleTimelineTest(GESTest): return clip - def append_clip(self, layer=0): + def append_clip(self, layer=0, asset_type=GES.TestClip): while len(self.timeline.get_layers()) < layer + 1: self.timeline.append_layer() layer = self.timeline.get_layers()[layer] - clip = GES.TestClip() + clip = GES.Asset.request(asset_type, None).extract() clip.props.start = layer.get_duration() clip.props.duration = 10 self.assertTrue(layer.add_clip(clip)) @@ -215,6 +215,9 @@ class GESSimpleTimelineTest(GESTest): for clip in layer.get_clips(): layer_timings.append( (type(clip), clip.props.start, clip.props.duration)) + for child in clip.get_children(True): + self.assertEqual(child.props.start, clip.props.start) + self.assertEqual(child.props.duration, clip.props.duration) res.append(layer_timings) if topology != res: diff --git a/tests/check/python/test_timeline.py b/tests/check/python/test_timeline.py index 2cfb7afb4e..4b3c9c91dd 100644 --- a/tests/check/python/test_timeline.py +++ b/tests/check/python/test_timeline.py @@ -505,6 +505,160 @@ class TestEditing(common.GESSimpleTimelineTest): ] ]) + def test_illegal_effect_move(self): + c0 = self.append_clip() + self.append_clip() + self.assertTimelineTopology([ + [ + (GES.TestClip, 0, 10), + (GES.TestClip, 10, 10), + ] + ]) + + effect = GES.Effect.new("agingtv") + c0.add(effect) + self.assertTimelineTopology([ + [ + (GES.TestClip, 0, 10), + (GES.TestClip, 10, 10), + ] + ]) + + self.assertFalse(effect.set_start(10)) + self.assertEqual(effect.props.start, 0) + self.assertTimelineTopology([ + [ + (GES.TestClip, 0, 10), + (GES.TestClip, 10, 10), + ] + ]) + + + self.assertFalse(effect.set_duration(20)) + self.assertEqual(effect.props.duration, 10) + self.assertTimelineTopology([ + [ + (GES.TestClip, 0, 10), + (GES.TestClip, 10, 10), + ] + ]) + + def test_moving_overlay_clip_in_group(self): + c0 = self.append_clip() + overlay = self.append_clip(asset_type=GES.TextOverlayClip) + group = GES.Group.new() + group.add(c0) + group.add(overlay) + + self.assertTimelineTopology([ + [ + (GES.TestClip, 0, 10), + (GES.TextOverlayClip, 10, 10), + ] + ], groups=[(c0, overlay)]) + + self.assertTrue(overlay.set_start(20)) + self.assertTimelineTopology([ + [ + (GES.TestClip, 10, 10), + (GES.TextOverlayClip, 20, 10), + ] + ], groups=[(c0, overlay)]) + + def test_moving_group_in_group(self): + c0 = self.append_clip() + overlay = self.append_clip(asset_type=GES.TextOverlayClip) + group0 = GES.Group.new() + group0.add(c0) + group0.add(overlay) + + c1 = self.append_clip() + group1 = GES.Group.new() + group1.add(group0) + group1.add(c1) + + self.assertTimelineTopology([ + [ + (GES.TestClip, 0, 10), + (GES.TextOverlayClip, 10, 10), + (GES.TestClip, 20, 10), + ] + ], groups=[(c1, group0), (c0, overlay)]) + + self.assertTrue(group0.set_start(10)) + self.assertTimelineTopology([ + [ + (GES.TestClip, 10, 10), + (GES.TextOverlayClip, 20, 10), + (GES.TestClip, 30, 10), + ] + ], groups=[(c1, group0), (c0, overlay)]) + self.check_element_values(group0, 10, 0, 20) + self.check_element_values(group1, 10, 0, 30) + + def test_illegal_group_child_move(self): + clip0 = self.append_clip() + _clip1 = self.add_clip(20, 0, 10) + overlay = self.add_clip(20, 0, 10, asset_type=GES.TextOverlayClip) + + group = GES.Group.new() + group.add(clip0) + group.add(overlay) + + self.assertTimelineTopology([ + [ + (GES.TestClip, 0, 10), + (GES.TextOverlayClip, 20, 10), + (GES.TestClip, 20, 10), + ] + ], groups=[(clip0, overlay),]) + + # Can't move as clip0 and clip1 would fully overlap + self.assertFalse(overlay.set_start(40)) + self.assertTimelineTopology([ + [ + (GES.TestClip, 0, 10), + (GES.TextOverlayClip, 20, 10), + (GES.TestClip, 20, 10), + ] + ], groups=[(clip0, overlay)]) + + def test_child_duration_change(self): + c0 = self.append_clip() + + self.assertTimelineTopology([ + [ + (GES.TestClip, 0, 10), + ] + ]) + self.assertTrue(c0.set_duration(40)) + self.assertTimelineTopology([ + [ + (GES.TestClip, 0, 40), + ] + ]) + + c0.children[0].set_duration(10) + self.assertTimelineTopology([ + [ + (GES.TestClip, 0, 10), + ] + ]) + + self.assertTrue(c0.set_start(40)) + self.assertTimelineTopology([ + [ + (GES.TestClip, 40, 10), + ] + ]) + + c0.children[0].set_start(10) + self.assertTimelineTopology([ + [ + (GES.TestClip, 10, 10), + ] + ]) + class TestInvalidOverlaps(common.GESSimpleTimelineTest):