element: Implement a paste method
authorThibault Saunier <tsaunier@gnome.org>
Mon, 29 Jun 2015 16:04:32 +0000 (18:04 +0200)
committerThibault Saunier <tsaunier@gnome.org>
Fri, 3 Jul 2015 12:06:54 +0000 (14:06 +0200)
Allowing user to copy paste clips very easily

docs/libs/ges-sections.txt
ges/ges-clip.c
ges/ges-container.c
ges/ges-group.c
ges/ges-internal.h
ges/ges-timeline-element.c
ges/ges-timeline-element.h
ges/ges-track-element.c

index 68d0b0a..b54d1da 100644 (file)
@@ -454,6 +454,7 @@ ges_timeline_element_roll_end
 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
index 9dd935a..66a74b2 100644 (file)
@@ -625,6 +625,44 @@ _edit (GESContainer * container, GList * layers,
   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;
+}
 
 
 /****************************************************
@@ -715,7 +753,7 @@ ges_clip_class_init (GESClipClass * klass)
   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;
@@ -1245,7 +1283,7 @@ ges_clip_split (GESClip * clip, guint64 position)
     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);
   }
 
index 03a8276..016b6dd 100644 (file)
@@ -283,6 +283,31 @@ _get_track_types (GESTimelineElement * object)
   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 *
@@ -382,6 +407,7 @@ ges_container_class_init (GESContainerClass * klass)
   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;
index d0f70a4..abd0102 100644 (file)
@@ -598,6 +598,23 @@ _group (GList * containers)
   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;
+}
+
 
 /****************************************************
  *                                                  *
@@ -675,6 +692,7 @@ ges_group_class_init (GESGroupClass * klass)
   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.
index 7582078..d083d4f 100644 (file)
@@ -339,7 +339,7 @@ G_GNUC_INTERNAL guint32   _ges_track_element_get_layer_priority (GESTrackElement
 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);
 
index 059dbfe..2530561 100644 (file)
@@ -94,6 +94,8 @@ struct _GESTimelineElementPrivate
    * The hashtable should look like
    * {GParamaSpec ---> child}*/
   GHashTable *children_props;
+
+  GESTimelineElement *copied_from;
 };
 
 static gboolean
@@ -241,6 +243,14 @@ _set_property (GObject * object, guint property_id,
 }
 
 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);
@@ -387,6 +397,7 @@ ges_timeline_element_class_init (GESTimelineElementClass * klass)
       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;
@@ -1113,6 +1124,10 @@ ges_timeline_element_copy (GESTimelineElement * self, gboolean deep)
           " 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;
 }
 
@@ -1661,8 +1676,48 @@ ges_timeline_element_remove_child_property (GESTimelineElement * self,
 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;
+}
index 0e521f5..906b882 100644 (file)
@@ -186,6 +186,8 @@ struct _GESTimelineElementClass
   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,
@@ -277,6 +279,9 @@ gboolean ges_timeline_element_add_child_property   (GESTimelineElement * self,
 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
index 3ae4184..648131b 100644 (file)
@@ -1282,12 +1282,77 @@ ges_track_element_copy_properties (GESTimelineElement * element,
   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;
 
@@ -1295,60 +1360,31 @@ ges_track_element_split_bindings (GESTrackElement * element,
       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)