timeline: Make get_groups public

Had to separate timeline_emit_group_added from timeline_add_group
to avoid emitting group-added when the project is being loaded.

Reviewed-by: Thibault Saunier <thibault.saunier@collabora.com>
Differential Revision: https://phabricator.freedesktop.org/D1302
This commit is contained in:
Alexandru Băluț 2016-09-06 14:27:38 +02:00 committed by Thibault Saunier
parent 2df506e5ba
commit a63c754222
9 changed files with 210 additions and 20 deletions

View file

@ -451,6 +451,8 @@ _add_all_groups (GESFormatter * self)
GList *lchild; GList *lchild;
PendingGroup *pgroup = tmp->data; PendingGroup *pgroup = tmp->data;
timeline_add_group (self->timeline, pgroup->group);
for (lchild = ((PendingGroup *) tmp->data)->pending_children; lchild; for (lchild = ((PendingGroup *) tmp->data)->pending_children; lchild;
lchild = lchild->next) { lchild = lchild->next) {
child = g_hash_table_lookup (priv->containers, lchild->data); child = g_hash_table_lookup (priv->containers, lchild->data);
@ -458,8 +460,6 @@ _add_all_groups (GESFormatter * self)
GST_DEBUG_OBJECT (tmp->data, "Adding %s child %" GST_PTR_FORMAT " %s", GST_DEBUG_OBJECT (tmp->data, "Adding %s child %" GST_PTR_FORMAT " %s",
(const gchar *) lchild->data, child, (const gchar *) lchild->data, child,
GES_TIMELINE_ELEMENT_NAME (child)); GES_TIMELINE_ELEMENT_NAME (child));
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (pgroup->group),
self->timeline);
ges_container_add (GES_CONTAINER (pgroup->group), child); ges_container_add (GES_CONTAINER (pgroup->group), child);
} }
} }

View file

@ -442,6 +442,8 @@ _child_added (GESContainer * group, GESTimelineElement * child)
if (!GES_TIMELINE_ELEMENT_TIMELINE (group)) { if (!GES_TIMELINE_ELEMENT_TIMELINE (group)) {
timeline_add_group (GES_TIMELINE_ELEMENT_TIMELINE (child), timeline_add_group (GES_TIMELINE_ELEMENT_TIMELINE (child),
GES_GROUP (group)); GES_GROUP (group));
timeline_emit_group_added (GES_TIMELINE_ELEMENT_TIMELINE (child),
GES_GROUP (group));
} }
children = GES_CONTAINER_CHILDREN (group); children = GES_CONTAINER_CHILDREN (group);
@ -618,9 +620,12 @@ _paste (GESTimelineElement * element, GESTimelineElement * ref,
paste_position); paste_position);
if (ngroup) { if (ngroup) {
if (GES_CONTAINER_CHILDREN (ngroup)) if (GES_CONTAINER_CHILDREN (ngroup)) {
timeline_add_group (GES_TIMELINE_ELEMENT_TIMELINE (GES_CONTAINER_CHILDREN timeline_add_group (GES_TIMELINE_ELEMENT_TIMELINE (GES_CONTAINER_CHILDREN
(ngroup)->data), GES_GROUP (element)); (ngroup)->data), GES_GROUP (element));
timeline_emit_group_added (GES_TIMELINE_ELEMENT_TIMELINE
(GES_CONTAINER_CHILDREN (ngroup)->data), GES_GROUP (element));
}
} }
return ngroup; return ngroup;

View file

@ -100,10 +100,15 @@ timeline_context_to_layer (GESTimeline *timeline, gint offset);
G_GNUC_INTERNAL void G_GNUC_INTERNAL void
timeline_add_group (GESTimeline *timeline, timeline_add_group (GESTimeline *timeline,
GESGroup *group); GESGroup *group);
G_GNUC_INTERNAL G_GNUC_INTERNAL void
void
timeline_remove_group (GESTimeline *timeline, timeline_remove_group (GESTimeline *timeline,
GESGroup *group); GESGroup *group);
G_GNUC_INTERNAL void
timeline_emit_group_added (GESTimeline *timeline,
GESGroup *group);
G_GNUC_INTERNAL void
timeline_emit_group_removed (GESTimeline * timeline,
GESGroup * group, GPtrArray * array);
G_GNUC_INTERNAL G_GNUC_INTERNAL
gboolean gboolean
@ -118,12 +123,6 @@ G_GNUC_INTERNAL
void void
timeline_fill_gaps (GESTimeline *timeline); timeline_fill_gaps (GESTimeline *timeline);
G_GNUC_INTERNAL GList *
timeline_get_groups (GESTimeline * timeline);
G_GNUC_INTERNAL void
timeline_emit_group_removed (GESTimeline * timeline,
GESGroup * group, GPtrArray * array);
G_GNUC_INTERNAL G_GNUC_INTERNAL
void void
track_resort_and_fill_gaps (GESTrack *track); track_resort_and_fill_gaps (GESTrack *track);

