Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-clone.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2008  Intel Corporation.
7  *
8  * Authored By: Robert Bragg <robert@linux.intel.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22  */
23
24 /**
25  * SECTION:clutter-clone
26  * @short_description: An actor that displays a clone of a source actor
27  *
28  * #ClutterClone is a #ClutterActor which draws with the paint
29  * function of another actor, scaled to fit its own allocation.
30  *
31  * #ClutterClone can be used to efficiently clone any other actor.
32  *
33  * <note><para>This is different from clutter_texture_new_from_actor()
34  * which requires support for FBOs in the underlying GL
35  * implementation.</para></note>
36  *
37  * #ClutterClone is available since Clutter 1.0
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include "clutter-actor-private.h"
45 #include "clutter-clone.h"
46 #include "clutter-debug.h"
47 #include "clutter-main.h"
48 #include "clutter-paint-volume-private.h"
49 #include "clutter-private.h"
50
51 #include "cogl/cogl.h"
52
53 G_DEFINE_TYPE (ClutterClone, clutter_clone, CLUTTER_TYPE_ACTOR);
54
55 enum
56 {
57   PROP_0,
58
59   PROP_SOURCE,
60
61   PROP_LAST
62 };
63
64 static GParamSpec *obj_props[PROP_LAST];
65
66 #define CLUTTER_CLONE_GET_PRIVATE(obj)  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_CLONE, ClutterClonePrivate))
67
68 struct _ClutterClonePrivate
69 {
70   ClutterActor *clone_source;
71 };
72
73 static void clutter_clone_set_source_internal (ClutterClone *clone,
74                                                ClutterActor *source);
75 static void
76 clutter_clone_get_preferred_width (ClutterActor *self,
77                                    gfloat        for_height,
78                                    gfloat       *min_width_p,
79                                    gfloat       *natural_width_p)
80 {
81   ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
82   ClutterActor *clone_source = priv->clone_source;
83
84   if (clone_source == NULL)
85     {
86       if (min_width_p)
87         *min_width_p = 0;
88
89       if (natural_width_p)
90         *natural_width_p = 0;
91     }
92   else
93     clutter_actor_get_preferred_width (clone_source,
94                                        for_height,
95                                        min_width_p,
96                                        natural_width_p);
97 }
98
99 static void
100 clutter_clone_get_preferred_height (ClutterActor *self,
101                                     gfloat        for_width,
102                                     gfloat       *min_height_p,
103                                     gfloat       *natural_height_p)
104 {
105   ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
106   ClutterActor *clone_source = priv->clone_source;
107
108   if (clone_source == NULL)
109     {
110       if (min_height_p)
111         *min_height_p = 0;
112
113       if (natural_height_p)
114         *natural_height_p = 0;
115     }
116   else
117     clutter_actor_get_preferred_height (clone_source,
118                                         for_width,
119                                         min_height_p,
120                                         natural_height_p);
121 }
122
123 static void
124 clutter_clone_apply_transform (ClutterActor *self, CoglMatrix *matrix)
125 {
126   ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
127   ClutterGeometry geom;
128   ClutterGeometry source_geom;
129   gfloat x_scale;
130   gfloat y_scale;
131
132   /* First chain up and apply all the standard ClutterActor
133    * transformations... */
134   CLUTTER_ACTOR_CLASS (clutter_clone_parent_class)->apply_transform (self,
135                                                                      matrix);
136
137   /* if we don't have a source, nothing else to do */
138   if (priv->clone_source == NULL)
139     return;
140
141   /* get our allocated size */
142   clutter_actor_get_allocation_geometry (self, &geom);
143
144   /* and get the allocated size of the source */
145   clutter_actor_get_allocation_geometry (priv->clone_source, &source_geom);
146
147   /* We need to scale what the clone-source actor paints to fill our own
148    * allocation...
149    */
150   x_scale = (gfloat) geom.width  / source_geom.width;
151   y_scale = (gfloat) geom.height / source_geom.height;
152
153   cogl_matrix_scale (matrix, x_scale, y_scale, x_scale);
154 }
155
156 static void
157 clutter_clone_paint (ClutterActor *actor)
158 {
159   ClutterClone *self = CLUTTER_CLONE (actor);
160   ClutterClonePrivate *priv = self->priv;
161   gboolean was_unmapped = FALSE;
162
163   if (priv->clone_source == NULL)
164     return;
165
166   CLUTTER_NOTE (PAINT, "painting clone actor '%s'",
167                 _clutter_actor_get_debug_name (actor));
168
169   /* The final bits of magic:
170    * - We need to override the paint opacity of the actor with our own
171    *   opacity.
172    * - We need to inform the actor that it's in a clone paint (for the function
173    *   clutter_actor_is_in_clone_paint())
174    * - We need to stop clutter_actor_paint applying the model view matrix of
175    *   the clone source actor.
176    */
177   _clutter_actor_set_in_clone_paint (priv->clone_source, TRUE);
178   _clutter_actor_set_opacity_override (priv->clone_source,
179                                        clutter_actor_get_paint_opacity (actor));
180   _clutter_actor_set_enable_model_view_transform (priv->clone_source, FALSE);
181
182   if (!CLUTTER_ACTOR_IS_MAPPED (priv->clone_source))
183     {
184       _clutter_actor_set_enable_paint_unmapped (priv->clone_source, TRUE);
185       was_unmapped = TRUE;
186     }
187
188   _clutter_actor_push_clone_paint ();
189   clutter_actor_paint (priv->clone_source);
190   _clutter_actor_pop_clone_paint ();
191
192   if (was_unmapped)
193     _clutter_actor_set_enable_paint_unmapped (priv->clone_source, FALSE);
194
195   _clutter_actor_set_enable_model_view_transform (priv->clone_source, TRUE);
196   _clutter_actor_set_opacity_override (priv->clone_source, -1);
197   _clutter_actor_set_in_clone_paint (priv->clone_source, FALSE);
198 }
199
200 static gboolean
201 clutter_clone_get_paint_volume (ClutterActor       *actor,
202                                 ClutterPaintVolume *volume)
203 {
204   ClutterClonePrivate *priv = CLUTTER_CLONE (actor)->priv;
205   const ClutterPaintVolume *source_volume;
206
207   /* if the source is not set the paint volume is defined to be empty */
208   if (priv->clone_source == NULL)
209     return TRUE;
210
211   /* query the volume of the source actor and simply masquarade it as
212    * the clones volume... */
213   source_volume = clutter_actor_get_paint_volume (priv->clone_source);
214   if (source_volume == NULL)
215     return FALSE;
216
217   _clutter_paint_volume_set_from_volume (volume, source_volume);
218   _clutter_paint_volume_set_reference_actor (volume, actor);
219
220   return TRUE;
221 }
222
223 static gboolean
224 clutter_clone_has_overlaps (ClutterActor *actor)
225 {
226   ClutterClonePrivate *priv = CLUTTER_CLONE (actor)->priv;
227
228   /* The clone has overlaps iff the source has overlaps */
229
230   if (priv->clone_source == NULL)
231     return FALSE;
232
233   return clutter_actor_has_overlaps (priv->clone_source);
234 }
235
236 static void
237 clutter_clone_allocate (ClutterActor           *self,
238                         const ClutterActorBox  *box,
239                         ClutterAllocationFlags  flags)
240 {
241   ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
242   ClutterActorClass *parent_class;
243
244   /* chain up */
245   parent_class = CLUTTER_ACTOR_CLASS (clutter_clone_parent_class);
246   parent_class->allocate (self, box, flags);
247
248   if (priv->clone_source == NULL)
249     return;
250
251 #if 0
252   /* XXX - this is wrong: ClutterClone cannot clone unparented
253    * actors, as it will break all invariants
254    */
255
256   /* we act like a "foster parent" for the source we are cloning;
257    * if the source has not been parented we have to force an
258    * allocation on it, so that we can paint it correctly from
259    * within our paint() implementation. since the actor does not
260    * have a parent, and thus it won't be painted by the usual
261    * paint cycle, we can safely give it as much size as it requires
262    */
263   if (clutter_actor_get_parent (priv->clone_source) == NULL)
264     clutter_actor_allocate_preferred_size (priv->clone_source, flags);
265 #endif
266 }
267
268 static void
269 clutter_clone_set_property (GObject      *gobject,
270                             guint         prop_id,
271                             const GValue *value,
272                             GParamSpec   *pspec)
273 {
274   ClutterClone *self = CLUTTER_CLONE (gobject);
275
276   switch (prop_id)
277     {
278     case PROP_SOURCE:
279       clutter_clone_set_source (self, g_value_get_object (value));
280       break;
281
282     default:
283       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
284       break;
285   }
286 }
287
288 static void
289 clutter_clone_get_property (GObject    *gobject,
290                             guint       prop_id,
291                             GValue     *value,
292                             GParamSpec *pspec)
293 {
294   ClutterClonePrivate *priv = CLUTTER_CLONE (gobject)->priv;
295
296   switch (prop_id)
297     {
298     case PROP_SOURCE:
299       g_value_set_object (value, priv->clone_source);
300       break;
301
302     default:
303       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
304       break;
305     }
306 }
307
308 static void
309 clutter_clone_dispose (GObject *gobject)
310 {
311   clutter_clone_set_source_internal (CLUTTER_CLONE (gobject), NULL);
312
313   G_OBJECT_CLASS (clutter_clone_parent_class)->dispose (gobject);
314 }
315
316 static void
317 clutter_clone_class_init (ClutterCloneClass *klass)
318 {
319   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
320   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
321
322   g_type_class_add_private (gobject_class, sizeof (ClutterClonePrivate));
323
324   actor_class->apply_transform = clutter_clone_apply_transform;
325   actor_class->paint = clutter_clone_paint;
326   actor_class->get_paint_volume = clutter_clone_get_paint_volume;
327   actor_class->get_preferred_width = clutter_clone_get_preferred_width;
328   actor_class->get_preferred_height = clutter_clone_get_preferred_height;
329   actor_class->allocate = clutter_clone_allocate;
330   actor_class->has_overlaps = clutter_clone_has_overlaps;
331
332   gobject_class->dispose = clutter_clone_dispose;
333   gobject_class->set_property = clutter_clone_set_property;
334   gobject_class->get_property = clutter_clone_get_property;
335
336   /**
337    * ClutterClone:source:
338    *
339    * This property specifies the source actor being cloned.
340    *
341    * Since: 1.0
342    */
343   obj_props[PROP_SOURCE] =
344     g_param_spec_object ("source",
345                          P_("Source"),
346                          P_("Specifies the actor to be cloned"),
347                          CLUTTER_TYPE_ACTOR,
348                          G_PARAM_CONSTRUCT |
349                          CLUTTER_PARAM_READWRITE);
350
351   g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
352 }
353
354 static void
355 clutter_clone_init (ClutterClone *self)
356 {
357   ClutterClonePrivate *priv;
358
359   self->priv = priv = CLUTTER_CLONE_GET_PRIVATE (self);
360
361   priv->clone_source = NULL;
362 }
363
364 /**
365  * clutter_clone_new:
366  * @source: a #ClutterActor, or %NULL
367  *
368  * Creates a new #ClutterActor which clones @source/
369  *
370  * Return value: the newly created #ClutterClone
371  *
372  * Since: 1.0
373  */
374 ClutterActor *
375 clutter_clone_new (ClutterActor *source)
376 {
377   return g_object_new (CLUTTER_TYPE_CLONE, "source", source,  NULL);
378 }
379
380 static void
381 clone_source_queue_redraw_cb (ClutterActor *source,
382                               ClutterActor *origin,
383                               ClutterClone *self)
384 {
385   clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
386 }
387
388 static void
389 clone_source_queue_relayout_cb (ClutterActor *source,
390                                 ClutterClone *self)
391 {
392   clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
393 }
394
395 static void
396 clutter_clone_set_source_internal (ClutterClone *self,
397                                    ClutterActor *source)
398 {
399   ClutterClonePrivate *priv = self->priv;
400
401   if (priv->clone_source != NULL)
402     {
403       g_signal_handlers_disconnect_by_func (priv->clone_source,
404                                             G_CALLBACK (clone_source_queue_redraw_cb),
405                                             self);
406       g_signal_handlers_disconnect_by_func (priv->clone_source,
407                                             G_CALLBACK (clone_source_queue_relayout_cb),
408                                             self);
409       g_object_unref (priv->clone_source);
410       priv->clone_source = NULL;
411     }
412
413   if (source != NULL)
414     {
415       priv->clone_source = g_object_ref (source);
416       g_signal_connect (priv->clone_source, "queue-redraw",
417                         G_CALLBACK (clone_source_queue_redraw_cb), self);
418       g_signal_connect (priv->clone_source, "queue-relayout",
419                         G_CALLBACK (clone_source_queue_relayout_cb), self);
420     }
421
422   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SOURCE]);
423
424   clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
425 }
426
427 /**
428  * clutter_clone_set_source:
429  * @self: a #ClutterClone
430  * @source: (allow-none): a #ClutterActor, or %NULL
431  *
432  * Sets @source as the source actor to be cloned by @self.
433  *
434  * Since: 1.0
435  */
436 void
437 clutter_clone_set_source (ClutterClone *self,
438                           ClutterActor *source)
439 {
440   g_return_if_fail (CLUTTER_IS_CLONE (self));
441   g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source));
442
443   clutter_clone_set_source_internal (self, source);
444   clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
445 }
446
447 /**
448  * clutter_clone_get_source:
449  * @self: a #ClutterClone
450  *
451  * Retrieves the source #ClutterActor being cloned by @self.
452  *
453  * Return value: (transfer none): the actor source for the clone
454  *
455  * Since: 1.0
456  */
457 ClutterActor *
458 clutter_clone_get_source (ClutterClone *self)
459 {
460   g_return_val_if_fail (CLUTTER_IS_CLONE (self), NULL);
461
462   return self->priv->clone_source;
463 }