ges_timeline_element_trim
ges_timeline_element_get_toplevel_parent
ges_timeline_element_copy
+ges_timeline_element_paste
ges_timeline_element_get_name
ges_timeline_element_set_name
ges_timeline_element_list_children_properties
return ret;
}
+static gboolean
+_paste (GESTimelineElement * element, GESTimelineElement * ref,
+ GstClockTime paste_position)
+{
+ GList *tmp;
+ GESClip *self = GES_CLIP (element);
+ GESClip *refclip = GES_CLIP (ref);
+
+ ges_clip_set_moving_from_layer (self, TRUE);
+ ges_layer_add_clip (refclip->priv->layer, self);
+ ges_clip_set_moving_from_layer (self, FALSE);
+
+ ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (self), paste_position);
+
+ for (tmp = GES_CONTAINER_CHILDREN (refclip); tmp; tmp = tmp->next) {
+ GESTrackElement *new_trackelement, *trackelement =
+ GES_TRACK_ELEMENT (tmp->data);
+
+ new_trackelement =
+ GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT
+ (trackelement), FALSE));
+ if (new_trackelement == NULL) {
+ GST_WARNING_OBJECT (trackelement, "Could not create a copy");
+ continue;
+ }
+
+ ges_container_add (GES_CONTAINER (self),
+ GES_TIMELINE_ELEMENT (new_trackelement));
+
+ ges_track_element_copy_properties (GES_TIMELINE_ELEMENT (trackelement),
+ GES_TIMELINE_ELEMENT (new_trackelement));
+
+ ges_track_element_copy_bindings (trackelement, new_trackelement,
+ GST_CLOCK_TIME_NONE);
+ }
+
+ return TRUE;
+}
/****************************************************
element_class->set_inpoint = _set_inpoint;
element_class->set_priority = _set_priority;
element_class->set_max_duration = _set_max_duration;
- /* TODO implement the deep_copy Virtual method */
+ element_class->paste = _paste;
container_class->add_child = _add_child;
container_class->remove_child = _remove_child;
ges_track_element_copy_properties (GES_TIMELINE_ELEMENT (trackelement),
GES_TIMELINE_ELEMENT (new_trackelement));
- ges_track_element_split_bindings (trackelement, new_trackelement,
+ ges_track_element_copy_bindings (trackelement, new_trackelement,
position - start + inpoint);
}
return types ^ GES_TRACK_TYPE_UNKNOWN;
}
+static gboolean
+_paste (GESTimelineElement * element, GESTimelineElement * ref,
+ GstClockTime paste_position)
+{
+ GList *tmp;
+ GESContainer *self = GES_CONTAINER (element);
+ GESContainer *refcontainer = GES_CONTAINER (ref);
+
+ for (tmp = GES_CONTAINER_CHILDREN (refcontainer); tmp; tmp = tmp->next) {
+ ChildMapping *map;
+ GESTimelineElement *child, *refchild = GES_TIMELINE_ELEMENT (tmp->data);
+
+ map = g_hash_table_lookup (refcontainer->priv->mappings, refchild);
+ child = ges_timeline_element_copy (GES_TIMELINE_ELEMENT (refchild), TRUE);
+
+ ges_timeline_element_paste (child, paste_position + map->start_offset);
+ ges_timeline_element_set_timeline (element,
+ GES_TIMELINE_ELEMENT_TIMELINE (ref));
+ ges_container_add (self, child);
+ }
+
+ return TRUE;
+}
+
+
/******************************************
* *
* GObject virtual methods implementation *
element_class->list_children_properties = _list_children_properties;
element_class->lookup_child = _lookup_child;
element_class->get_track_types = _get_track_types;
+ element_class->paste = _paste;
/* No default implementations */
klass->remove_child = NULL;
return ret;
}
+static gboolean
+_paste (GESTimelineElement * element, GESTimelineElement * ref,
+ GstClockTime paste_position)
+{
+ if (GES_TIMELINE_ELEMENT_CLASS (parent_class)->paste (element,
+ ref, paste_position)) {
+
+ if (GES_CONTAINER_CHILDREN (element))
+ timeline_add_group (GES_TIMELINE_ELEMENT_TIMELINE (GES_CONTAINER_CHILDREN
+ (element)->data), GES_GROUP (element));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/****************************************************
* *
element_class->set_inpoint = _set_inpoint;
element_class->set_start = _set_start;
element_class->set_priority = _set_priority;
+ element_class->paste = _paste;
/* We override start, inpoint, duration and max-duration from GESTimelineElement
* in order to makes sure those fields are not serialized.
G_GNUC_INTERNAL void ges_track_element_copy_properties (GESTimelineElement * element,
GESTimelineElement * elementcopy);
-G_GNUC_INTERNAL void ges_track_element_split_bindings (GESTrackElement *element,
+G_GNUC_INTERNAL void ges_track_element_copy_bindings (GESTrackElement *element,
GESTrackElement *new_element,
guint64 position);
* The hashtable should look like
* {GParamaSpec ---> child}*/
GHashTable *children_props;
+
+ GESTimelineElement *copied_from;
};
static gboolean
}
static void
+ges_timeline_element_dispose (GObject * object)
+{
+ GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
+
+ g_clear_object (&self->priv->copied_from);
+}
+
+static void
ges_timeline_element_finalize (GObject * self)
{
GESTimelineElement *tle = GES_TIMELINE_ELEMENT (self);
G_SIGNAL_NO_HOOKS, 0, NULL, NULL, g_cclosure_marshal_generic,
G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_PARAM);
+ object_class->dispose = ges_timeline_element_dispose;
object_class->finalize = ges_timeline_element_finalize;
klass->set_parent = NULL;
" on class %s. Can not finish the copy", G_OBJECT_CLASS_NAME (klass));
}
+ if (deep) {
+ ret->priv->copied_from = gst_object_ref (self);
+ }
+
return ret;
}
GESTrackType
ges_timeline_element_get_track_types (GESTimelineElement * self)
{
- g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
- g_return_if_fail (GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_track_types);
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), 0);
+ g_return_val_if_fail (GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_track_types,
+ 0);
return GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_track_types (self);
}
+
+/**
+ * ges_timeline_element_paste:
+ * @self: The #GESTimelineElement to paste
+ * @paste_position: The position in the timeline the element should
+ * be copied to, meaning it will become the start of @self
+ *
+ * Paste @self inside the timeline. @self must have been created
+ * using ges_timeline_element_copy with recurse=TRUE set,
+ * otherwise it will fail.
+ *
+ * Since: 1.6.0
+ */
+gboolean
+ges_timeline_element_paste (GESTimelineElement * self,
+ GstClockTime paste_position)
+{
+ gboolean res;
+ g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
+
+ if (!self->priv->copied_from) {
+ GST_ERROR_OBJECT (self, "Is not being 'deeply' copied!");
+
+ return FALSE;
+ }
+
+ if (!GES_TIMELINE_ELEMENT_GET_CLASS (self)->paste) {
+ GST_ERROR_OBJECT (self, "No paste vmethod implemented");
+
+ return FALSE;
+ }
+
+ res = GES_TIMELINE_ELEMENT_GET_CLASS (self)->paste (self,
+ self->priv->copied_from, paste_position);
+
+ g_clear_object (&self->priv->copied_from);
+
+ return res;
+}
gboolean (*roll_end) (GESTimelineElement *self, guint64 end);
gboolean (*trim) (GESTimelineElement *self, guint64 start);
void (*deep_copy) (GESTimelineElement *self, GESTimelineElement *copy);
+ gboolean (*paste) (GESTimelineElement *self, GESTimelineElement *ref_element,
+ GstClockTime paste_position);
GParamSpec** (*list_children_properties) (GESTimelineElement * self, guint *n_properties);
gboolean (*lookup_child) (GESTimelineElement *self, const gchar *prop_name,
gboolean ges_timeline_element_remove_child_property(GESTimelineElement * self,
GParamSpec *pspec);
+gboolean ges_timeline_element_paste (GESTimelineElement * self,
+ GstClockTime paste_position);
+
GESTrackType ges_timeline_element_get_track_types (GESTimelineElement * self);
G_END_DECLS
g_free (specs);
}
+static void
+_split_binding (GESTrackElement * element, GESTrackElement * new_element,
+ guint64 position, GstTimedValueControlSource * source,
+ GstTimedValueControlSource * new_source, gboolean absolute)
+{
+ GstTimedValue *last_value = NULL;
+ gboolean past_position = FALSE;
+ GList *values, *tmp;
+
+ values =
+ gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source));
+
+ for (tmp = values; tmp; tmp = tmp->next) {
+ GstTimedValue *value = tmp->data;
+ if (value->timestamp > position) {
+ gfloat value_at_pos;
+
+ /* FIXME We should be able to use gst_control_source_get_value so
+ * all modes are handled. Right now that method only works if the value
+ * we are looking for is between two actual keyframes which is not enough
+ * in our case. bug #706621 */
+ value_at_pos =
+ interpolate_values_for_position (last_value, value, position,
+ absolute);
+
+ past_position = TRUE;
+
+ gst_timed_value_control_source_set (new_source, position, value_at_pos);
+ gst_timed_value_control_source_set (new_source, value->timestamp,
+ value->value);
+ gst_timed_value_control_source_unset (source, value->timestamp);
+ gst_timed_value_control_source_set (source, position, value_at_pos);
+ } else if (past_position) {
+ gst_timed_value_control_source_unset (source, value->timestamp);
+ gst_timed_value_control_source_set (new_source, value->timestamp,
+ value->value);
+ }
+ last_value = value;
+ }
+}
+
+static void
+_copy_binding (GESTrackElement * element, GESTrackElement * new_element,
+ guint64 position, GstTimedValueControlSource * source,
+ GstTimedValueControlSource * new_source, gboolean absolute)
+{
+ GList *values, *tmp;
+
+ values =
+ gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
+ (source));
+ for (tmp = values; tmp; tmp = tmp->next) {
+ GstTimedValue *value = tmp->data;
+
+ gst_timed_value_control_source_set (new_source, value->timestamp,
+ value->value);
+ }
+}
+
+/* position == GST_CLOCK_TIME_NONE means that we do a simple copy
+ * other position means that the function will do a splitting
+ * and thus interpollate the values in the element and new_element
+ */
void
-ges_track_element_split_bindings (GESTrackElement * element,
+ges_track_element_copy_bindings (GESTrackElement * element,
GESTrackElement * new_element, guint64 position)
{
GParamSpec **specs;
guint n, n_specs;
+ gboolean absolute;
GstControlBinding *binding;
GstTimedValueControlSource *source, *new_source;
ges_track_element_list_children_properties (GES_TRACK_ELEMENT (element),
&n_specs);
for (n = 0; n < n_specs; ++n) {
- GList *values, *tmp;
- GstTimedValue *last_value = NULL;
- gboolean past_position = FALSE, absolute;
GstInterpolationMode mode;
binding = ges_track_element_get_control_binding (element, specs[n]->name);
if (!binding)
continue;
- g_object_get (binding, "control_source", &source, NULL);
-
/* FIXME : this should work as well with other types of control sources */
+ g_object_get (binding, "control_source", &source, NULL);
if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source))
continue;
g_object_get (binding, "absolute", &absolute, NULL);
+ g_object_get (source, "mode", &mode, NULL);
new_source =
GST_TIMED_VALUE_CONTROL_SOURCE (gst_interpolation_control_source_new
());
-
- g_object_get (source, "mode", &mode, NULL);
g_object_set (new_source, "mode", mode, NULL);
- values =
- gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
- (source));
- for (tmp = values; tmp; tmp = tmp->next) {
- GstTimedValue *value = tmp->data;
- if (value->timestamp > position) {
- gfloat value_at_pos;
-
- /* FIXME We should be able to use gst_control_source_get_value so
- * all modes are handled. Right now that method only works if the value
- * we are looking for is between two actual keyframes which is not enough
- * in our case. bug #706621 */
- value_at_pos =
- interpolate_values_for_position (last_value, value, position,
- absolute);
-
- past_position = TRUE;
-
- gst_timed_value_control_source_set (new_source, position, value_at_pos);
- gst_timed_value_control_source_set (new_source, value->timestamp,
- value->value);
- gst_timed_value_control_source_unset (source, value->timestamp);
- gst_timed_value_control_source_set (source, position, value_at_pos);
- } else if (past_position) {
- gst_timed_value_control_source_unset (source, value->timestamp);
- gst_timed_value_control_source_set (new_source, value->timestamp,
- value->value);
- }
- last_value = value;
- }
+ if (GST_CLOCK_TIME_IS_VALID (position))
+ _split_binding (element, new_element, position, source, new_source,
+ absolute);
+ else
+ _copy_binding (element, new_element, position, source, new_source,
+ absolute);
/* We only manage direct (absolute) bindings, see TODO in set_control_source */
if (absolute)