View file

@ -550,7 +550,7 @@ ges_timeline_class_init (GESTimelineClass * klass)
* @timeline: the #GESTimeline * @timeline: the #GESTimeline
* @layer: the #GESLayer that was added to the timeline * @layer: the #GESLayer that was added to the timeline
* *
* Will be emitted after the layer was added to the timeline. * Will be emitted after a new layer is added to the timeline.
*/ */
ges_timeline_signals[LAYER_ADDED] = ges_timeline_signals[LAYER_ADDED] =
g_signal_new ("layer-added", G_TYPE_FROM_CLASS (klass), g_signal_new ("layer-added", G_TYPE_FROM_CLASS (klass),
@ -574,7 +574,7 @@ ges_timeline_class_init (GESTimelineClass * klass)
* @timeline: the #GESTimeline * @timeline: the #GESTimeline
* @group: the #GESGroup * @group: the #GESGroup
* *
* Will be emitted after a group has been added to to the timeline. * Will be emitted after a new group is added to to the timeline.
*/ */
ges_timeline_signals[GROUP_ADDED] = ges_timeline_signals[GROUP_ADDED] =
g_signal_new ("group-added", G_TYPE_FROM_CLASS (klass), g_signal_new ("group-added", G_TYPE_FROM_CLASS (klass),
@ -2185,7 +2185,18 @@ timeline_add_group (GESTimeline * timeline, GESGroup * group)
gst_object_ref_sink (group)); gst_object_ref_sink (group));
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (group), timeline); ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (group), timeline);
}
/**
* timeline_emit_group_added:
* @timeline: a #GESTimeline
* @group: group that was added
*
* Emit group-added signal.
*/
void
timeline_emit_group_added (GESTimeline * timeline, GESGroup * group)
{
g_signal_emit (timeline, ges_timeline_signals[GROUP_ADDED], 0, group); g_signal_emit (timeline, ges_timeline_signals[GROUP_ADDED], 0, group);
} }
@ -2217,12 +2228,6 @@ timeline_remove_group (GESTimeline * timeline, GESGroup * group)
gst_object_unref (group); gst_object_unref (group);
} }
GList *
timeline_get_groups (GESTimeline * timeline)
{
return timeline->priv->groups;
}
static GPtrArray * static GPtrArray *
select_tracks_for_object_default (GESTimeline * timeline, select_tracks_for_object_default (GESTimeline * timeline,
GESClip * clip, GESTrackElement * tr_object, gpointer user_data) GESClip * clip, GESTrackElement * tr_object, gpointer user_data)
@ -2917,6 +2922,22 @@ ges_timeline_save_to_uri (GESTimeline * timeline, const gchar * uri,
return ret; return ret;
} }
/**
* ges_timeline_get_groups:
* @timeline: a #GESTimeline
*
* Get the list of #GESGroup present in the Timeline.
*
* Returns: (transfer none) (element-type GESGroup): the list of
* #GESGroup that contain clips present in the timeline's layers.
* Must not be changed.
*/
GList *
ges_timeline_get_groups (GESTimeline * timeline)
{
return timeline->priv->groups;
}
/** /**
* ges_timeline_append_layer: * ges_timeline_append_layer:
* @timeline: a #GESTimeline * @timeline: a #GESTimeline

View file

@ -120,6 +120,8 @@ GESTrack * ges_timeline_get_track_for_pad (GESTimeline *timeline, GstPad *pad);
GstPad * ges_timeline_get_pad_for_track (GESTimeline * timeline, GESTrack *track); GstPad * ges_timeline_get_pad_for_track (GESTimeline * timeline, GESTrack *track);
GList *ges_timeline_get_tracks (GESTimeline *timeline); GList *ges_timeline_get_tracks (GESTimeline *timeline);
GList* ges_timeline_get_groups (GESTimeline * timeline);
gboolean ges_timeline_commit (GESTimeline * timeline); gboolean ges_timeline_commit (GESTimeline * timeline);
gboolean ges_timeline_commit_sync (GESTimeline * timeline); gboolean ges_timeline_commit_sync (GESTimeline * timeline);

View file

@ -1293,7 +1293,7 @@ _save_groups (GESXmlFormatter * self, GString * str, GESTimeline * timeline)
GList *seen_groups = NULL; GList *seen_groups = NULL;
g_string_append (str, " <groups>\n"); g_string_append (str, " <groups>\n");
for (tmp = timeline_get_groups (timeline); tmp; tmp = tmp->next) { for (tmp = ges_timeline_get_groups (timeline); tmp; tmp = tmp->next) {
_save_group (self, str, &seen_groups, tmp->data); _save_group (self, str, &seen_groups, tmp->data);
} }
g_string_append (str, " </groups>\n"); g_string_append (str, " </groups>\n");

View file

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Alexandru Băluț <alexandru.balut@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301, USA.
from gi.repository import GES
from gi.repository import GLib
import tempfile
def create_main_loop():
"""Creates a MainLoop with a timeout."""
mainloop = GLib.MainLoop()
timed_out = False
def quit_cb(unused):
nonlocal timed_out
timed_out = True
mainloop.quit()
def run(timeout_seconds=5):
source = GLib.timeout_source_new_seconds(timeout_seconds)
source.set_callback(quit_cb)
source.attach()
GLib.MainLoop.run(mainloop)
source.destroy()
if timed_out:
raise Exception("Timed out after %s seconds" % timeout_seconds)
mainloop.run = run
return mainloop
def create_project(with_group=False, saved=False):
"""Creates a project with two clips in a group."""
project = GES.Project()
timeline = project.extract()
layer = timeline.append_layer()
if with_group:
clip1 = GES.TitleClip()
clip1.set_start(0)
clip1.set_duration(10)
layer.add_clip(clip1)
clip2 = GES.TitleClip()
clip2.set_start(100)
clip2.set_duration(10)
layer.add_clip(clip2)
group = GES.Container.group([clip1, clip2])
if saved:
uri = "file://%s" % tempfile.NamedTemporaryFile(suffix=".xges").name
project.save(timeline, uri, None, overwrite=True)
return timeline

View file

@ -27,6 +27,8 @@ from gi.repository import GES # noqa
import unittest # noqa import unittest # noqa
from unittest import mock from unittest import mock
import common
Gst.init(None) Gst.init(None)
GES.init() GES.init()
@ -167,3 +169,33 @@ class TestGroup(unittest.TestCase):
child_removed_cb.reset_mock() child_removed_cb.reset_mock()
group.ungroup(recursive=False) group.ungroup(recursive=False)
child_removed_cb.assert_called_once_with(group, clip1) child_removed_cb.assert_called_once_with(group, clip1)
def test_loaded_project_has_groups(self):
mainloop = common.create_main_loop()
timeline = common.create_project(with_group=True, saved=True)
layer, = timeline.get_layers()
group, = timeline.get_groups()
self.assertEqual(len(layer.get_clips()), 2)
for clip in layer.get_clips():
self.assertEqual(clip.get_parent(), group)
# Reload the project, check the group.
project = GES.Project.new(uri=timeline.get_asset().props.uri)
loaded_called = False
def loaded(unused_project, unused_timeline):
nonlocal loaded_called
loaded_called = True
mainloop.quit()
project.connect("loaded", loaded)
timeline = project.extract()
mainloop.run()
self.assertTrue(loaded_called)
layer, = timeline.get_layers()
group, = timeline.get_groups()
self.assertEqual(len(layer.get_clips()), 2)
for clip in layer.get_clips():
self.assertEqual(clip.get_parent(), group)

View file

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Alexandru Băluț <alexandru.balut@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301, USA.
import gi
gi.require_version("Gst", "1.0")
gi.require_version("GES", "1.0")
from gi.repository import Gst # noqa
from gi.repository import GES # noqa
import unittest # noqa
from unittest import mock
import common
Gst.init(None)
GES.init()
class TestTimeline(unittest.TestCase):
def test_signals_not_emitted_when_loading(self):
mainloop = common.create_main_loop()
timeline = common.create_project(with_group=True, saved=True)
# Reload the project, check the group.
project = GES.Project.new(uri=timeline.get_asset().props.uri)
loaded_called = False
def loaded(unused_project, unused_timeline):
nonlocal loaded_called
loaded_called = True
mainloop.quit()
project.connect("loaded", loaded)
timeline = project.extract()
signals = ["layer-added", "group-added", "track-added"]
called = []
handle = mock.Mock()
for signal in signals:
timeline.connect(signal, handle)
mainloop.run()
self.assertTrue(loaded_called)
handle.assert_not_called()