GESTimelineLayer: Add a 'priority' property
[platform/upstream/gstreamer.git] / ges / ges-timeline-layer.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
3  *               2009 Nokia Corporation
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:ges-timeline-layer
23  * @short_description: Non-overlaping sequence of #GESTimelineObject
24  *
25  * Responsible for the ordering of the various contained TimelineObject(s)
26  */
27
28 #include "ges-internal.h"
29 #include "gesmarshal.h"
30 #include "ges-timeline-layer.h"
31 #include "ges.h"
32
33 G_DEFINE_TYPE (GESTimelineLayer, ges_timeline_layer, G_TYPE_OBJECT);
34
35 enum
36 {
37   PROP_0,
38   PROP_PRIORITY,
39 };
40
41 enum
42 {
43   OBJECT_ADDED,
44   OBJECT_REMOVED,
45   LAST_SIGNAL
46 };
47
48 static guint ges_timeline_layer_signals[LAST_SIGNAL] = { 0 };
49
50 static void
51 ges_timeline_layer_get_property (GObject * object, guint property_id,
52     GValue * value, GParamSpec * pspec)
53 {
54   GESTimelineLayer *layer = GES_TIMELINE_LAYER (object);
55
56   switch (property_id) {
57     case PROP_PRIORITY:
58       g_value_set_uint (value, layer->priority);
59       break;
60     default:
61       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
62   }
63 }
64
65 static void
66 ges_timeline_layer_set_property (GObject * object, guint property_id,
67     const GValue * value, GParamSpec * pspec)
68 {
69   GESTimelineLayer *layer = GES_TIMELINE_LAYER (object);
70
71   switch (property_id) {
72     case PROP_PRIORITY:
73       ges_timeline_layer_set_priority (layer, g_value_get_uint (value));
74       break;
75     default:
76       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
77   }
78 }
79
80 static void
81 ges_timeline_layer_dispose (GObject * object)
82 {
83   GESTimelineLayer *layer = GES_TIMELINE_LAYER (object);
84
85   if (layer->objects_start) {
86     g_slist_foreach (layer->objects_start, (GFunc) g_object_unref, NULL);
87     g_slist_free (layer->objects_start);
88     layer->objects_start = NULL;
89   }
90   G_OBJECT_CLASS (ges_timeline_layer_parent_class)->dispose (object);
91 }
92
93 static void
94 ges_timeline_layer_finalize (GObject * object)
95 {
96   G_OBJECT_CLASS (ges_timeline_layer_parent_class)->finalize (object);
97 }
98
99 static void
100 ges_timeline_layer_class_init (GESTimelineLayerClass * klass)
101 {
102   GObjectClass *object_class = G_OBJECT_CLASS (klass);
103
104   object_class->get_property = ges_timeline_layer_get_property;
105   object_class->set_property = ges_timeline_layer_set_property;
106   object_class->dispose = ges_timeline_layer_dispose;
107   object_class->finalize = ges_timeline_layer_finalize;
108
109   /**
110    * GESTimelineLayer:priority
111    *
112    * The priority of the layer in the #GESTimeline. 0 is the highest
113    * priority.
114    */
115   g_object_class_install_property (object_class, PROP_PRIORITY,
116       g_param_spec_uint ("priority", "Priority",
117           "The priority of the layer", 0, G_MAXUINT, 0, G_PARAM_READWRITE));
118
119   /**
120    * GESTimelineLayer::object-added
121    * @layer: the #GESTimelineLayer
122    * @object: the #GESTimelineObject that was added.
123    *
124    * Will be emitted after the object was added to the layer.
125    */
126   ges_timeline_layer_signals[OBJECT_ADDED] =
127       g_signal_new ("object-added", G_TYPE_FROM_CLASS (klass),
128       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineLayerClass, object_added),
129       NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
130       GES_TYPE_TIMELINE_OBJECT);
131
132   /**
133    * GESTimelineLayer::object-removed
134    * @layer: the #GESTimelineLayer
135    * @object: the #GESTimelineObject that was removed
136    *
137    * Will be emitted after the object was removed from the layer.
138    */
139   ges_timeline_layer_signals[OBJECT_REMOVED] =
140       g_signal_new ("object-removed", G_TYPE_FROM_CLASS (klass),
141       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineLayerClass,
142           object_removed), NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
143       GES_TYPE_TIMELINE_OBJECT);
144
145 }
146
147 static void
148 ges_timeline_layer_init (GESTimelineLayer * self)
149 {
150   /* TODO : Keep those 3 values in sync */
151   self->priority = 0;
152   self->min_gnl_priority = 0;
153   self->max_gnl_priority = 9;
154 }
155
156 /**
157  * ges_timeline_layer_new:
158  *
159  * Creates a new #GESTimelineLayer.
160  *
161  * Returns: A new #GESTimelineLayer
162  */
163 GESTimelineLayer *
164 ges_timeline_layer_new (void)
165 {
166   return g_object_new (GES_TYPE_TIMELINE_LAYER, NULL);
167 }
168
169 void
170 ges_timeline_layer_set_timeline (GESTimelineLayer * layer,
171     GESTimeline * timeline)
172 {
173   GST_DEBUG ("layer:%p, timeline:%p", layer, timeline);
174
175   layer->timeline = timeline;
176 }
177
178 static gint
179 objects_start_compare (GESTimelineObject * a, GESTimelineObject * b)
180 {
181   if (a->start == b->start) {
182     if (a->priority < b->priority)
183       return -1;
184     if (a->priority > b->priority)
185       return 1;
186     return 0;
187   }
188   if (a->start < b->start)
189     return -1;
190   if (a->start > b->start)
191     return 1;
192   return 0;
193 }
194
195 /**
196  * ges_timeline_layer_add_object:
197  * @layer: a #GESTimelineLayer
198  * @object: the #GESTimelineObject to add.
199  *
200  * Adds the object to the layer. The layer will steal a reference to the
201  * provided object.
202  *
203  * Returns: TRUE if the object was properly added to the layer, or FALSE
204  * if the @layer refused to add the object.
205  */
206
207 gboolean
208 ges_timeline_layer_add_object (GESTimelineLayer * layer,
209     GESTimelineObject * object)
210 {
211   GST_DEBUG ("layer:%p, object:%p", layer, object);
212
213   if (G_UNLIKELY (object->layer)) {
214     GST_WARNING ("TimelineObject %p already belongs to another layer");
215     return FALSE;
216   }
217
218   /* Take a reference to the object and store it stored by start/priority */
219   layer->objects_start =
220       g_slist_insert_sorted (layer->objects_start, object,
221       (GCompareFunc) objects_start_compare);
222
223   /* Inform the object it's now in this layer */
224   ges_timeline_object_set_layer (object, layer);
225
226   /* Set the priority. */
227   ges_timeline_object_set_priority (object, layer->min_gnl_priority);
228
229   /* emit 'object-added' */
230   g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_ADDED], 0, object);
231
232   return TRUE;
233 }
234
235 /**
236  * ges_timeline_layer_remove_object:
237  * @layer: a #GESTimelineLayer
238  * @object: the #GESTimelineObject to remove
239  *
240  * Removes the given @object from the @layer. The reference stolen by the @layer
241  * when the object was added will be removed. If you wish to use the object after
242  * this function, make sure you take an extra reference to the object before
243  * calling this function.
244  *
245  * Returns: TRUE if the object was properly remove, else FALSE.
246  */
247 gboolean
248 ges_timeline_layer_remove_object (GESTimelineLayer * layer,
249     GESTimelineObject * object)
250 {
251   GST_DEBUG ("layer:%p, object:%p", layer, object);
252
253   if (G_UNLIKELY (object->layer != layer)) {
254     GST_WARNING ("TimelineObject doesn't belong to this layer");
255     return FALSE;
256   }
257
258   /* emit 'object-removed' */
259   g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_REMOVED], 0, object);
260
261   /* inform the object it's no longer in a layer */
262   ges_timeline_object_set_layer (object, NULL);
263
264   /* Remove it from our list of controlled objects */
265   layer->objects_start = g_slist_remove (layer->objects_start, object);
266
267   /* Remove our reference to the object */
268   g_object_unref (object);
269
270   return TRUE;
271 }
272
273 /**
274  * ges_timeline_layer_resync_priorities:
275  * @layer: a #GESTimelineLayer
276  *
277  * Resyncs the priorities of the objects controlled by @layer.
278  * This method */
279 gboolean
280 ges_timeline_layer_resync_priorities (GESTimelineLayer * layer)
281 {
282   GSList *tmp;
283
284   /* TODO : Inhibit composition updates while doing this.
285    * Ideally we want to do it from an even higher level, but here will
286    * do in the meantime. */
287
288   /* TODO : This is the dumb version where we put everything linearly,
289    * will need to be adjusted for more complex usages (like with
290    * transitions).  */
291   for (tmp = layer->objects_start; tmp; tmp = tmp->next) {
292     ges_timeline_object_set_priority ((GESTimelineObject *) tmp->data,
293         layer->min_gnl_priority);
294   }
295
296   return TRUE;
297 }
298
299 void
300 ges_timeline_layer_set_priority (GESTimelineLayer * layer, guint priority)
301 {
302   GST_DEBUG ("layer:%p, priority:%d", layer, priority);
303
304   if (priority != layer->priority) {
305     layer->priority = priority;
306     layer->min_gnl_priority = (priority * 10);
307     layer->max_gnl_priority = ((priority + 1) * 10) - 1;
308
309     /* FIXME : Update controlled object's gnl priority accordingly */
310     ges_timeline_layer_resync_priorities (layer);
311   }
312 }