4 * An OpenGL based 'interactive canvas' library.
6 * Copyright (C) 2008 Intel Corporation.
8 * Authored By: Robert Bragg <robert@linux.intel.com>
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.
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.
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/>.
25 * SECTION:clutter-clone
26 * @short_description: An actor that displays a clone of a source actor
28 * #ClutterClone is a #ClutterActor which draws with the paint
29 * function of another actor, scaled to fit its own allocation.
31 * #ClutterClone can be used to efficiently clone any other actor.
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>
37 * #ClutterClone is available since Clutter 1.0
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"
51 #include "cogl/cogl.h"
53 G_DEFINE_TYPE (ClutterClone, clutter_clone, CLUTTER_TYPE_ACTOR);
64 static GParamSpec *obj_props[PROP_LAST];
66 #define CLUTTER_CLONE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_CLONE, ClutterClonePrivate))
68 struct _ClutterClonePrivate
70 ClutterActor *clone_source;
73 static void clutter_clone_set_source_internal (ClutterClone *clone,
74 ClutterActor *source);
76 clutter_clone_get_preferred_width (ClutterActor *self,
79 gfloat *natural_width_p)
81 ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
82 ClutterActor *clone_source = priv->clone_source;
84 if (clone_source == NULL)
93 clutter_actor_get_preferred_width (clone_source,
100 clutter_clone_get_preferred_height (ClutterActor *self,
102 gfloat *min_height_p,
103 gfloat *natural_height_p)
105 ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
106 ClutterActor *clone_source = priv->clone_source;
108 if (clone_source == NULL)
113 if (natural_height_p)
114 *natural_height_p = 0;
117 clutter_actor_get_preferred_height (clone_source,
124 clutter_clone_apply_transform (ClutterActor *self, CoglMatrix *matrix)
126 ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
127 ClutterGeometry geom;
128 ClutterGeometry source_geom;
132 /* First chain up and apply all the standard ClutterActor
133 * transformations... */
134 CLUTTER_ACTOR_CLASS (clutter_clone_parent_class)->apply_transform (self,
137 /* if we don't have a source, nothing else to do */
138 if (priv->clone_source == NULL)
141 /* get our allocated size */
142 clutter_actor_get_allocation_geometry (self, &geom);
144 /* and get the allocated size of the source */
145 clutter_actor_get_allocation_geometry (priv->clone_source, &source_geom);
147 /* We need to scale what the clone-source actor paints to fill our own
150 x_scale = (gfloat) geom.width / source_geom.width;
151 y_scale = (gfloat) geom.height / source_geom.height;
153 cogl_matrix_scale (matrix, x_scale, y_scale, x_scale);
157 clutter_clone_paint (ClutterActor *actor)
159 ClutterClone *self = CLUTTER_CLONE (actor);
160 ClutterClonePrivate *priv = self->priv;
161 gboolean was_unmapped = FALSE;
163 if (priv->clone_source == NULL)
166 CLUTTER_NOTE (PAINT, "painting clone actor '%s'",
167 _clutter_actor_get_debug_name (actor));
169 /* The final bits of magic:
170 * - We need to override the paint opacity of the actor with our own
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.
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);
182 if (!CLUTTER_ACTOR_IS_MAPPED (priv->clone_source))
184 _clutter_actor_set_enable_paint_unmapped (priv->clone_source, TRUE);
188 _clutter_actor_push_clone_paint ();
189 clutter_actor_paint (priv->clone_source);
190 _clutter_actor_pop_clone_paint ();
193 _clutter_actor_set_enable_paint_unmapped (priv->clone_source, FALSE);
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);
201 clutter_clone_get_paint_volume (ClutterActor *actor,
202 ClutterPaintVolume *volume)
204 ClutterClonePrivate *priv = CLUTTER_CLONE (actor)->priv;
205 const ClutterPaintVolume *source_volume;
207 /* if the source is not set the paint volume is defined to be empty */
208 if (priv->clone_source == NULL)
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)
217 _clutter_paint_volume_set_from_volume (volume, source_volume);
218 _clutter_paint_volume_set_reference_actor (volume, actor);
224 clutter_clone_has_overlaps (ClutterActor *actor)
226 ClutterClonePrivate *priv = CLUTTER_CLONE (actor)->priv;
228 /* The clone has overlaps iff the source has overlaps */
230 if (priv->clone_source == NULL)
233 return clutter_actor_has_overlaps (priv->clone_source);
237 clutter_clone_allocate (ClutterActor *self,
238 const ClutterActorBox *box,
239 ClutterAllocationFlags flags)
241 ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv;
242 ClutterActorClass *parent_class;
245 parent_class = CLUTTER_ACTOR_CLASS (clutter_clone_parent_class);
246 parent_class->allocate (self, box, flags);
248 if (priv->clone_source == NULL)
252 /* XXX - this is wrong: ClutterClone cannot clone unparented
253 * actors, as it will break all invariants
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
263 if (clutter_actor_get_parent (priv->clone_source) == NULL)
264 clutter_actor_allocate_preferred_size (priv->clone_source, flags);
269 clutter_clone_set_property (GObject *gobject,
274 ClutterClone *self = CLUTTER_CLONE (gobject);
279 clutter_clone_set_source (self, g_value_get_object (value));
283 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
289 clutter_clone_get_property (GObject *gobject,
294 ClutterClonePrivate *priv = CLUTTER_CLONE (gobject)->priv;
299 g_value_set_object (value, priv->clone_source);
303 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
309 clutter_clone_dispose (GObject *gobject)
311 clutter_clone_set_source_internal (CLUTTER_CLONE (gobject), NULL);
313 G_OBJECT_CLASS (clutter_clone_parent_class)->dispose (gobject);
317 clutter_clone_class_init (ClutterCloneClass *klass)
319 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
320 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
322 g_type_class_add_private (gobject_class, sizeof (ClutterClonePrivate));
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;
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;
337 * ClutterClone:source:
339 * This property specifies the source actor being cloned.
343 obj_props[PROP_SOURCE] =
344 g_param_spec_object ("source",
346 P_("Specifies the actor to be cloned"),
349 CLUTTER_PARAM_READWRITE);
351 g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
355 clutter_clone_init (ClutterClone *self)
357 ClutterClonePrivate *priv;
359 self->priv = priv = CLUTTER_CLONE_GET_PRIVATE (self);
361 priv->clone_source = NULL;
366 * @source: a #ClutterActor, or %NULL
368 * Creates a new #ClutterActor which clones @source/
370 * Return value: the newly created #ClutterClone
375 clutter_clone_new (ClutterActor *source)
377 return g_object_new (CLUTTER_TYPE_CLONE, "source", source, NULL);
381 clone_source_queue_redraw_cb (ClutterActor *source,
382 ClutterActor *origin,
385 clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
389 clone_source_queue_relayout_cb (ClutterActor *source,
392 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
396 clutter_clone_set_source_internal (ClutterClone *self,
397 ClutterActor *source)
399 ClutterClonePrivate *priv = self->priv;
401 if (priv->clone_source != NULL)
403 g_signal_handlers_disconnect_by_func (priv->clone_source,
404 G_CALLBACK (clone_source_queue_redraw_cb),
406 g_signal_handlers_disconnect_by_func (priv->clone_source,
407 G_CALLBACK (clone_source_queue_relayout_cb),
409 g_object_unref (priv->clone_source);
410 priv->clone_source = NULL;
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);
422 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SOURCE]);
424 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
428 * clutter_clone_set_source:
429 * @self: a #ClutterClone
430 * @source: (allow-none): a #ClutterActor, or %NULL
432 * Sets @source as the source actor to be cloned by @self.
437 clutter_clone_set_source (ClutterClone *self,
438 ClutterActor *source)
440 g_return_if_fail (CLUTTER_IS_CLONE (self));
441 g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source));
443 clutter_clone_set_source_internal (self, source);
444 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
448 * clutter_clone_get_source:
449 * @self: a #ClutterClone
451 * Retrieves the source #ClutterActor being cloned by @self.
453 * Return value: (transfer none): the actor source for the clone
458 clutter_clone_get_source (ClutterClone *self)
460 g_return_val_if_fail (CLUTTER_IS_CLONE (self), NULL);
462 return self->priv->clone_source;