Timeline: Implement remove_track, remove_layer, and _layer_object_removed_cb
[platform/upstream/gstreamer.git] / ges / ges-timeline.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "gesmarshal.h"
21 #include "ges-internal.h"
22 #include "ges-timeline.h"
23 #include "ges-track.h"
24 #include "ges-timeline-layer.h"
25 #include "ges.h"
26
27 /**
28  * GESTimelinePipeline
29  *
30  * Top-level container for pipelines
31  * 
32  * Contains a list of TimelineLayer which users should use to arrange the
33  * various timeline objects.
34  *
35  */
36
37 G_DEFINE_TYPE (GESTimeline, ges_timeline, GST_TYPE_BIN);
38
39
40 enum
41 {
42   TRACK_ADDED,
43   TRACK_REMOVED,
44   LAYER_ADDED,
45   LAYER_REMOVED,
46   LAST_SIGNAL
47 };
48
49 static guint ges_timeline_signals[LAST_SIGNAL] = { 0 };
50
51 static void
52 ges_timeline_get_property (GObject * object, guint property_id,
53     GValue * value, GParamSpec * pspec)
54 {
55   switch (property_id) {
56     default:
57       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
58   }
59 }
60
61 static void
62 ges_timeline_set_property (GObject * object, guint property_id,
63     const GValue * value, GParamSpec * pspec)
64 {
65   switch (property_id) {
66     default:
67       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
68   }
69 }
70
71 static void
72 ges_timeline_dispose (GObject * object)
73 {
74   G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
75 }
76
77 static void
78 ges_timeline_finalize (GObject * object)
79 {
80   G_OBJECT_CLASS (ges_timeline_parent_class)->finalize (object);
81 }
82
83 static void
84 ges_timeline_class_init (GESTimelineClass * klass)
85 {
86   GObjectClass *object_class = G_OBJECT_CLASS (klass);
87
88   object_class->get_property = ges_timeline_get_property;
89   object_class->set_property = ges_timeline_set_property;
90   object_class->dispose = ges_timeline_dispose;
91   object_class->finalize = ges_timeline_finalize;
92
93   /* Signals
94    * 'track-added'
95    * 'track-removed'
96    * 'layer-added'
97    * 'layer-removed'
98    */
99
100   ges_timeline_signals[TRACK_ADDED] =
101       g_signal_new ("track-added", G_TYPE_FROM_CLASS (klass),
102       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_added), NULL,
103       NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GES_TYPE_TRACK);
104
105   ges_timeline_signals[TRACK_REMOVED] =
106       g_signal_new ("track-removed", G_TYPE_FROM_CLASS (klass),
107       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_removed),
108       NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GES_TYPE_TRACK);
109
110   ges_timeline_signals[LAYER_ADDED] =
111       g_signal_new ("layer-added", G_TYPE_FROM_CLASS (klass),
112       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_added), NULL,
113       NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GES_TYPE_TIMELINE_LAYER);
114
115   ges_timeline_signals[LAYER_REMOVED] =
116       g_signal_new ("layer-removed", G_TYPE_FROM_CLASS (klass),
117       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_removed),
118       NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
119       GES_TYPE_TIMELINE_LAYER);
120 }
121
122 static void
123 ges_timeline_init (GESTimeline * self)
124 {
125   self->layers = NULL;
126   self->tracks = NULL;
127 }
128
129 GESTimeline *
130 ges_timeline_new (void)
131 {
132   return g_object_new (GES_TYPE_TIMELINE, NULL);
133 }
134
135 GESTimeline *
136 ges_timeline_load_from_uri (gchar * uri)
137 {
138   /* FIXME : IMPLEMENT */
139   return NULL;
140 }
141
142 gboolean
143 ges_timeline_save (GESTimeline * timeline, gchar * uri)
144 {
145   /* FIXME : IMPLEMENT */
146   return FALSE;
147 }
148
149 static void
150 layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
151     GESTimeline * timeline)
152 {
153   GList *tmp;
154
155   GST_DEBUG ("New TimelineObject %p added to layer %p", object, layer);
156
157   for (tmp = timeline->tracks; tmp; tmp = g_list_next (tmp)) {
158     GESTrack *track = (GESTrack *) tmp->data;
159     GESTrackObject *trobj;
160
161     GST_LOG ("Trying with track %p", track);
162
163     if (G_UNLIKELY (!(trobj =
164                 ges_timeline_object_create_track_object (object, track)))) {
165       GST_WARNING ("Couldn't create TrackObject for TimelineObject");
166       continue;
167     }
168
169     GST_LOG ("Got new TrackObject %p, adding it to track", trobj);
170     ges_track_add_object (track, trobj);
171   }
172
173   GST_DEBUG ("done");
174 }
175
176
177 static void
178 layer_object_removed_cb (GESTimelineLayer * layer, GESTimelineObject * object,
179     GESTimeline * timeline)
180 {
181   GList *tmp;
182
183   GST_DEBUG ("TimelineObject %p removed from layer %p", object, layer);
184
185   /* Go over the object's track objects and figure out which one belongs to
186    * the list of tracks we control */
187
188   for (tmp = object->trackobjects; tmp; tmp = g_list_next (tmp)) {
189     GESTrackObject *trobj = (GESTrackObject *) tmp->data;
190
191     GST_DEBUG ("Trying to remove TrackObject %p", trobj);
192
193     if (G_LIKELY (g_list_find (timeline->tracks, trobj->track))) {
194       GST_DEBUG ("Belongs to one of the tracks we control");
195       ges_track_remove_object (trobj->track, trobj);
196
197       ges_timeline_object_release_track_object (object, trobj);
198     }
199   }
200
201   GST_DEBUG ("Done");
202 }
203
204
205 gboolean
206 ges_timeline_add_layer (GESTimeline * timeline, GESTimelineLayer * layer)
207 {
208   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
209
210   /* We can only add a layer that doesn't already belong to another timeline */
211   if (G_UNLIKELY (layer->timeline)) {
212     GST_WARNING ("Layer belongs to another timeline, can't add it");
213     return FALSE;
214   }
215
216   /* Add to the list of layers, make sure we don't already control it */
217   if (G_UNLIKELY (g_list_find (timeline->layers, (gconstpointer) layer))) {
218     GST_WARNING ("Layer is already controlled by this timeline");
219     return FALSE;
220   }
221
222   /* Reference is taken */
223   timeline->layers = g_list_append (timeline->layers, g_object_ref (layer));
224
225   /* Inform the layer that it belongs to a new timeline */
226   ges_timeline_layer_set_timeline (layer, timeline);
227
228   /* FIXME : GO OVER THE LIST OF ALREADY EXISTING TIMELINE OBJECTS IN THAT
229    * LAYER AND ADD THEM !!! */
230
231   /* Connect to 'object-added'/'object-removed' signal from the new layer */
232   g_signal_connect (layer, "object-added", G_CALLBACK (layer_object_added_cb),
233       timeline);
234   g_signal_connect (layer, "object-removed",
235       G_CALLBACK (layer_object_removed_cb), timeline);
236
237   GST_DEBUG ("Done adding layer, emitting 'layer-added' signal");
238   g_signal_emit (timeline, ges_timeline_signals[LAYER_ADDED], 0, layer);
239
240   return TRUE;
241 }
242
243 gboolean
244 ges_timeline_remove_layer (GESTimeline * timeline, GESTimelineLayer * layer)
245 {
246   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
247
248   if (G_UNLIKELY (!g_list_find (timeline->layers, layer))) {
249     GST_WARNING ("Layer doesn't belong to this timeline");
250     return FALSE;
251   }
252
253   /* Disconnect signals */
254   GST_DEBUG ("Disconnecting signal callbacks");
255   g_signal_handlers_disconnect_by_func (layer, layer_object_added_cb, timeline);
256   g_signal_handlers_disconnect_by_func (layer, layer_object_removed_cb,
257       timeline);
258
259   timeline->layers = g_list_remove (timeline->layers, layer);
260
261   ges_timeline_layer_set_timeline (layer, NULL);
262
263   g_signal_emit (timeline, ges_timeline_signals[LAYER_REMOVED], 0, layer);
264
265   g_object_unref (layer);
266
267   return TRUE;
268 }
269
270 gboolean
271 ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
272 {
273   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
274
275   /* make sure we don't already control it */
276   if (G_UNLIKELY (g_list_find (timeline->tracks, (gconstpointer) track))) {
277     GST_WARNING ("Track is already controlled by this timeline");
278     return FALSE;
279   }
280
281   /* Add the track to ourself (as a GstBin) 
282    * Reference is taken ! */
283   if (G_UNLIKELY (!gst_bin_add (GST_BIN (timeline), GST_ELEMENT (track)))) {
284     GST_WARNING ("Couldn't add track to ourself (GST)");
285     return FALSE;
286   }
287
288   /* Add the track to the list of tracks we track */
289   timeline->tracks = g_list_append (timeline->tracks, track);
290
291   /* Inform the track that it's currently being used by ourself */
292   ges_track_set_timeline (track, timeline);
293
294   GST_DEBUG ("Done adding track, emitting 'track-added' signal");
295
296   /* emit 'track-added' */
297   g_signal_emit (timeline, ges_timeline_signals[TRACK_ADDED], 0, track);
298
299   return TRUE;
300 }
301
302 gboolean
303 ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track)
304 {
305   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
306
307   if (G_UNLIKELY (!g_list_find (timeline->tracks, track))) {
308     GST_WARNING ("Track doesn't belong to this timeline");
309     return FALSE;
310   }
311
312   timeline->tracks = g_list_remove (timeline->tracks, track);
313
314   ges_track_set_timeline (track, NULL);
315
316   /* remove track from our bin */
317   if (G_UNLIKELY (!gst_bin_remove (GST_BIN (timeline), GST_ELEMENT (track)))) {
318     GST_WARNING ("Couldn't remove track to ourself (GST)");
319     return FALSE;
320   }
321
322   /* Signal track removal to all layers/objects */
323   g_signal_emit (timeline, ges_timeline_signals[TRACK_REMOVED], 0, track);
324
325   return TRUE;
326 }