1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Authored By Matthew Allum  <mallum@openedhand.com>
7  *
8  * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd
9  * Copyright (C) 2009, 2010, 2011, 2012 Intel Corp
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 /**
26  * SECTION:clutter-actor
27  * @short_description: The basic element of the scene graph
28  *
29  * The ClutterActor class is the basic element of the scene graph in Clutter,
30  * and it encapsulates the position, size, and transformations of a node in
31  * the graph.
32  *
33  * ## Actor transformations ## {#clutter-actor-transformations}
34  *
35  * Each actor can be transformed using methods like clutter_actor_set_scale()
36  * or clutter_actor_set_rotation(). The order in which the transformations are
37  * applied is decided by Clutter and it is the following:
38  *
39  *  1. translation by the origin of the #ClutterActor:allocation property
40  *  2. translation by the actor's #ClutterActor:z-position property
41  *  3. translation by the actor's #ClutterActor:pivot-point property
42  *  4. scaling by the #ClutterActor:scale-x and #ClutterActor:scale-y factors
43  *  5. rotation around the #ClutterActor:rotation-angle-x and #ClutterActor:rotation-center-x
44  *  6. rotation around the #ClutterActor:rotation-angle-y and #ClutterActor:rotation-center-y
45  *  7. rotation around the #ClutterActor:rotation-angle-z and #ClutterActor:rotation-center-z
46  *  8. negative translation by the actor's #ClutterActor:pivot-point
47  *
48  * ## Modifying an actor's geometry ## {#clutter-actor-geometry}
49  *
50  * Each actor has a bounding box, called #ClutterActor:allocation
51  * which is either set by its parent or explicitly through the
52  * clutter_actor_set_position() and clutter_actor_set_size() methods.
53  * Each actor also has an implicit preferred size.
54  *
55  * An actor’s preferred size can be defined by any subclass by
56  * overriding the #ClutterActorClass.get_preferred_width() and the
57  * #ClutterActorClass.get_preferred_height() virtual functions, or it can
58  * be explicitly set by using clutter_actor_set_width() and
59  * clutter_actor_set_height().
60  *
61  * An actor’s position can be set explicitly by using
62  * clutter_actor_set_x() and clutter_actor_set_y(); the coordinates are
63  * relative to the origin of the actor’s parent.
64  *
65  * ## Managing actor children ## {#clutter-actor-children}
66  *
67  * Each actor can have multiple children, by calling
68  * clutter_actor_add_child() to add a new child actor, and
69  * clutter_actor_remove_child() to remove an existing child. #ClutterActor
70  * will hold a reference on each child actor, which will be released when
71  * the child is removed from its parent, or destroyed using
72  * clutter_actor_destroy().
73  *
74  * |[<!-- language="C" -->
75  *  ClutterActor *actor = clutter_actor_new ();
76  *
77  *  // set the bounding box of the actor
78  *  clutter_actor_set_position (actor, 0, 0);
79  *  clutter_actor_set_size (actor, 480, 640);
80  *
81  *  // set the background color of the actor
82  *  clutter_actor_set_background_color (actor, CLUTTER_COLOR_Orange);
83  *
84  *  // set the bounding box of the child, relative to the parent
85  *  ClutterActor *child = clutter_actor_new ();
86  *  clutter_actor_set_position (child, 20, 20);
87  *  clutter_actor_set_size (child, 80, 240);
88  *
89  *  // set the background color of the child
90  *  clutter_actor_set_background_color (child, CLUTTER_COLOR_Blue);
91  *
92  *  // add the child to the actor
93  *  clutter_actor_add_child (actor, child);
94  * ]|
95  *
96  * Children can be inserted at a given index, or above and below
97  * another child actor. The order of insertion determines the order of the
98  * children when iterating over them. Iterating over children is performed
99  * by using clutter_actor_get_first_child(), clutter_actor_get_previous_sibling(),
100  * clutter_actor_get_next_sibling(), and clutter_actor_get_last_child(). It is
101  * also possible to retrieve a list of children by using
102  * clutter_actor_get_children(), as well as retrieving a specific child at a
103  * given index by using clutter_actor_get_child_at_index().
104  *
105  * If you need to track additions of children to a #ClutterActor, use
106  * the #ClutterContainer::actor-added signal; similarly, to track removals
107  * of children from a ClutterActor, use the #ClutterContainer::actor-removed
108  * signal.
109  *
110  * See [basic-actor.c](https://git.gnome.org/browse/clutter/tree/examples/basic-actor.c?h=clutter-1.18).
111  *
112  * ## Painting an actor ## {#clutter-actor-painting}
113  *
114  * There are three ways to paint an actor:
115  *
116  *  - set a delegate #ClutterContent as the value for the #ClutterActor:content property of the actor
117  *  - subclass #ClutterActor and override the #ClutterActorClass.paint_node() virtual function
118  *  - subclass #ClutterActor and override the #ClutterActorClass.paint() virtual function.
119  *
120  * A #ClutterContent is a delegate object that takes over the painting
121  * operations of one, or more actors. The #ClutterContent painting will
122  * be performed on top of the #ClutterActor:background-color of the actor,
123  * and before calling the actor's own implementation of the
124  * #ClutterActorClass.paint_node() virtual function.
125  *
126  * |[<!-- language="C" -->
127  * ClutterActor *actor = clutter_actor_new ();
128  *
129  * // set the bounding box
130  * clutter_actor_set_position (actor, 50, 50);
131  * clutter_actor_set_size (actor, 100, 100);
132  *
133  * // set the content; the image_content variable is set elsewhere
134  * clutter_actor_set_content (actor, image_content);
135  * ]|
136  *
137  * The #ClutterActorClass.paint_node() virtual function is invoked whenever
138  * an actor needs to be painted. The implementation of the virtual function
139  * must only paint the contents of the actor itself, and not the contents of
140  * its children, if the actor has any.
141  *
142  * The #ClutterPaintNode passed to the virtual function is the local root of
143  * the render tree; any node added to it will be rendered at the correct
144  * position, as defined by the actor's #ClutterActor:allocation.
145  *
146  * |[<!-- language="C" -->
147  * static void
148  * my_actor_paint_node (ClutterActor     *actor,
149  *                      ClutterPaintNode *root)
150  * {
151  *   ClutterPaintNode *node;
152  *   ClutterActorBox box;
153  *
154  *   // where the content of the actor should be painted
155  *   clutter_actor_get_allocation_box (actor, &box);
156  *
157  *   // the cogl_texture variable is set elsewhere
158  *   node = clutter_texture_node_new (cogl_texture, CLUTTER_COLOR_White,
159  *                                    CLUTTER_SCALING_FILTER_TRILINEAR,
160  *                                    CLUTTER_SCALING_FILTER_LINEAR);
161  *
162  *   // paint the content of the node using the allocation
163  *   clutter_paint_node_add_rectangle (node, &box);
164  *
165  *   // add the node, and transfer ownership
166  *   clutter_paint_node_add_child (root, node);
167  *   clutter_paint_node_unref (node);
168  * }
169  *
170  * The #ClutterActorClass.paint() virtual function function gives total
171  * control to the paint sequence of the actor itself, including the
172  * children of the actor, if any. It is strongly discouraged to override
173  * the #ClutterActorClass.paint() virtual function and it will be removed
174  * when the Clutter API changes.
175  *
176  * ## Handling events on an actor ## {#clutter-actor-event-handling}
177  *
178  * A #ClutterActor can receive and handle input device events, for
179  * instance pointer events and key events, as long as its
180  * #ClutterActor:reactive property is set to %TRUE.
181  *
182  * Once an actor has been determined to be the source of an event,
183  * Clutter will traverse the scene graph from the top-level actor towards the
184  * event source, emitting the #ClutterActor::captured-event signal on each
185  * ancestor until it reaches the source; this phase is also called
186  * the "capture" phase. If the event propagation was not stopped, the graph
187  * is walked backwards, from the source actor to the top-level, and the
188  * #ClutterActor::event signal is emitted, alongside eventual event-specific
189  * signals like #ClutterActor::button-press-event or #ClutterActor::motion-event;
190  * this phase is also called the "bubble" phase.
191  *
192  * At any point of the signal emission, signal handlers can stop the propagation
193  * through the scene graph by returning %CLUTTER_EVENT_STOP; otherwise, they can
194  * continue the propagation by returning %CLUTTER_EVENT_PROPAGATE.
195  *
196  * ## Animation ## {#clutter-actor-animation}
197  *
198  * Animation is a core concept of modern user interfaces; Clutter provides a
199  * complete and powerful animation framework that automatically tweens the
200  * actor's state without requiring direct, frame by frame manipulation from
201  * your application code. You have two models at your disposal:
202  *
203  *  - an implicit animation model
204  *  - an explicit animation model
205  *
206  * The implicit animation model of Clutter assumes that all the
207  * changes in an actor state should be gradual and asynchronous; Clutter
208  * will automatically transition an actor's property change between the
209  * current state and the desired one without manual intervention, if the
210  * property is defined to be animatable in its documentation.
211  *
212  * By default, in the 1.0 API series, the transition happens with a duration
213  * of zero milliseconds, and the implicit animation is an opt in feature to
214  * retain backwards compatibility.
215  *
216  * Implicit animations depend on the current easing state; in order to use
217  * the default easing state for an actor you should call the
218  * clutter_actor_save_easing_state() function:
219  *
220  * |[<!-- language="C" -->
221  * // assume that the actor is currently positioned at (100, 100)
222  *
223  * // store the current easing state and reset the new easing state to
224  * // its default values
225  * clutter_actor_save_easing_state (actor);
226  *
227  * // change the actor's position
228  * clutter_actor_set_position (actor, 500, 500);
229  *
230  * // restore the previously saved easing state
231  * clutter_actor_restore_easing_state (actor);
232  * ]|
233  *
234  * The example above will trigger an implicit animation of the
235  * actor between its current position to a new position.
236  *
237  * Implicit animations use a default duration of 250 milliseconds,
238  * and a default easing mode of %CLUTTER_EASE_OUT_CUBIC, unless you call
239  * clutter_actor_set_easing_mode() and clutter_actor_set_easing_duration()
240  * after changing the easing state of the actor.
241  *
242  * It is possible to animate multiple properties of an actor
243  * at the same time, and you can animate multiple actors at the same
244  * time as well, for instance:
245  *
246  * |[<!-- language="C" -->
247  * clutter_actor_save_easing_state (actor);
248  *
249  * // animate the actor's opacity and depth
250  * clutter_actor_set_opacity (actor, 0);
251  * clutter_actor_set_z_position (actor, -100);
252  *
253  * clutter_actor_restore_easing_state (actor);
254  *
255  * clutter_actor_save_easing_state (another_actor);
256  *
257  * // animate another actor's opacity
258  * clutter_actor_set_opacity (another_actor, 255);
259  * clutter_actor_set_z_position (another_actor, 100);
260  *
261  * clutter_actor_restore_easing_state (another_actor);
262  * ]|
263  *
264  * Changing the easing state will affect all the following property
265  * transitions, but will not affect existing transitions.
266  *
267  * It is important to note that if you modify the state on an
268  * animatable property while a transition is in flight, the transition's
269  * final value will be updated, as well as its duration and progress
270  * mode by using the current easing state; for instance, in the following
271  * example:
272  *
273  * |[<!-- language="C" -->
274  * clutter_actor_save_easing_state (actor);
275  * clutter_actor_set_easing_duration (actor, 1000);
276  * clutter_actor_set_x (actor, 200);
277  * clutter_actor_restore_easing_state (actor);
278  *
279  * clutter_actor_save_easing_state (actor);
280  * clutter_actor_set_easing_duration (actor, 500);
281  * clutter_actor_set_x (actor, 100);
282  * clutter_actor_restore_easing_state (actor);
283  * ]|
284  *
285  * the first call to clutter_actor_set_x() will begin a transition
286  * of the #ClutterActor:x property from the current value to the value of
287  * 200 over a duration of one second; the second call to clutter_actor_set_x()
288  * will change the transition's final value to 100 and the duration to 500
289  * milliseconds.
290  *
291  * It is possible to receive a notification of the completion of an
292  * implicit transition by using the #ClutterActor::transition-stopped
293  * signal, decorated with the name of the property. In case you want to
294  * know when all the currently in flight transitions are complete, use
295  * the #ClutterActor::transitions-completed signal instead.
296  *
297  * It is possible to retrieve the #ClutterTransition used by the
298  * animatable properties by using clutter_actor_get_transition() and using
299  * the property name as the transition name.
300  *
301  * The explicit animation model supported by Clutter requires that
302  * you create a #ClutterTransition object, and optionally set the initial
303  * and final values. The transition will not start unless you add it to the
304  * #ClutterActor.
305  *
306  * |[<!-- language="C" -->
307  * ClutterTransition *transition;
308  *
309  * transition = clutter_property_transition_new_for_actor (actor, "opacity");
310  * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 3000);
311  * clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), 2);
312  * clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE);
313  * clutter_transition_set_from (transition, G_TYPE_UINT, 255);
314  * clutter_transition_set_to (transition, G_TYPE_UINT, 0);
315  *
316  * clutter_actor_add_transition (actor, "animate-opacity", transition);
317  * ]|
318  *
319  * The example above will animate the #ClutterActor:opacity property
320  * of an actor between fully opaque and fully transparent, and back, over
321  * a span of 3 seconds. The animation does not begin until it is added to
322  * the actor.
323  *
324  * The explicit animation API applies to all #GObject properties,
325  * as well as the custom properties defined through the #ClutterAnimatable
326  * interface, regardless of whether they are defined as implicitly
327  * animatable or not.
328  *
329  * The explicit animation API should also be used when using custom
330  * animatable properties for #ClutterAction, #ClutterConstraint, and
331  * #ClutterEffect instances associated to an actor; see the section on
332  * custom animatable properties below for an example.
333  *
334  * Finally, explicit animations are useful for creating animations
335  * that run continuously, for instance:
336  *
337  * |[<!-- language="C" -->
338  * // this animation will pulse the actor's opacity continuously
339  * ClutterTransition *transition;
340  * ClutterInterval *interval;
341  *
342  * transition = clutter_property_transition_new_for_actor (actor, "opacity");
343  *
344  * // we want to animate the opacity between 0 and 255
345  * clutter_transition_set_from (transition, G_TYPE_UINT, 0);
346  * clutter_transition_set_to (transition, G_TYPE_UINT, 255);
347  *
348  * // over a one second duration, running an infinite amount of times
349  * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 1000);
350  * clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), -1);
351  *
352  * // we want to fade in and out, so we need to auto-reverse the transition
353  * clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE);
354  *
355  * // and we want to use an easing function that eases both in and out
356  * clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
357  *                                     CLUTTER_EASE_IN_OUT_CUBIC);
358  *
359  * // add the transition to the desired actor to start it
360  * clutter_actor_add_transition (actor, "opacityAnimation", transition);
361  * ]|
362  *
363  * ## Implementing an actor ## {#clutter-actor-implementing}
364  *
365  * Careful consideration should be given when deciding to implement
366  * a #ClutterActor sub-class. It is generally recommended to implement a
367  * sub-class of #ClutterActor only for actors that should be used as leaf
368  * nodes of a scene graph.
369  *
370  * By overriding the #ClutterActorClass.get_preferred_width() and
371  * #ClutterActorClass.get_preferred_height() virtual functions it is
372  * possible to change or provide the preferred size of an actor; similarly,
373  * by overriding the #ClutterActorClass.allocate() virtual function it is
374  * possible to control the layout of the children of an actor. Make sure to
375  * always chain up to the parent implementation of the
376  * #ClutterActorClass.allocate() virtual function.
377  *
378  * In general, it is strongly encouraged to use delegation and composition
379  * instead of direct subclassing.
380  *
381  * ## ClutterActor custom properties for ClutterScript ## {#clutter-actor-custom-script}
382  *
383  * #ClutterActor defines a custom "rotation" property which allows a short-hand
384  * description of the rotations to be applied to an actor.
385  *
386  * The syntax of the "rotation" property is the following:
387  *
388  * |[
389  * "rotation" : [ { "<axis>" : [ <angle>, [ <center-point> ] ] } ]
390  * ]|
391  *
392  * where:
393  *
394  *  - axis is the name of an enumeration value of type #ClutterRotateAxis
395  *  - angle is a floating point value representing the rotation angle on the given axis in degrees
396  *  - center-point is an optional array, and if present it must contain the center of rotation as described by two coordinates:
397  *    - Y and Z for "x-axis"
398  *    - X and Z for "y-axis"
399  *    - X and Y for "z-axis".
400  *
401  * #ClutterActor also defines a scriptable "margin" property which follows the CSS "margin" shorthand.
402  *
403  * |[
404  *   // 4 values
405  *   "margin" : [ top, right, bottom, left ]
406  *   // 3 values
407  *   "margin" : [ top, left/right, bottom ]
408  *   // 2 values
409  *   "margin" : [ top/bottom, left/right ]
410  *   // 1 value
411  *   "margin" : [ top/right/bottom/left ]
412  * ]|
413  *
414  * #ClutterActor will also parse every positional and dimensional
415  * property defined as a string through clutter_units_from_string(); you
416  * should read the documentation for the #ClutterUnits parser format for
417  * the valid units and syntax.
418  *
419  * ## Custom animatable properties
420  *
421  * #ClutterActor allows accessing properties of #ClutterAction,
422  * #ClutterEffect, and #ClutterConstraint instances associated to an actor
423  * instance for animation purposes, as well as its #ClutterLayoutManager.
424  *
425  * In order to access a specific #ClutterAction or a #ClutterConstraint
426  * property it is necessary to set the #ClutterActorMeta:name property on the
427  * given action or constraint.
428  *
429  * The property can be accessed using the following syntax:
430  *
431  * |[
432  *   @<section>.<meta-name>.<property-name>
433  * ]|
434  *
435  *  - the initial `@` is mandatory
436  *  - the `section` fragment can be one between "actions", "constraints", "content",
437  *    and "effects"
438  *  - the `meta-name` fragment is the name of the action, effect, or constraint, as
439  *    specified by the #ClutterActorMeta:name property of #ClutterActorMeta
440  *  - the `property-name` fragment is the name of the action, effect, or constraint
441  *    property to be animated.
442  *
443  * The example below animates a #ClutterBindConstraint applied to an actor
444  * using an explicit transition. The `rect` actor has a binding constraint
445  * on the `origin` actor, and in its initial state is overlapping the actor
446  * to which is bound to.
447  *
448  * As the actor has only one #ClutterLayoutManager, the syntax for accessing its
449  * properties is simpler:
450  *
451  * |[
452  *   @layout.<property-name>
453  * ]|
454  *
455  * |[<!-- language="C" -->
456  * constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_X, 0.0);
457  * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-x");
458  * clutter_actor_add_constraint (rect, constraint);
459  *
460  * constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_Y, 0.0);
461  * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-y");
462  * clutter_actor_add_constraint (rect, constraint);
463  *
464  * clutter_actor_set_reactive (origin, TRUE);
465  *
466  * g_signal_connect (origin, "button-press-event",
467  *                   G_CALLBACK (on_button_press),
468  *                   rect);
469  * ]|
470  *
471  * On button press, the rectangle "slides" from behind the actor to
472  * which is bound to, using the #ClutterBindConstraint:offset property to
473  * achieve the effect:
474  *
475  * |[<!-- language="C" -->
476  * gboolean
477  * on_button_press (ClutterActor *origin,
478  *                  ClutterEvent *event,
479  *                  ClutterActor *rect)
480  * {
481  *   ClutterTransition *transition;
482  *
483  *   // the offset that we want to apply; this will make the actor
484  *   // slide in from behind the origin and rest at the right of
485  *   // the origin, plus a padding value
486  *   float new_offset = clutter_actor_get_width (origin) + h_padding;
487  *
488  *   // the property we wish to animate; the "@constraints" section
489  *   // tells Clutter to check inside the constraints associated
490  *   // with the actor; the "bind-x" section is the name of the
491  *   // constraint; and the "offset" is the name of the property
492  *   // on the constraint
493  *   const char *prop = "@constraints.bind-x.offset";
494  *
495  *   // create a new transition for the given property
496  *   transition = clutter_property_transition_new_for_actor (rect, prop);
497  *
498  *   // set the easing mode and duration
499  *   clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
500  *                                       CLUTTER_EASE_OUT_CUBIC);
501  *   clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 500);
502  *
503  *   // create the interval with the initial and final values
504  *   clutter_transition_set_from (transition, G_TYPE_FLOAT, 0.f);
505  *   clutter_transition_set_to (transition, G_TYPE_FLOAT, new_offset);
506  *
507  *   // add the transition to the actor; this causes the animation
508  *   // to start. the name "offsetAnimation" can be used to retrieve
509  *   // the transition later
510  *   clutter_actor_add_transition (rect, "offsetAnimation", transition);
511  *
512  *   // we handled the event
513  *   return CLUTTER_EVENT_STOP;
514  * }
515  * ]|
516  */
517 
518 /**
519  * CLUTTER_ACTOR_IS_MAPPED:
520  * @a: a #ClutterActor
521  *
522  * Evaluates to %TRUE if the %CLUTTER_ACTOR_MAPPED flag is set.
523  *
524  * The mapped state is set when the actor is visible and all its parents up
525  * to a top-level (e.g. a #ClutterStage) are visible, realized, and mapped.
526  *
527  * This check can be used to see if an actor is going to be painted, as only
528  * actors with the %CLUTTER_ACTOR_MAPPED flag set are going to be painted.
529  *
530  * The %CLUTTER_ACTOR_MAPPED flag is managed by Clutter itself, and it should
531  * not be checked directly; instead, the recommended usage is to connect a
532  * handler on the #GObject::notify signal for the #ClutterActor:mapped
533  * property of #ClutterActor, and check the presence of
534  * the %CLUTTER_ACTOR_MAPPED flag on state changes.
535  *
536  * It is also important to note that Clutter may delay the changes of
537  * the %CLUTTER_ACTOR_MAPPED flag on top-levels due to backend-specific
538  * limitations, or during the reparenting of an actor, to optimize
539  * unnecessary (and potentially expensive) state changes.
540  *
541  * Since: 0.2
542  *
543  * Deprecated: 1.24: Use clutter_actor_is_mapped() or the #ClutterActor:mapped
544  *   property instead of this macro.
545  */
546 
547 /**
548  * CLUTTER_ACTOR_IS_REALIZED:
549  * @a: a #ClutterActor
550  *
551  * Evaluates to %TRUE if the %CLUTTER_ACTOR_REALIZED flag is set.
552  *
553  * The realized state has an actor-dependant interpretation. If an
554  * actor wants to delay allocating resources until it is attached to a
555  * stage, it may use the realize state to do so. However it is
556  * perfectly acceptable for an actor to allocate Cogl resources before
557  * being realized because there is only one drawing context used by Clutter
558  * so any resources will work on any stage.  If an actor is mapped it
559  * must also be realized, but an actor can be realized and unmapped
560  * (this is so hiding an actor temporarily doesn't do an expensive
561  * unrealize/realize).
562  *
563  * To be realized an actor must be inside a stage, and all its parents
564  * must be realized.
565  *
566  * Since: 0.2
567  *
568  * Deprecated: 1.24: Use clutter_actor_is_realized() or the #ClutterActor:realized
569  *   property instead of this macro.
570  */
571 
572 /**
573  * CLUTTER_ACTOR_IS_VISIBLE:
574  * @a: a #ClutterActor
575  *
576  * Evaluates to %TRUE if the actor has been shown, %FALSE if it's hidden.
577  * Equivalent to the ClutterActor::visible object property.
578  *
579  * Note that an actor is only painted onscreen if it's mapped, which
580  * means it's visible, and all its parents are visible, and one of the
581  * parents is a toplevel stage; see also %CLUTTER_ACTOR_IS_MAPPED.
582  *
583  * Since: 0.2
584  *
585  * Deprecated: 1.24: Use clutter_actor_is_visible() or the #ClutterActor:visible
586  *   property instead of this macro.
587  */
588 
589 /**
590  * CLUTTER_ACTOR_IS_REACTIVE:
591  * @a: a #ClutterActor
592  *
593  * Evaluates to %TRUE if the %CLUTTER_ACTOR_REACTIVE flag is set.
594  *
595  * Only reactive actors will receive event-related signals.
596  *
597  * Since: 0.6
598  *
599  * Deprecated: 1.24: Use clutter_actor_get_reactive() or the
600  *   #ClutterActor:reactive property instead of this macro.
601  */
602 
603 #include "clutter-build-config.h"
604 
605 #include <math.h>
606 
607 #include <gobject/gvaluecollector.h>
608 
609 #include <cogl/cogl.h>
610 
611 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
612 
613 #include "clutter-actor-private.h"
614 
615 #include "clutter-action.h"
616 #include "clutter-actor-meta-private.h"
617 #include "clutter-animatable.h"
618 #include "clutter-color-static.h"
619 #include "clutter-color.h"
620 #include "clutter-constraint-private.h"
621 #include "clutter-container-private.h"
622 #include "clutter-content-private.h"
623 #include "clutter-debug.h"
624 #include "clutter-easing.h"
625 #include "clutter-effect-private.h"
626 #include "clutter-enum-types.h"
627 #include "clutter-fixed-layout.h"
628 #include "clutter-flatten-effect.h"
629 #include "clutter-interval.h"
630 #include "clutter-main.h"
631 #include "clutter-marshal.h"
632 #include "clutter-mutter.h"
633 #include "clutter-paint-context-private.h"
634 #include "clutter-paint-nodes.h"
635 #include "clutter-paint-node-private.h"
636 #include "clutter-paint-volume-private.h"
637 #include "clutter-pick-context-private.h"
638 #include "clutter-private.h"
639 #include "clutter-property-transition.h"
640 #include "clutter-scriptable.h"
641 #include "clutter-script-private.h"
642 #include "clutter-stage-private.h"
643 #include "clutter-stage-view-private.h"
644 #include "clutter-timeline.h"
645 #include "clutter-transition.h"
646 #include "clutter-units.h"
647 
648 #include "deprecated/clutter-container.h"
649 
650 /* Internal enum used to control mapped state update.  This is a hint
651  * which indicates when to do something other than just enforce
652  * invariants.
653  */
654 typedef enum
655 {
656   MAP_STATE_CHECK,           /* just enforce invariants. */
657   MAP_STATE_MAKE_UNREALIZED, /* force unrealize, ignoring invariants,
658                               * used when about to unparent.
659                               */
660   MAP_STATE_MAKE_MAPPED,     /* set mapped, error if invariants not met;
661                               * used to set mapped on toplevels.
662                               */
663   MAP_STATE_MAKE_UNMAPPED    /* set unmapped, even if parent is mapped,
664                               * used just before unmapping parent.
665                               */
666 } MapStateChange;
667 
668 /* 3 entries should be a good compromise, few layout managers
669  * will ask for 3 different preferred size in each allocation cycle */
670 #define N_CACHED_SIZE_REQUESTS 3
671 
672 struct _ClutterActorPrivate
673 {
674   /* request mode */
675   ClutterRequestMode request_mode;
676 
677   /* our cached size requests for different width / height */
678   SizeRequest width_requests[N_CACHED_SIZE_REQUESTS];
679   SizeRequest height_requests[N_CACHED_SIZE_REQUESTS];
680 
681   /* An age of 0 means the entry is not set */
682   guint cached_height_age;
683   guint cached_width_age;
684 
685   /* the bounding box of the actor, relative to the parent's
686    * allocation
687    */
688   ClutterActorBox allocation;
689 
690   /* clip, in actor coordinates */
691   graphene_rect_t clip;
692 
693   /* the cached transformation matrix; see apply_transform() */
694   graphene_matrix_t transform;
695 
696   float resource_scale;
697 
698   guint8 opacity;
699   gint opacity_override;
700   unsigned int inhibit_culling_counter;
701 
702   ClutterOffscreenRedirect offscreen_redirect;
703 
704   /* This is an internal effect used to implement the
705      offscreen-redirect property */
706   ClutterEffect *flatten_effect;
707 
708   /* scene graph */
709   ClutterActor *parent;
710   ClutterActor *prev_sibling;
711   ClutterActor *next_sibling;
712   ClutterActor *first_child;
713   ClutterActor *last_child;
714 
715   gint n_children;
716 
717   /* tracks whenever the children of an actor are changed; the
718    * age is incremented by 1 whenever an actor is added or
719    * removed. the age is not incremented when the first or the
720    * last child pointers are changed, or when grandchildren of
721    * an actor are changed.
722    */
723   gint age;
724 
725   gchar *name; /* a non-unique name, used for debugging */
726 
727   /* a back-pointer to the Pango context that we can use
728    * to create pre-configured PangoLayout
729    */
730   PangoContext *pango_context;
731 
732   /* the text direction configured for this child - either by
733    * application code, or by the actor's parent
734    */
735   ClutterTextDirection text_direction;
736 
737   /* meta classes */
738   ClutterMetaGroup *actions;
739   ClutterMetaGroup *constraints;
740   ClutterMetaGroup *effects;
741 
742   /* delegate object used to allocate the children of this actor */
743   ClutterLayoutManager *layout_manager;
744 
745   /* delegate object used to paint the contents of this actor */
746   ClutterContent *content;
747 
748   ClutterActorBox content_box;
749   ClutterContentGravity content_gravity;
750   ClutterScalingFilter min_filter;
751   ClutterScalingFilter mag_filter;
752   ClutterContentRepeat content_repeat;
753 
754   /* used when painting, to update the paint volume */
755   ClutterEffect *current_effect;
756 
757   /* This is used to store an effect which needs to be redrawn. A
758      redraw can be queued to start from a particular effect. This is
759      used by parametrised effects that can cache an image of the
760      actor. If a parameter of the effect changes then it only needs to
761      redraw the cached image, not the actual actor. The pointer is
762      only valid if is_dirty == TRUE. If the pointer is NULL then the
763      whole actor is dirty. */
764   ClutterEffect *effect_to_redraw;
765 
766   /* This is used when painting effects to implement the
767      clutter_actor_continue_paint() function. It points to the node in
768      the list of effects that is next in the chain */
769   const GList *next_effect_to_paint;
770 
771   ClutterPaintVolume paint_volume;
772 
773   /* NB: This volume isn't relative to this actor, it is in eye
774    * coordinates so that it can remain valid after the actor changes.
775    */
776   ClutterPaintVolume last_paint_volume;
777 
778   ClutterColor bg_color;
779 
780   /* a string used for debugging messages */
781   char *debug_name;
782 
783   /* a set of clones of the actor */
784   GHashTable *clones;
785 
786   /* whether the actor is inside a cloned branch; this
787    * value is propagated to all the actor's children
788    */
789   gulong in_cloned_branch;
790 
791   guint unmapped_paint_branch_counter;
792 
793   GListModel *child_model;
794   ClutterActorCreateChildFunc create_child_func;
795   gpointer create_child_data;
796   GDestroyNotify create_child_notify;
797 
798   gulong resolution_changed_id;
799   gulong font_changed_id;
800   gulong layout_changed_id;
801 
802   GList *stage_views;
803 
804   /* bitfields: KEEP AT THE END */
805 
806   /* fixed position and sizes */
807   guint position_set                : 1;
808   guint min_width_set               : 1;
809   guint min_height_set              : 1;
810   guint natural_width_set           : 1;
811   guint natural_height_set          : 1;
812   /* cached request is invalid (implies allocation is too) */
813   guint needs_width_request         : 1;
814   /* cached request is invalid (implies allocation is too) */
815   guint needs_height_request        : 1;
816   /* cached allocation is invalid (request has changed, probably) */
817   guint needs_allocation            : 1;
818   guint show_on_set_parent          : 1;
819   guint has_clip                    : 1;
820   guint clip_to_allocation          : 1;
821   guint enable_model_view_transform : 1;
822   guint enable_paint_unmapped       : 1;
823   guint has_pointer                 : 1;
824   guint has_key_focus               : 1;
825   guint propagated_one_redraw       : 1;
826   guint paint_volume_valid          : 1;
827   guint last_paint_volume_valid     : 1;
828   guint in_clone_paint              : 1;
829   guint transform_valid             : 1;
830   /* This is TRUE if anything has queued a redraw since we were last
831      painted. In this case effect_to_redraw will point to an effect
832      the redraw was queued from or it will be NULL if the redraw was
833      queued without an effect. */
834   guint is_dirty                    : 1;
835   guint bg_color_set                : 1;
836   guint content_box_valid           : 1;
837   guint x_expand_set                : 1;
838   guint y_expand_set                : 1;
839   guint needs_compute_expand        : 1;
840   guint needs_x_expand              : 1;
841   guint needs_y_expand              : 1;
842   guint needs_paint_volume_update   : 1;
843   guint had_effects_on_last_paint_volume_update : 1;
844   guint needs_update_stage_views    : 1;
845 };
846 
847 enum
848 {
849   PROP_0,
850 
851   PROP_NAME,
852 
853   /* X, Y, WIDTH, HEIGHT are "do what I mean" properties;
854    * when set they force a size request, when gotten they
855    * get the allocation if the allocation is valid, and the
856    * request otherwise
857    */
858   PROP_X,
859   PROP_Y,
860   PROP_WIDTH,
861   PROP_HEIGHT,
862 
863   PROP_POSITION,
864   PROP_SIZE,
865 
866   /* Then the rest of these size-related properties are the "actual"
867    * underlying properties set or gotten by X, Y, WIDTH, HEIGHT
868    */
869   PROP_FIXED_X,
870   PROP_FIXED_Y,
871 
872   PROP_FIXED_POSITION_SET,
873 
874   PROP_MIN_WIDTH,
875   PROP_MIN_WIDTH_SET,
876 
877   PROP_MIN_HEIGHT,
878   PROP_MIN_HEIGHT_SET,
879 
880   PROP_NATURAL_WIDTH,
881   PROP_NATURAL_WIDTH_SET,
882 
883   PROP_NATURAL_HEIGHT,
884   PROP_NATURAL_HEIGHT_SET,
885 
886   PROP_REQUEST_MODE,
887 
888   /* Allocation properties are read-only */
889   PROP_ALLOCATION,
890 
891   PROP_Z_POSITION,
892 
893   PROP_CLIP_RECT,
894   PROP_HAS_CLIP,
895   PROP_CLIP_TO_ALLOCATION,
896 
897   PROP_OPACITY,
898 
899   PROP_OFFSCREEN_REDIRECT,
900 
901   PROP_VISIBLE,
902   PROP_MAPPED,
903   PROP_REALIZED,
904   PROP_REACTIVE,
905 
906   PROP_PIVOT_POINT,
907   PROP_PIVOT_POINT_Z,
908 
909   PROP_SCALE_X,
910   PROP_SCALE_Y,
911   PROP_SCALE_Z,
912 
913   PROP_ROTATION_ANGLE_X, /* XXX:2.0 rename to rotation-x */
914   PROP_ROTATION_ANGLE_Y, /* XXX:2.0 rename to rotation-y */
915   PROP_ROTATION_ANGLE_Z, /* XXX:2.0 rename to rotation-z */
916 
917   PROP_TRANSLATION_X,
918   PROP_TRANSLATION_Y,
919   PROP_TRANSLATION_Z,
920 
921   PROP_TRANSFORM,
922   PROP_TRANSFORM_SET,
923   PROP_CHILD_TRANSFORM,
924   PROP_CHILD_TRANSFORM_SET,
925 
926   PROP_SHOW_ON_SET_PARENT, /*XXX:2.0 remove */
927 
928   PROP_TEXT_DIRECTION,
929   PROP_HAS_POINTER,
930 
931   PROP_ACTIONS,
932   PROP_CONSTRAINTS,
933   PROP_EFFECT,
934 
935   PROP_LAYOUT_MANAGER,
936 
937   PROP_X_EXPAND,
938   PROP_Y_EXPAND,
939   PROP_X_ALIGN,
940   PROP_Y_ALIGN,
941   PROP_MARGIN_TOP,
942   PROP_MARGIN_BOTTOM,
943   PROP_MARGIN_LEFT,
944   PROP_MARGIN_RIGHT,
945 
946   PROP_BACKGROUND_COLOR,
947   PROP_BACKGROUND_COLOR_SET,
948 
949   PROP_FIRST_CHILD,
950   PROP_LAST_CHILD,
951 
952   PROP_CONTENT,
953   PROP_CONTENT_GRAVITY,
954   PROP_CONTENT_BOX,
955   PROP_MINIFICATION_FILTER,
956   PROP_MAGNIFICATION_FILTER,
957   PROP_CONTENT_REPEAT,
958 
959   PROP_LAST
960 };
961 
962 static GParamSpec *obj_props[PROP_LAST];
963 
964 enum
965 {
966   SHOW,
967   HIDE,
968   DESTROY,
969   PARENT_SET,
970   KEY_FOCUS_IN,
971   KEY_FOCUS_OUT,
972   PAINT,
973   PICK,
974   REALIZE,
975   UNREALIZE,
976   QUEUE_RELAYOUT,
977   EVENT,
978   CAPTURED_EVENT,
979   BUTTON_PRESS_EVENT,
980   BUTTON_RELEASE_EVENT,
981   SCROLL_EVENT,
982   KEY_PRESS_EVENT,
983   KEY_RELEASE_EVENT,
984   MOTION_EVENT,
985   ENTER_EVENT,
986   LEAVE_EVENT,
987   TRANSITIONS_COMPLETED,
988   TOUCH_EVENT,
989   TRANSITION_STOPPED,
990   STAGE_VIEWS_CHANGED,
991   RESOURCE_SCALE_CHANGED,
992 
993   LAST_SIGNAL
994 };
995 
996 static guint actor_signals[LAST_SIGNAL] = { 0, };
997 
998 typedef struct _TransitionClosure
999 {
1000   ClutterActor *actor;
1001   ClutterTransition *transition;
1002   gchar *name;
1003   gulong completed_id;
1004 } TransitionClosure;
1005 
1006 static void clutter_container_iface_init  (ClutterContainerIface  *iface);
1007 static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
1008 static void clutter_animatable_iface_init (ClutterAnimatableInterface *iface);
1009 static void atk_implementor_iface_init    (AtkImplementorIface    *iface);
1010 
1011 /* These setters are all static for now, maybe they should be in the
1012  * public API, but they are perhaps obscure enough to leave only as
1013  * properties
1014  */
1015 static void clutter_actor_set_min_width          (ClutterActor *self,
1016                                                   gfloat        min_width);
1017 static void clutter_actor_set_min_height         (ClutterActor *self,
1018                                                   gfloat        min_height);
1019 static void clutter_actor_set_natural_width      (ClutterActor *self,
1020                                                   gfloat        natural_width);
1021 static void clutter_actor_set_natural_height     (ClutterActor *self,
1022                                                   gfloat        natural_height);
1023 static void clutter_actor_set_min_width_set      (ClutterActor *self,
1024                                                   gboolean      use_min_width);
1025 static void clutter_actor_set_min_height_set     (ClutterActor *self,
1026                                                   gboolean      use_min_height);
1027 static void clutter_actor_set_natural_width_set  (ClutterActor *self,
1028                                                   gboolean  use_natural_width);
1029 static void clutter_actor_set_natural_height_set (ClutterActor *self,
1030                                                   gboolean  use_natural_height);
1031 static void clutter_actor_update_map_state       (ClutterActor  *self,
1032                                                   MapStateChange change);
1033 static void clutter_actor_unrealize_not_hiding   (ClutterActor *self);
1034 
1035 static void _clutter_actor_get_relative_transformation_matrix (ClutterActor      *self,
1036                                                                ClutterActor      *ancestor,
1037                                                                graphene_matrix_t *matrix);
1038 
1039 static ClutterPaintVolume *_clutter_actor_get_paint_volume_mutable (ClutterActor *self);
1040 
1041 static guint8   clutter_actor_get_paint_opacity_internal        (ClutterActor *self);
1042 
1043 static inline void clutter_actor_set_background_color_internal (ClutterActor *self,
1044                                                                 const ClutterColor *color);
1045 
1046 static void on_layout_manager_changed (ClutterLayoutManager *manager,
1047                                        ClutterActor         *self);
1048 
1049 static inline void clutter_actor_queue_compute_expand (ClutterActor *self);
1050 
1051 static inline void clutter_actor_set_margin_internal (ClutterActor *self,
1052                                                       gfloat        margin,
1053                                                       GParamSpec   *pspec);
1054 
1055 static void clutter_actor_set_transform_internal (ClutterActor            *self,
1056                                                   const graphene_matrix_t *transform);
1057 static void clutter_actor_set_child_transform_internal (ClutterActor            *self,
1058                                                         const graphene_matrix_t *transform);
1059 
1060 static void     clutter_actor_realize_internal          (ClutterActor *self);
1061 static void     clutter_actor_unrealize_internal        (ClutterActor *self);
1062 
1063 static void clutter_actor_push_in_cloned_branch (ClutterActor *self,
1064                                                  gulong        count);
1065 static void clutter_actor_pop_in_cloned_branch (ClutterActor *self,
1066                                                 gulong        count);
1067 static void ensure_valid_actor_transform (ClutterActor *actor);
1068 
1069 static void push_in_paint_unmapped_branch (ClutterActor *self,
1070                                            guint         count);
1071 static void pop_in_paint_unmapped_branch (ClutterActor *self,
1072                                           guint         count);
1073 
1074 static GQuark quark_actor_layout_info = 0;
1075 static GQuark quark_actor_transform_info = 0;
1076 static GQuark quark_actor_animation_info = 0;
1077 
1078 static GQuark quark_key = 0;
1079 static GQuark quark_motion = 0;
1080 static GQuark quark_pointer_focus = 0;
1081 static GQuark quark_button = 0;
1082 static GQuark quark_scroll = 0;
1083 static GQuark quark_stage = 0;
1084 static GQuark quark_touch = 0;
1085 static GQuark quark_touchpad = 0;
1086 static GQuark quark_proximity = 0;
1087 static GQuark quark_pad = 0;
1088 static GQuark quark_im = 0;
1089 
1090 G_DEFINE_TYPE_WITH_CODE (ClutterActor,
1091                          clutter_actor,
1092                          G_TYPE_INITIALLY_UNOWNED,
1093                          G_ADD_PRIVATE (ClutterActor)
1094                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
1095                                                 clutter_container_iface_init)
1096                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
1097                                                 clutter_scriptable_iface_init)
1098                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE,
1099                                                 clutter_animatable_iface_init)
1100                          G_IMPLEMENT_INTERFACE (ATK_TYPE_IMPLEMENTOR,
1101                                                 atk_implementor_iface_init));
1102 
1103 /*< private >
1104  * clutter_actor_get_debug_name:
1105  * @actor: a #ClutterActor
1106  *
1107  * Retrieves a printable name of @actor for debugging messages
1108  *
1109  * Return value: a string with a printable name
1110  */
1111 const char *
_clutter_actor_get_debug_name(ClutterActor * actor)1112 _clutter_actor_get_debug_name (ClutterActor *actor)
1113 {
1114   ClutterActorPrivate *priv = actor->priv;
1115   const char *retval;
1116 
1117   if (G_UNLIKELY (priv->debug_name == NULL))
1118     {
1119       priv->debug_name = g_strdup_printf ("<%s>[<%s>:%p]",
1120                                           priv->name != NULL ? priv->name
1121                                                              : "unnamed",
1122                                           G_OBJECT_TYPE_NAME (actor),
1123                                           actor);
1124     }
1125 
1126   retval = priv->debug_name;
1127 
1128   return retval;
1129 }
1130 
1131 #ifdef CLUTTER_ENABLE_DEBUG
1132 /* XXX - this is for debugging only, remove once working (or leave
1133  * in only in some debug mode). Should leave it for a little while
1134  * until we're confident in the new map/realize/visible handling.
1135  */
1136 static inline void
clutter_actor_verify_map_state(ClutterActor * self)1137 clutter_actor_verify_map_state (ClutterActor *self)
1138 {
1139   ClutterActorPrivate *priv = self->priv;
1140 
1141   if (CLUTTER_ACTOR_IS_REALIZED (self))
1142     {
1143       if (priv->parent == NULL)
1144         {
1145           if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
1146             {
1147               g_warning ("Realized non-toplevel actor '%s' should "
1148                          "have a parent",
1149                          _clutter_actor_get_debug_name (self));
1150             }
1151         }
1152       else if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent))
1153         {
1154           g_warning ("Realized actor %s has an unrealized parent %s",
1155                      _clutter_actor_get_debug_name (self),
1156                      _clutter_actor_get_debug_name (priv->parent));
1157         }
1158     }
1159 
1160   if (CLUTTER_ACTOR_IS_MAPPED (self))
1161     {
1162       if (!CLUTTER_ACTOR_IS_REALIZED (self))
1163         g_warning ("Actor '%s' is mapped but not realized",
1164                    _clutter_actor_get_debug_name (self));
1165 
1166       if (priv->parent == NULL)
1167         {
1168           if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
1169             {
1170               if (!CLUTTER_ACTOR_IS_VISIBLE (self) &&
1171                   !CLUTTER_ACTOR_IN_DESTRUCTION (self))
1172                 {
1173                   g_warning ("Toplevel actor '%s' is mapped "
1174                              "but not visible",
1175                              _clutter_actor_get_debug_name (self));
1176                 }
1177             }
1178           else
1179             {
1180               g_warning ("Mapped actor '%s' should have a parent",
1181                          _clutter_actor_get_debug_name (self));
1182             }
1183         }
1184       else
1185         {
1186           ClutterActor *iter = self;
1187 
1188           /* check for the enable_paint_unmapped flag on the actor
1189            * and parents; if the flag is enabled at any point of this
1190            * branch of the scene graph then all the later checks
1191            * become pointless
1192            */
1193           while (iter != NULL)
1194             {
1195               if (iter->priv->enable_paint_unmapped)
1196                 return;
1197 
1198               iter = iter->priv->parent;
1199             }
1200 
1201           if (!CLUTTER_ACTOR_IS_VISIBLE (priv->parent))
1202             {
1203               g_warning ("Actor '%s' should not be mapped if parent '%s'"
1204                          "is not visible",
1205                          _clutter_actor_get_debug_name (self),
1206                          _clutter_actor_get_debug_name (priv->parent));
1207             }
1208 
1209           if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent))
1210             {
1211               g_warning ("Actor '%s' should not be mapped if parent '%s'"
1212                          "is not realized",
1213                          _clutter_actor_get_debug_name (self),
1214                          _clutter_actor_get_debug_name (priv->parent));
1215             }
1216 
1217           if (!CLUTTER_ACTOR_IS_TOPLEVEL (priv->parent))
1218             {
1219               if (!CLUTTER_ACTOR_IS_MAPPED (priv->parent))
1220                 g_warning ("Actor '%s' is mapped but its non-toplevel "
1221                            "parent '%s' is not mapped",
1222                            _clutter_actor_get_debug_name (self),
1223                            _clutter_actor_get_debug_name (priv->parent));
1224             }
1225         }
1226     }
1227 }
1228 
1229 #endif /* CLUTTER_ENABLE_DEBUG */
1230 
1231 /**
1232  * clutter_actor_pick_box:
1233  * @self: The #ClutterActor being "pick" painted.
1234  * @pick_context: The #ClutterPickContext
1235  * @box: A rectangle in the actor's own local coordinates.
1236  *
1237  * Logs (does a virtual paint of) a rectangle for picking. Note that @box is
1238  * in the actor's own local coordinates, so is usually {0,0,width,height}
1239  * to include the whole actor. That is unless the actor has a shaped input
1240  * region in which case you may wish to log the (multiple) smaller rectangles
1241  * that make up the input region.
1242  */
1243 void
clutter_actor_pick_box(ClutterActor * self,ClutterPickContext * pick_context,const ClutterActorBox * box)1244 clutter_actor_pick_box (ClutterActor          *self,
1245                         ClutterPickContext    *pick_context,
1246                         const ClutterActorBox *box)
1247 {
1248   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1249   g_return_if_fail (box != NULL);
1250 
1251   if (box->x1 >= box->x2 || box->y1 >= box->y2)
1252     return;
1253 
1254   clutter_pick_context_log_pick (pick_context, box, self);
1255 }
1256 
1257 static void
clutter_actor_set_mapped(ClutterActor * self,gboolean mapped)1258 clutter_actor_set_mapped (ClutterActor *self,
1259                           gboolean      mapped)
1260 {
1261   if (CLUTTER_ACTOR_IS_MAPPED (self) == mapped)
1262     return;
1263 
1264   if (mapped)
1265     {
1266       CLUTTER_ACTOR_GET_CLASS (self)->map (self);
1267       g_assert (CLUTTER_ACTOR_IS_MAPPED (self));
1268     }
1269   else
1270     {
1271       CLUTTER_ACTOR_GET_CLASS (self)->unmap (self);
1272       g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
1273     }
1274 }
1275 
1276 /* this function updates the mapped and realized states according to
1277  * invariants, in the appropriate order.
1278  */
1279 static void
clutter_actor_update_map_state(ClutterActor * self,MapStateChange change)1280 clutter_actor_update_map_state (ClutterActor  *self,
1281                                 MapStateChange change)
1282 {
1283   gboolean was_mapped;
1284 
1285   was_mapped = CLUTTER_ACTOR_IS_MAPPED (self);
1286 
1287   if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
1288     {
1289       /* the mapped flag on top-level actors must be set by the
1290        * per-backend implementation because it might be asynchronous.
1291        *
1292        * That is, the MAPPED flag on toplevels currently tracks the X
1293        * server mapped-ness of the window, while the expected behavior
1294        * (if used to GTK) may be to track WM_STATE!=WithdrawnState.
1295        * This creates some weird complexity by breaking the invariant
1296        * that if we're visible and all ancestors shown then we are
1297        * also mapped - instead, we are mapped if all ancestors
1298        * _possibly excepting_ the stage are mapped. The stage
1299        * will map/unmap for example when it is minimized or
1300        * moved to another workspace.
1301        *
1302        * So, the only invariant on the stage is that if visible it
1303        * should be realized, and that it has to be visible to be
1304        * mapped.
1305        */
1306       if (CLUTTER_ACTOR_IS_VISIBLE (self))
1307         clutter_actor_realize (self);
1308 
1309       switch (change)
1310         {
1311         case MAP_STATE_CHECK:
1312           break;
1313 
1314         case MAP_STATE_MAKE_MAPPED:
1315           g_assert (!was_mapped);
1316           clutter_actor_set_mapped (self, TRUE);
1317           break;
1318 
1319         case MAP_STATE_MAKE_UNMAPPED:
1320           g_assert (was_mapped);
1321           clutter_actor_set_mapped (self, FALSE);
1322           break;
1323 
1324         case MAP_STATE_MAKE_UNREALIZED:
1325           /* we only use MAKE_UNREALIZED in unparent,
1326            * and unparenting a stage isn't possible.
1327            * If someone wants to just unrealize a stage
1328            * then clutter_actor_unrealize() doesn't
1329            * go through this codepath.
1330            */
1331           g_warning ("Trying to force unrealize stage is not allowed");
1332           break;
1333         }
1334 
1335       if (CLUTTER_ACTOR_IS_MAPPED (self) &&
1336           !CLUTTER_ACTOR_IS_VISIBLE (self) &&
1337           !CLUTTER_ACTOR_IN_DESTRUCTION (self))
1338         {
1339           g_warning ("Clutter toplevel of type '%s' is not visible, but "
1340                      "it is somehow still mapped",
1341                      _clutter_actor_get_debug_name (self));
1342         }
1343     }
1344   else
1345     {
1346       ClutterActorPrivate *priv = self->priv;
1347       ClutterActor *parent = priv->parent;
1348       gboolean should_be_mapped;
1349       gboolean may_be_realized;
1350       gboolean must_be_realized;
1351 
1352       should_be_mapped = FALSE;
1353       may_be_realized = TRUE;
1354       must_be_realized = FALSE;
1355 
1356       if (parent == NULL || change == MAP_STATE_MAKE_UNREALIZED)
1357         {
1358           may_be_realized = FALSE;
1359         }
1360       else
1361         {
1362           /* Maintain invariant that if parent is mapped, and we are
1363            * visible, then we are mapped ...  unless parent is a
1364            * stage, in which case we map regardless of parent's map
1365            * state but do require stage to be visible and realized.
1366            *
1367            * If parent is realized, that does not force us to be
1368            * realized; but if parent is unrealized, that does force
1369            * us to be unrealized.
1370            *
1371            * The reason we don't force children to realize with
1372            * parents is _clutter_actor_rerealize(); if we require that
1373            * a realized parent means children are realized, then to
1374            * unrealize an actor we would have to unrealize its
1375            * parents, which would end up meaning unrealizing and
1376            * hiding the entire stage. So we allow unrealizing a
1377            * child (as long as that child is not mapped) while that
1378            * child still has a realized parent.
1379            *
1380            * Also, if we unrealize from leaf nodes to root, and
1381            * realize from root to leaf, the invariants are never
1382            * violated if we allow children to be unrealized
1383            * while parents are realized.
1384            *
1385            * When unmapping, MAP_STATE_MAKE_UNMAPPED is specified
1386            * to force us to unmap, even though parent is still
1387            * mapped. This is because we're unmapping from leaf nodes
1388            * up to root nodes.
1389            */
1390           if (CLUTTER_ACTOR_IS_VISIBLE (self) &&
1391               change != MAP_STATE_MAKE_UNMAPPED)
1392             {
1393               gboolean parent_is_visible_realized_toplevel;
1394 
1395               parent_is_visible_realized_toplevel =
1396                 (CLUTTER_ACTOR_IS_TOPLEVEL (parent) &&
1397                  CLUTTER_ACTOR_IS_VISIBLE (parent) &&
1398                  CLUTTER_ACTOR_IS_REALIZED (parent));
1399 
1400               if (CLUTTER_ACTOR_IS_MAPPED (parent) ||
1401                   parent_is_visible_realized_toplevel)
1402                 {
1403                   must_be_realized = TRUE;
1404                   should_be_mapped = TRUE;
1405                 }
1406             }
1407 
1408           /* if the actor has been set to be painted even if unmapped
1409            * then we should map it and check for realization as well;
1410            * this is an override for the branch of the scene graph
1411            * which begins with this node
1412            */
1413           if (priv->enable_paint_unmapped)
1414             {
1415               should_be_mapped = TRUE;
1416               must_be_realized = TRUE;
1417             }
1418 
1419           if (!CLUTTER_ACTOR_IS_REALIZED (parent))
1420             may_be_realized = FALSE;
1421         }
1422 
1423       if (change == MAP_STATE_MAKE_MAPPED && !should_be_mapped)
1424         {
1425           if (parent == NULL)
1426             g_warning ("Attempting to map a child that does not "
1427                        "meet the necessary invariants: the actor '%s' "
1428                        "has no parent",
1429                        _clutter_actor_get_debug_name (self));
1430           else
1431             g_warning ("Attempting to map a child that does not "
1432                        "meet the necessary invariants: the actor '%s' "
1433                        "is parented to an unmapped actor '%s'",
1434                        _clutter_actor_get_debug_name (self),
1435                        _clutter_actor_get_debug_name (priv->parent));
1436         }
1437 
1438       /* We want to go in the order "realize, map" and "unmap, unrealize" */
1439 
1440       /* Unmap */
1441       if (!should_be_mapped)
1442         clutter_actor_set_mapped (self, FALSE);
1443 
1444       /* Realize */
1445       if (must_be_realized)
1446         clutter_actor_realize (self);
1447 
1448       /* if we must be realized then we may be, presumably */
1449       g_assert (!(must_be_realized && !may_be_realized));
1450 
1451       /* Unrealize */
1452       if (!may_be_realized)
1453         clutter_actor_unrealize_not_hiding (self);
1454 
1455       /* Map */
1456       if (should_be_mapped)
1457         {
1458           if (!must_be_realized)
1459             g_warning ("Somehow we think actor '%s' should be mapped but "
1460                        "not realized, which isn't allowed",
1461                        _clutter_actor_get_debug_name (self));
1462 
1463           /* realization is allowed to fail (though I don't know what
1464            * an app is supposed to do about that - shouldn't it just
1465            * be a g_error? anyway, we have to avoid mapping if this
1466            * happens)
1467            */
1468           if (CLUTTER_ACTOR_IS_REALIZED (self))
1469             clutter_actor_set_mapped (self, TRUE);
1470         }
1471     }
1472 
1473 #ifdef CLUTTER_ENABLE_DEBUG
1474   /* check all invariants were kept */
1475   clutter_actor_verify_map_state (self);
1476 #endif
1477 }
1478 
1479 static void
queue_update_stage_views(ClutterActor * actor)1480 queue_update_stage_views (ClutterActor *actor)
1481 {
1482   while (actor && !actor->priv->needs_update_stage_views)
1483     {
1484       actor->priv->needs_update_stage_views = TRUE;
1485 
1486       /* We don't really need to update the stage-views of the actors up the
1487        * hierarchy, we set the flag anyway though so we can avoid traversing
1488        * the whole scenegraph when looking for actors which need an update
1489        * in clutter_actor_finish_layout().
1490        */
1491       actor = actor->priv->parent;
1492     }
1493 }
1494 
1495 static void queue_update_paint_volume (ClutterActor *actor);
1496 
1497 static void
queue_update_paint_volume_on_clones(ClutterActor * self)1498 queue_update_paint_volume_on_clones (ClutterActor *self)
1499 {
1500   ClutterActorPrivate *priv = self->priv;
1501   GHashTableIter iter;
1502   gpointer key;
1503 
1504   if (priv->clones == NULL)
1505     return;
1506 
1507   g_hash_table_iter_init (&iter, priv->clones);
1508   while (g_hash_table_iter_next (&iter, &key, NULL))
1509     queue_update_paint_volume (key);
1510 }
1511 
1512 void
queue_update_paint_volume(ClutterActor * actor)1513 queue_update_paint_volume (ClutterActor *actor)
1514 {
1515   queue_update_paint_volume_on_clones (actor);
1516 
1517   while (actor)
1518     {
1519       actor->priv->needs_paint_volume_update = TRUE;
1520       actor = actor->priv->parent;
1521     }
1522 }
1523 
1524 static void
clutter_actor_real_map(ClutterActor * self)1525 clutter_actor_real_map (ClutterActor *self)
1526 {
1527   ClutterActorPrivate *priv = self->priv;
1528   ClutterActor *iter;
1529 
1530   g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
1531 
1532   CLUTTER_NOTE (ACTOR, "Mapping actor '%s'",
1533                 _clutter_actor_get_debug_name (self));
1534 
1535   CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_MAPPED);
1536 
1537   if (priv->unmapped_paint_branch_counter == 0)
1538     {
1539       /* We skip unmapped actors when updating the stage-views list, so if
1540        * an actors list got invalidated while it was unmapped make sure to
1541        * set priv->needs_update_stage_views to TRUE for all actors up the
1542        * hierarchy now.
1543        */
1544       if (priv->needs_update_stage_views)
1545         {
1546           /* Avoid the early return in queue_update_stage_views() */
1547           priv->needs_update_stage_views = FALSE;
1548           queue_update_stage_views (self);
1549         }
1550 
1551       /* Avoid the early return in clutter_actor_queue_relayout() */
1552       priv->needs_width_request = FALSE;
1553       priv->needs_height_request = FALSE;
1554       priv->needs_allocation = FALSE;
1555 
1556       clutter_actor_queue_relayout (self);
1557     }
1558 
1559   /* notify on parent mapped before potentially mapping
1560    * children, so apps see a top-down notification.
1561    */
1562   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]);
1563 
1564   for (iter = priv->first_child;
1565        iter != NULL;
1566        iter = iter->priv->next_sibling)
1567     {
1568       clutter_actor_map (iter);
1569     }
1570 }
1571 
1572 /**
1573  * clutter_actor_map:
1574  * @self: A #ClutterActor
1575  *
1576  * Sets the %CLUTTER_ACTOR_MAPPED flag on the actor and possibly maps
1577  * and realizes its children if they are visible. Does nothing if the
1578  * actor is not visible.
1579  *
1580  * Calling this function is strongly discouraged: the default
1581  * implementation of #ClutterActorClass.map() will map all the children
1582  * of an actor when mapping its parent.
1583  *
1584  * When overriding map, it is mandatory to chain up to the parent
1585  * implementation.
1586  *
1587  * Since: 1.0
1588  */
1589 void
clutter_actor_map(ClutterActor * self)1590 clutter_actor_map (ClutterActor *self)
1591 {
1592   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1593 
1594   if (CLUTTER_ACTOR_IS_MAPPED (self))
1595     return;
1596 
1597   if (!CLUTTER_ACTOR_IS_VISIBLE (self))
1598     return;
1599 
1600   clutter_actor_update_map_state (self, MAP_STATE_MAKE_MAPPED);
1601 }
1602 
1603 /**
1604  * clutter_actor_is_mapped:
1605  * @self: a #ClutterActor
1606  *
1607  * Checks whether a #ClutterActor has been set as mapped.
1608  *
1609  * See also %CLUTTER_ACTOR_IS_MAPPED and #ClutterActor:mapped
1610  *
1611  * Returns: %TRUE if the actor is mapped
1612  *
1613  * Since: 1.24
1614  */
1615 gboolean
clutter_actor_is_mapped(ClutterActor * self)1616 clutter_actor_is_mapped (ClutterActor *self)
1617 {
1618   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
1619 
1620   return CLUTTER_ACTOR_IS_MAPPED (self);
1621 }
1622 
1623 static void
maybe_unset_key_focus(ClutterActor * self)1624 maybe_unset_key_focus (ClutterActor *self)
1625 {
1626   ClutterActor *stage;
1627 
1628   if (!self->priv->has_key_focus)
1629     return;
1630 
1631   stage = _clutter_actor_get_stage_internal (self);
1632 
1633   if (stage)
1634     clutter_stage_set_key_focus (CLUTTER_STAGE (stage), NULL);
1635 }
1636 
1637 static void
clutter_actor_real_unmap(ClutterActor * self)1638 clutter_actor_real_unmap (ClutterActor *self)
1639 {
1640   ClutterActorPrivate *priv = self->priv;
1641   ClutterActor *iter;
1642 
1643   g_assert (CLUTTER_ACTOR_IS_MAPPED (self));
1644 
1645   CLUTTER_NOTE (ACTOR, "Unmapping actor '%s'",
1646                 _clutter_actor_get_debug_name (self));
1647 
1648   for (iter = priv->first_child;
1649        iter != NULL;
1650        iter = iter->priv->next_sibling)
1651     {
1652       clutter_actor_unmap (iter);
1653     }
1654 
1655   CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_MAPPED);
1656 
1657   if (priv->unmapped_paint_branch_counter == 0)
1658     {
1659       /* clear the contents of the last paint volume, so that hiding + moving +
1660        * showing will not result in the wrong area being repainted
1661        */
1662      _clutter_paint_volume_init_static (&priv->last_paint_volume, NULL);
1663       priv->last_paint_volume_valid = TRUE;
1664 
1665       if (priv->parent && !CLUTTER_ACTOR_IN_DESTRUCTION (priv->parent))
1666         {
1667           if (G_UNLIKELY (priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT))
1668             clutter_actor_queue_redraw (priv->parent);
1669           else
1670             clutter_actor_queue_relayout (priv->parent);
1671         }
1672     }
1673 
1674   /* notify on parent mapped after potentially unmapping
1675    * children, so apps see a bottom-up notification.
1676    */
1677   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]);
1678 
1679   /* relinquish keyboard focus if we were unmapped while owning it */
1680   if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
1681     maybe_unset_key_focus (self);
1682 }
1683 
1684 /**
1685  * clutter_actor_unmap:
1686  * @self: A #ClutterActor
1687  *
1688  * Unsets the %CLUTTER_ACTOR_MAPPED flag on the actor and possibly
1689  * unmaps its children if they were mapped.
1690  *
1691  * Calling this function is not encouraged: the default #ClutterActor
1692  * implementation of #ClutterActorClass.unmap() will also unmap any
1693  * eventual children by default when their parent is unmapped.
1694  *
1695  * When overriding #ClutterActorClass.unmap(), it is mandatory to
1696  * chain up to the parent implementation.
1697  *
1698  * It is important to note that the implementation of the
1699  * #ClutterActorClass.unmap() virtual function may be called after
1700  * the #ClutterActorClass.destroy() or the #GObjectClass.dispose()
1701  * implementation, but it is guaranteed to be called before the
1702  * #GObjectClass.finalize() implementation.
1703  *
1704  * Since: 1.0
1705  */
1706 void
clutter_actor_unmap(ClutterActor * self)1707 clutter_actor_unmap (ClutterActor *self)
1708 {
1709   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1710 
1711   if (!CLUTTER_ACTOR_IS_MAPPED (self))
1712     return;
1713 
1714   clutter_actor_update_map_state (self, MAP_STATE_MAKE_UNMAPPED);
1715 }
1716 
1717 static void
clutter_actor_queue_shallow_relayout(ClutterActor * self)1718 clutter_actor_queue_shallow_relayout (ClutterActor *self)
1719 {
1720   ClutterActor *stage = _clutter_actor_get_stage_internal (self);
1721 
1722   if (stage != NULL)
1723     clutter_stage_queue_actor_relayout (CLUTTER_STAGE (stage), self);
1724 }
1725 
1726 static void
clutter_actor_real_show(ClutterActor * self)1727 clutter_actor_real_show (ClutterActor *self)
1728 {
1729   if (CLUTTER_ACTOR_IS_VISIBLE (self))
1730     return;
1731 
1732   CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_VISIBLE);
1733 
1734   /* we notify on the "visible" flag in the clutter_actor_show()
1735    * wrapper so the entire show signal emission completes first,
1736    * and the branch of the scene graph is in a stable state
1737    */
1738   clutter_actor_update_map_state (self, MAP_STATE_CHECK);
1739 }
1740 
1741 static inline void
set_show_on_set_parent(ClutterActor * self,gboolean set_show)1742 set_show_on_set_parent (ClutterActor *self,
1743                         gboolean      set_show)
1744 {
1745   ClutterActorPrivate *priv = self->priv;
1746 
1747   set_show = !!set_show;
1748 
1749   if (priv->show_on_set_parent == set_show)
1750     return;
1751 
1752   if (priv->parent == NULL)
1753     {
1754       priv->show_on_set_parent = set_show;
1755       g_object_notify_by_pspec (G_OBJECT (self),
1756                                 obj_props[PROP_SHOW_ON_SET_PARENT]);
1757     }
1758 }
1759 
1760 static void
clutter_actor_queue_redraw_on_parent(ClutterActor * self)1761 clutter_actor_queue_redraw_on_parent (ClutterActor *self)
1762 {
1763   const ClutterPaintVolume *pv;
1764 
1765   if (!self->priv->parent)
1766     return;
1767 
1768   /* A relayout/redraw is underway */
1769   if (self->priv->needs_allocation)
1770     return;
1771 
1772   pv = clutter_actor_get_transformed_paint_volume (self, self->priv->parent);
1773   _clutter_actor_queue_redraw_full (self->priv->parent, pv, NULL);
1774 }
1775 
1776 /**
1777  * clutter_actor_show:
1778  * @self: A #ClutterActor
1779  *
1780  * Flags an actor to be displayed. An actor that isn't shown will not
1781  * be rendered on the stage.
1782  *
1783  * Actors are visible by default.
1784  *
1785  * If this function is called on an actor without a parent, the
1786  * #ClutterActor:show-on-set-parent will be set to %TRUE as a side
1787  * effect.
1788  */
1789 void
clutter_actor_show(ClutterActor * self)1790 clutter_actor_show (ClutterActor *self)
1791 {
1792   ClutterActorPrivate *priv;
1793 
1794   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1795 
1796   /* simple optimization */
1797   if (CLUTTER_ACTOR_IS_VISIBLE (self))
1798     {
1799       /* we still need to set the :show-on-set-parent property, in
1800        * case show() is called on an unparented actor
1801        */
1802       set_show_on_set_parent (self, TRUE);
1803       return;
1804     }
1805 
1806 #ifdef CLUTTER_ENABLE_DEBUG
1807   clutter_actor_verify_map_state (self);
1808 #endif
1809 
1810   priv = self->priv;
1811 
1812   g_object_freeze_notify (G_OBJECT (self));
1813 
1814   set_show_on_set_parent (self, TRUE);
1815 
1816   /* if we're showing a child that needs to expand, or may
1817    * expand, then we need to recompute the expand flags for
1818    * its parent as well
1819    */
1820   if (priv->needs_compute_expand ||
1821       priv->needs_x_expand ||
1822       priv->needs_y_expand)
1823     {
1824       clutter_actor_queue_compute_expand (self);
1825     }
1826 
1827   g_signal_emit (self, actor_signals[SHOW], 0);
1828   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]);
1829 
1830   if (priv->parent != NULL)
1831     clutter_actor_queue_redraw (self);
1832 
1833   g_object_thaw_notify (G_OBJECT (self));
1834 }
1835 
1836 /**
1837  * clutter_actor_is_visible:
1838  * @self: a #ClutterActor
1839  *
1840  * Checks whether an actor is marked as visible.
1841  *
1842  * See also %CLUTTER_ACTOR_IS_VISIBLE and #ClutterActor:visible.
1843  *
1844  * Returns: %TRUE if the actor visible
1845  *
1846  * Since: 1.24
1847  */
1848 gboolean
clutter_actor_is_visible(ClutterActor * self)1849 clutter_actor_is_visible (ClutterActor *self)
1850 {
1851   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
1852 
1853   return CLUTTER_ACTOR_IS_VISIBLE (self);
1854 }
1855 
1856 static void
clutter_actor_real_hide(ClutterActor * self)1857 clutter_actor_real_hide (ClutterActor *self)
1858 {
1859   if (!CLUTTER_ACTOR_IS_VISIBLE (self))
1860     return;
1861 
1862   CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_VISIBLE);
1863 
1864   /* we notify on the "visible" flag in the clutter_actor_hide()
1865    * wrapper so the entire hide signal emission completes first,
1866    * and the branch of the scene graph is in a stable state
1867    */
1868   clutter_actor_update_map_state (self, MAP_STATE_CHECK);
1869 }
1870 
1871 /**
1872  * clutter_actor_hide:
1873  * @self: A #ClutterActor
1874  *
1875  * Flags an actor to be hidden. A hidden actor will not be
1876  * rendered on the stage.
1877  *
1878  * Actors are visible by default.
1879  *
1880  * If this function is called on an actor without a parent, the
1881  * #ClutterActor:show-on-set-parent property will be set to %FALSE
1882  * as a side-effect.
1883  */
1884 void
clutter_actor_hide(ClutterActor * self)1885 clutter_actor_hide (ClutterActor *self)
1886 {
1887   ClutterActorPrivate *priv;
1888 
1889   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1890 
1891   /* simple optimization */
1892   if (!CLUTTER_ACTOR_IS_VISIBLE (self))
1893     {
1894       /* we still need to set the :show-on-set-parent property, in
1895        * case hide() is called on an unparented actor
1896        */
1897       set_show_on_set_parent (self, FALSE);
1898       return;
1899     }
1900 
1901 #ifdef CLUTTER_ENABLE_DEBUG
1902   clutter_actor_verify_map_state (self);
1903 #endif
1904 
1905   priv = self->priv;
1906 
1907   g_object_freeze_notify (G_OBJECT (self));
1908 
1909   set_show_on_set_parent (self, FALSE);
1910 
1911   /* if we're hiding a child that needs to expand, or may
1912    * expand, then we need to recompute the expand flags for
1913    * its parent as well
1914    */
1915   if (priv->needs_compute_expand ||
1916       priv->needs_x_expand ||
1917       priv->needs_y_expand)
1918     {
1919       clutter_actor_queue_compute_expand (self);
1920     }
1921 
1922   g_signal_emit (self, actor_signals[HIDE], 0);
1923   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]);
1924 
1925   if (priv->parent != NULL && priv->needs_allocation)
1926     clutter_actor_queue_redraw (priv->parent);
1927   else
1928     clutter_actor_queue_redraw_on_parent (self);
1929 
1930   g_object_thaw_notify (G_OBJECT (self));
1931 }
1932 
1933 /**
1934  * clutter_actor_realize:
1935  * @self: A #ClutterActor
1936  *
1937  * Realization informs the actor that it is attached to a stage. It
1938  * can use this to allocate resources if it wanted to delay allocation
1939  * until it would be rendered. However it is perfectly acceptable for
1940  * an actor to create resources before being realized because Clutter
1941  * only ever has a single rendering context so that actor is free to
1942  * be moved from one stage to another.
1943  *
1944  * This function does nothing if the actor is already realized.
1945  *
1946  * Because a realized actor must have realized parent actors, calling
1947  * clutter_actor_realize() will also realize all parents of the actor.
1948  *
1949  * This function does not realize child actors, except in the special
1950  * case that realizing the stage, when the stage is visible, will
1951  * suddenly map (and thus realize) the children of the stage.
1952  *
1953  * Deprecated: 1.16: Actors are automatically realized, and nothing
1954  *   requires explicit realization.
1955  */
1956 void
clutter_actor_realize(ClutterActor * self)1957 clutter_actor_realize (ClutterActor *self)
1958 {
1959   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1960 
1961   clutter_actor_realize_internal (self);
1962 }
1963 
1964 /**
1965  * clutter_actor_is_realized:
1966  * @self: a #ClutterActor
1967  *
1968  * Checks whether a #ClutterActor is realized.
1969  *
1970  * See also %CLUTTER_ACTOR_IS_REALIZED and #ClutterActor:realized.
1971  *
1972  * Returns: %TRUE if the actor is realized
1973  *
1974  * Since: 1.24
1975  */
1976 gboolean
clutter_actor_is_realized(ClutterActor * self)1977 clutter_actor_is_realized (ClutterActor *self)
1978 {
1979   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
1980 
1981   return CLUTTER_ACTOR_IS_REALIZED (self);
1982 }
1983 
1984 static void
clutter_actor_realize_internal(ClutterActor * self)1985 clutter_actor_realize_internal (ClutterActor *self)
1986 {
1987   ClutterActorPrivate *priv = self->priv;
1988 
1989 #ifdef CLUTTER_ENABLE_DEBUG
1990   clutter_actor_verify_map_state (self);
1991 #endif
1992 
1993   if (CLUTTER_ACTOR_IS_REALIZED (self))
1994     return;
1995 
1996   /* To be realized, our parent actors must be realized first.
1997    * This will only succeed if we're inside a toplevel.
1998    */
1999   if (priv->parent != NULL)
2000     clutter_actor_realize (priv->parent);
2001 
2002   if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
2003     {
2004       /* toplevels can be realized at any time */
2005     }
2006   else
2007     {
2008       /* "Fail" the realization if parent is missing or unrealized;
2009        * this should really be a g_warning() not some kind of runtime
2010        * failure; how can an app possibly recover? Instead it's a bug
2011        * in the app and the app should get an explanatory warning so
2012        * someone can fix it. But for now it's too hard to fix this
2013        * because e.g. ClutterTexture needs reworking.
2014        */
2015       if (priv->parent == NULL ||
2016           !CLUTTER_ACTOR_IS_REALIZED (priv->parent))
2017         return;
2018     }
2019 
2020   CLUTTER_NOTE (ACTOR, "Realizing actor '%s'", _clutter_actor_get_debug_name (self));
2021 
2022   CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
2023   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]);
2024 
2025   g_signal_emit (self, actor_signals[REALIZE], 0);
2026 
2027   /* Stage actor is allowed to unset the realized flag again in its
2028    * default signal handler, though that is a pathological situation.
2029    */
2030 
2031   /* If realization "failed" we'll have to update child state. */
2032   clutter_actor_update_map_state (self, MAP_STATE_CHECK);
2033 }
2034 
2035 static void
clutter_actor_real_unrealize(ClutterActor * self)2036 clutter_actor_real_unrealize (ClutterActor *self)
2037 {
2038   /* we must be unmapped (implying our children are also unmapped) */
2039   g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
2040 }
2041 
2042 /**
2043  * clutter_actor_unrealize:
2044  * @self: A #ClutterActor
2045  *
2046  * Unrealization informs the actor that it may be being destroyed or
2047  * moved to another stage. The actor may want to destroy any
2048  * underlying graphics resources at this point. However it is
2049  * perfectly acceptable for it to retain the resources until the actor
2050  * is destroyed because Clutter only ever uses a single rendering
2051  * context and all of the graphics resources are valid on any stage.
2052  *
2053  * Because mapped actors must be realized, actors may not be
2054  * unrealized if they are mapped. This function hides the actor to be
2055  * sure it isn't mapped, an application-visible side effect that you
2056  * may not be expecting.
2057  *
2058  * This function should not be called by application code.
2059  *
2060  * This function should not really be in the public API, because
2061  * there isn't a good reason to call it. ClutterActor will already
2062  * unrealize things for you when it's important to do so.
2063  *
2064  * If you were using clutter_actor_unrealize() in a dispose
2065  * implementation, then don't, just chain up to ClutterActor's
2066  * dispose.
2067  *
2068  * If you were using clutter_actor_unrealize() to implement
2069  * unrealizing children of your container, then don't, ClutterActor
2070  * will already take care of that.
2071  *
2072  * Deprecated: 1.16: Actors are automatically unrealized, and nothing
2073  *   requires explicit realization.
2074  */
2075 void
clutter_actor_unrealize(ClutterActor * self)2076 clutter_actor_unrealize (ClutterActor *self)
2077 {
2078   g_return_if_fail (CLUTTER_IS_ACTOR (self));
2079   g_return_if_fail (!CLUTTER_ACTOR_IS_MAPPED (self));
2080 
2081   clutter_actor_unrealize_internal (self);
2082 }
2083 
2084 /* If you were using clutter_actor_unrealize() to re-realize to
2085  * create your resources in a different way, then use
2086  * _clutter_actor_rerealize() (inside Clutter) or just call your
2087  * code that recreates your resources directly (outside Clutter).
2088  */
2089 static void
clutter_actor_unrealize_internal(ClutterActor * self)2090 clutter_actor_unrealize_internal (ClutterActor *self)
2091 {
2092 #ifdef CLUTTER_ENABLE_DEBUG
2093   clutter_actor_verify_map_state (self);
2094 #endif
2095 
2096   clutter_actor_hide (self);
2097 
2098   clutter_actor_unrealize_not_hiding (self);
2099 }
2100 
2101 static ClutterActorTraverseVisitFlags
unrealize_actor_before_children_cb(ClutterActor * self,int depth,void * user_data)2102 unrealize_actor_before_children_cb (ClutterActor *self,
2103                                     int depth,
2104                                     void *user_data)
2105 {
2106   /* If an actor is already unrealized we know its children have also
2107    * already been unrealized... */
2108   if (!CLUTTER_ACTOR_IS_REALIZED (self))
2109     return CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN;
2110 
2111   g_signal_emit (self, actor_signals[UNREALIZE], 0);
2112 
2113   return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
2114 }
2115 
2116 static ClutterActorTraverseVisitFlags
unrealize_actor_after_children_cb(ClutterActor * self,int depth,void * user_data)2117 unrealize_actor_after_children_cb (ClutterActor *self,
2118                                    int depth,
2119                                    void *user_data)
2120 {
2121   ClutterActorPrivate *priv = self->priv;
2122   ClutterActor *stage = user_data;
2123 
2124   /* We want to unset the realized flag only _after_
2125    * child actors are unrealized, to maintain invariants.
2126    */
2127   CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
2128   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]);
2129 
2130   if (stage != NULL &&
2131       priv->parent != NULL &&
2132       priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT)
2133     clutter_stage_dequeue_actor_relayout (CLUTTER_STAGE (stage), self);
2134 
2135   if (stage != NULL)
2136     clutter_stage_dequeue_actor_redraw (CLUTTER_STAGE (stage), self);
2137 
2138   if (priv->unmapped_paint_branch_counter == 0)
2139     priv->allocation = (ClutterActorBox) CLUTTER_ACTOR_BOX_UNINITIALIZED;
2140 
2141   return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
2142 }
2143 
2144 /*
2145  * clutter_actor_unrealize_not_hiding:
2146  * @self: A #ClutterActor
2147  *
2148  * Unrealization informs the actor that it may be being destroyed or
2149  * moved to another stage. The actor may want to destroy any
2150  * underlying graphics resources at this point. However it is
2151  * perfectly acceptable for it to retain the resources until the actor
2152  * is destroyed because Clutter only ever uses a single rendering
2153  * context and all of the graphics resources are valid on any stage.
2154  *
2155  * Because mapped actors must be realized, actors may not be
2156  * unrealized if they are mapped. You must hide the actor or one of
2157  * its parents before attempting to unrealize.
2158  *
2159  * This function is separate from clutter_actor_unrealize() because it
2160  * does not automatically hide the actor.
2161  * Actors need not be hidden to be unrealized, they just need to
2162  * be unmapped. In fact we don't want to mess up the application's
2163  * setting of the "visible" flag, so hiding is very undesirable.
2164  *
2165  * clutter_actor_unrealize() does a clutter_actor_hide() just for
2166  * backward compatibility.
2167  */
2168 static void
clutter_actor_unrealize_not_hiding(ClutterActor * self)2169 clutter_actor_unrealize_not_hiding (ClutterActor *self)
2170 {
2171   ClutterActor *stage = _clutter_actor_get_stage_internal (self);
2172 
2173   _clutter_actor_traverse (self,
2174                            CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST,
2175                            unrealize_actor_before_children_cb,
2176                            unrealize_actor_after_children_cb,
2177                            stage);
2178 }
2179 
2180 /*
2181  * _clutter_actor_rerealize:
2182  * @self: A #ClutterActor
2183  * @callback: Function to call while unrealized
2184  * @data: data for callback
2185  *
2186  * If an actor is already unrealized, this just calls the callback.
2187  *
2188  * If it is realized, it unrealizes temporarily, calls the callback,
2189  * and then re-realizes the actor.
2190  *
2191  * As a side effect, leaves all children of the actor unrealized if
2192  * the actor was realized but not showing.  This is because when we
2193  * unrealize the actor temporarily we must unrealize its children
2194  * (e.g. children of a stage can't be realized if stage window is
2195  * gone). And we aren't clever enough to save the realization state of
2196  * all children. In most cases this should not matter, because
2197  * the children will automatically realize when they next become mapped.
2198  */
2199 void
_clutter_actor_rerealize(ClutterActor * self,ClutterCallback callback,void * data)2200 _clutter_actor_rerealize (ClutterActor    *self,
2201                           ClutterCallback  callback,
2202                           void            *data)
2203 {
2204   gboolean was_mapped;
2205   gboolean was_showing;
2206   gboolean was_realized;
2207 
2208   g_return_if_fail (CLUTTER_IS_ACTOR (self));
2209 
2210 #ifdef CLUTTER_ENABLE_DEBUG
2211   clutter_actor_verify_map_state (self);
2212 #endif
2213 
2214   was_realized = CLUTTER_ACTOR_IS_REALIZED (self);
2215   was_mapped = CLUTTER_ACTOR_IS_MAPPED (self);
2216   was_showing = CLUTTER_ACTOR_IS_VISIBLE (self);
2217 
2218   /* Must be unmapped to unrealize. Note we only have to hide this
2219    * actor if it was mapped (if all parents were showing).  If actor
2220    * is merely visible (but not mapped), then that's fine, we can
2221    * leave it visible.
2222    */
2223   if (was_mapped)
2224     clutter_actor_hide (self);
2225 
2226   g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
2227 
2228   /* unrealize self and all children */
2229   clutter_actor_unrealize_not_hiding (self);
2230 
2231   if (callback != NULL)
2232     {
2233       (* callback) (self, data);
2234     }
2235 
2236   if (was_showing)
2237     clutter_actor_show (self); /* will realize only if mapping implies it */
2238   else if (was_realized)
2239     clutter_actor_realize (self); /* realize self and all parents */
2240 }
2241 
2242 static void
clutter_actor_real_pick(ClutterActor * self,ClutterPickContext * pick_context)2243 clutter_actor_real_pick (ClutterActor       *self,
2244                          ClutterPickContext *pick_context)
2245 {
2246   ClutterActorPrivate *priv = self->priv;
2247 
2248   if (clutter_actor_should_pick (self, pick_context))
2249     {
2250       ClutterActorBox box = {
2251         .x1 = 0,
2252         .y1 = 0,
2253         .x2 = priv->allocation.x2 - priv->allocation.x1,
2254         .y2 = priv->allocation.y2 - priv->allocation.y1,
2255       };
2256 
2257       clutter_actor_pick_box (self, pick_context, &box);
2258     }
2259 
2260   /* XXX - this thoroughly sucks, but we need to maintain compatibility
2261    * with existing container classes that override the pick() virtual
2262    * and chain up to the default implementation - otherwise we'll end up
2263    * painting our children twice.
2264    *
2265    * this has to go away for 2.0; hopefully along the pick() itself.
2266    */
2267   if (CLUTTER_ACTOR_GET_CLASS (self)->pick == clutter_actor_real_pick)
2268     {
2269       ClutterActor *iter;
2270 
2271       for (iter = self->priv->first_child;
2272            iter != NULL;
2273            iter = iter->priv->next_sibling)
2274         clutter_actor_pick (iter, pick_context);
2275     }
2276 }
2277 
2278 /**
2279  * clutter_actor_should_pick:
2280  * @self: A #ClutterActor
2281  * @pick_context: a #ClutterPickContext
2282  *
2283  * Should be called inside the implementation of the
2284  * #ClutterActor::pick virtual function in order to check whether
2285  * the actor should be picked or not.
2286  *
2287  * This function should never be called directly by applications.
2288  *
2289  * Return value: %TRUE if the actor should be picked, %FALSE otherwise
2290  */
2291 gboolean
clutter_actor_should_pick(ClutterActor * self,ClutterPickContext * pick_context)2292 clutter_actor_should_pick (ClutterActor       *self,
2293                            ClutterPickContext *pick_context)
2294 {
2295   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
2296 
2297   if (CLUTTER_ACTOR_IS_MAPPED (self) &&
2298       clutter_actor_box_is_initialized (&self->priv->allocation) &&
2299       (clutter_pick_context_get_mode (pick_context) == CLUTTER_PICK_ALL ||
2300        CLUTTER_ACTOR_IS_REACTIVE (self)))
2301     return TRUE;
2302 
2303   return FALSE;
2304 }
2305 
2306 static void
clutter_actor_real_get_preferred_width(ClutterActor * self,gfloat for_height,gfloat * min_width_p,gfloat * natural_width_p)2307 clutter_actor_real_get_preferred_width (ClutterActor *self,
2308                                         gfloat        for_height,
2309                                         gfloat       *min_width_p,
2310                                         gfloat       *natural_width_p)
2311 {
2312   ClutterActorPrivate *priv = self->priv;
2313 
2314   if (priv->layout_manager != NULL)
2315     {
2316       ClutterContainer *container = CLUTTER_CONTAINER (self);
2317 
2318       CLUTTER_NOTE (LAYOUT, "Querying the layout manager '%s'[%p] "
2319                     "for the preferred width",
2320                     G_OBJECT_TYPE_NAME (priv->layout_manager),
2321                     priv->layout_manager);
2322 
2323       clutter_layout_manager_get_preferred_width (priv->layout_manager,
2324                                                   container,
2325                                                   for_height,
2326                                                   min_width_p,
2327                                                   natural_width_p);
2328 
2329       return;
2330     }
2331 
2332   /* Default implementation is always 0x0, usually an actor
2333    * using this default is relying on someone to set the
2334    * request manually
2335    */
2336   CLUTTER_NOTE (LAYOUT, "Default preferred width: 0, 0");
2337 
2338   if (min_width_p)
2339     *min_width_p = 0;
2340 
2341   if (natural_width_p)
2342     *natural_width_p = 0;
2343 }
2344 
2345 static void
clutter_actor_real_get_preferred_height(ClutterActor * self,gfloat for_width,gfloat * min_height_p,gfloat * natural_height_p)2346 clutter_actor_real_get_preferred_height (ClutterActor *self,
2347                                          gfloat        for_width,
2348                                          gfloat       *min_height_p,
2349                                          gfloat       *natural_height_p)
2350 {
2351   ClutterActorPrivate *priv = self->priv;
2352 
2353   if (priv->layout_manager != NULL)
2354     {
2355       ClutterContainer *container = CLUTTER_CONTAINER (self);
2356 
2357       CLUTTER_NOTE (LAYOUT, "Querying the layout manager '%s'[%p] "
2358                     "for the preferred height",
2359                     G_OBJECT_TYPE_NAME (priv->layout_manager),
2360                     priv->layout_manager);
2361 
2362       clutter_layout_manager_get_preferred_height (priv->layout_manager,
2363                                                    container,
2364                                                    for_width,
2365                                                    min_height_p,
2366                                                    natural_height_p);
2367 
2368       return;
2369     }
2370   /* Default implementation is always 0x0, usually an actor
2371    * using this default is relying on someone to set the
2372    * request manually
2373    */
2374   CLUTTER_NOTE (LAYOUT, "Default preferred height: 0, 0");
2375 
2376   if (min_height_p)
2377     *min_height_p = 0;
2378 
2379   if (natural_height_p)
2380     *natural_height_p = 0;
2381 }
2382 
2383 static void
clutter_actor_store_old_geometry(ClutterActor * self,ClutterActorBox * box)2384 clutter_actor_store_old_geometry (ClutterActor    *self,
2385                                   ClutterActorBox *box)
2386 {
2387   *box = self->priv->allocation;
2388 }
2389 
2390 static inline void
clutter_actor_notify_if_geometry_changed(ClutterActor * self,const ClutterActorBox * old)2391 clutter_actor_notify_if_geometry_changed (ClutterActor          *self,
2392                                           const ClutterActorBox *old)
2393 {
2394   ClutterActorPrivate *priv = self->priv;
2395   GObject *obj = G_OBJECT (self);
2396 
2397   g_object_freeze_notify (obj);
2398 
2399   /* to avoid excessive requisition or allocation cycles we
2400    * use the cached values.
2401    *
2402    * - if we don't have an allocation we assume that we need
2403    *   to notify anyway
2404    * - if we don't have a width or a height request we notify
2405    *   width and height
2406    * - if we have a valid allocation then we check the old
2407    *   bounding box with the current allocation and we notify
2408    *   the changes
2409    */
2410   if (priv->needs_allocation)
2411     {
2412       g_object_notify_by_pspec (obj, obj_props[PROP_X]);
2413       g_object_notify_by_pspec (obj, obj_props[PROP_Y]);
2414       g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
2415       g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
2416       g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
2417       g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2418     }
2419   else if (priv->needs_width_request || priv->needs_height_request)
2420     {
2421       g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
2422       g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
2423       g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2424     }
2425   else
2426     {
2427       gfloat x, y;
2428       gfloat width, height;
2429 
2430       x = priv->allocation.x1;
2431       y = priv->allocation.y1;
2432       width = priv->allocation.x2 - priv->allocation.x1;
2433       height = priv->allocation.y2 - priv->allocation.y1;
2434 
2435       if (x != old->x1)
2436         {
2437           g_object_notify_by_pspec (obj, obj_props[PROP_X]);
2438           g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
2439         }
2440 
2441       if (y != old->y1)
2442         {
2443           g_object_notify_by_pspec (obj, obj_props[PROP_Y]);
2444           g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
2445         }
2446 
2447       if (width != (old->x2 - old->x1))
2448         {
2449           g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
2450           g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2451         }
2452 
2453       if (height != (old->y2 - old->y1))
2454         {
2455           g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
2456           g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2457         }
2458     }
2459 
2460   g_object_thaw_notify (obj);
2461 }
2462 
2463 static void
absolute_geometry_changed(ClutterActor * actor)2464 absolute_geometry_changed (ClutterActor *actor)
2465 {
2466   queue_update_stage_views (actor);
2467 }
2468 
2469 static ClutterActorTraverseVisitFlags
absolute_geometry_changed_cb(ClutterActor * actor,int depth,gpointer user_data)2470 absolute_geometry_changed_cb (ClutterActor *actor,
2471                               int           depth,
2472                               gpointer      user_data)
2473 {
2474   absolute_geometry_changed (actor);
2475 
2476   return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
2477 }
2478 
2479 static void
transform_changed(ClutterActor * actor)2480 transform_changed (ClutterActor *actor)
2481 {
2482   actor->priv->transform_valid = FALSE;
2483 
2484   if (actor->priv->parent)
2485     queue_update_paint_volume (actor->priv->parent);
2486 
2487   _clutter_actor_traverse (actor,
2488                            CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST,
2489                            absolute_geometry_changed_cb,
2490                            NULL,
2491                            NULL);
2492 }
2493 
2494 /*< private >
2495  * clutter_actor_set_allocation_internal:
2496  * @self: a #ClutterActor
2497  * @box: a #ClutterActorBox
2498  * @flags: allocation flags
2499  *
2500  * Stores the allocation of @self.
2501  *
2502  * This function only performs basic storage and property notification.
2503  *
2504  * This function should be called by clutter_actor_set_allocation()
2505  * and by the default implementation of #ClutterActorClass.allocate().
2506  *
2507  * Return value: %TRUE if the allocation of the #ClutterActor has been
2508  *   changed, and %FALSE otherwise
2509  */
2510 static inline void
clutter_actor_set_allocation_internal(ClutterActor * self,const ClutterActorBox * box)2511 clutter_actor_set_allocation_internal (ClutterActor           *self,
2512                                        const ClutterActorBox  *box)
2513 {
2514   ClutterActorPrivate *priv = self->priv;
2515   GObject *obj;
2516   gboolean origin_changed, size_changed;
2517   ClutterActorBox old_alloc = { 0, };
2518 
2519   g_return_if_fail (!isnan (box->x1) && !isnan (box->x2) &&
2520                     !isnan (box->y1) && !isnan (box->y2));
2521 
2522   obj = G_OBJECT (self);
2523 
2524   g_object_freeze_notify (obj);
2525 
2526   clutter_actor_store_old_geometry (self, &old_alloc);
2527 
2528   origin_changed =
2529     priv->allocation.x1 != box->x1 || priv->allocation.y1 != box->y1;
2530   size_changed =
2531     priv->allocation.x2 - priv->allocation.x1 != box->x2 - box->x1 ||
2532     priv->allocation.y2 - priv->allocation.y1 != box->y2 - box->y1;
2533 
2534   priv->allocation = *box;
2535 
2536   /* allocation is authoritative */
2537   priv->needs_width_request = FALSE;
2538   priv->needs_height_request = FALSE;
2539   priv->needs_allocation = FALSE;
2540 
2541   if (origin_changed || size_changed)
2542     {
2543       CLUTTER_NOTE (LAYOUT, "Allocation for '%s' changed",
2544                     _clutter_actor_get_debug_name (self));
2545 
2546       /* This will also call absolute_geometry_changed() on the subtree */
2547       transform_changed (self);
2548 
2549       if (size_changed)
2550         queue_update_paint_volume (self);
2551 
2552       g_object_notify_by_pspec (obj, obj_props[PROP_ALLOCATION]);
2553 
2554       /* if the allocation changes, so does the content box */
2555       if (priv->content != NULL)
2556         {
2557           priv->content_box_valid = FALSE;
2558           g_object_notify_by_pspec (obj, obj_props[PROP_CONTENT_BOX]);
2559         }
2560     }
2561 
2562   clutter_actor_notify_if_geometry_changed (self, &old_alloc);
2563 
2564   g_object_thaw_notify (obj);
2565 }
2566 
2567 static void
clutter_actor_real_allocate(ClutterActor * self,const ClutterActorBox * box)2568 clutter_actor_real_allocate (ClutterActor           *self,
2569                              const ClutterActorBox  *box)
2570 {
2571   ClutterActorPrivate *priv = self->priv;
2572 
2573   g_object_freeze_notify (G_OBJECT (self));
2574 
2575   clutter_actor_set_allocation_internal (self, box);
2576 
2577   /* we allocate our children before we notify changes in our geometry,
2578    * so that people connecting to properties will be able to get valid
2579    * data out of the sub-tree of the scene graph that has this actor at
2580    * the root.
2581    */
2582   if (priv->n_children != 0 &&
2583       priv->layout_manager != NULL)
2584     {
2585       ClutterActorBox children_box;
2586 
2587       /* normalize the box passed to the layout manager */
2588       children_box.x1 = children_box.y1 = 0.f;
2589       children_box.x2 = box->x2 - box->x1;
2590       children_box.y2 = box->y2 - box->y1;
2591 
2592       CLUTTER_NOTE (LAYOUT,
2593                     "Allocating %d children of %s "
2594                     "at { %.2f, %.2f - %.2f x %.2f } "
2595                     "using %s",
2596                     priv->n_children,
2597                     _clutter_actor_get_debug_name (self),
2598                     box->x1,
2599                     box->y1,
2600                     (box->x2 - box->x1),
2601                     (box->y2 - box->y1),
2602                     G_OBJECT_TYPE_NAME (priv->layout_manager));
2603 
2604       clutter_layout_manager_allocate (priv->layout_manager,
2605                                        CLUTTER_CONTAINER (self),
2606                                        &children_box);
2607     }
2608 
2609   g_object_thaw_notify (G_OBJECT (self));
2610 }
2611 
2612 static void
_clutter_actor_queue_redraw_on_clones(ClutterActor * self)2613 _clutter_actor_queue_redraw_on_clones (ClutterActor *self)
2614 {
2615   ClutterActorPrivate *priv = self->priv;
2616   GHashTableIter iter;
2617   gpointer key;
2618 
2619   if (priv->clones == NULL)
2620     return;
2621 
2622   g_hash_table_iter_init (&iter, priv->clones);
2623   while (g_hash_table_iter_next (&iter, &key, NULL))
2624     clutter_actor_queue_redraw (key);
2625 }
2626 
2627 static void
_clutter_actor_propagate_queue_redraw(ClutterActor * self,ClutterActor * origin)2628 _clutter_actor_propagate_queue_redraw (ClutterActor *self,
2629                                        ClutterActor *origin)
2630 {
2631   while (self)
2632     {
2633       /* no point in queuing a redraw on a destroyed actor */
2634       if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
2635         break;
2636 
2637       _clutter_actor_queue_redraw_on_clones (self);
2638 
2639       /* If the queue redraw is coming from a child then the actor has
2640          become dirty and any queued effect is no longer valid */
2641       if (self != origin)
2642         {
2643           self->priv->is_dirty = TRUE;
2644           self->priv->effect_to_redraw = NULL;
2645         }
2646 
2647       /* If the actor isn't visible, we still had to emit the signal
2648        * to allow for a ClutterClone, but the appearance of the parent
2649        * won't change so we don't have to propagate up the hierarchy.
2650        */
2651       if (!CLUTTER_ACTOR_IS_VISIBLE (self))
2652         break;
2653 
2654       /* We guarantee that we will propagate a queue-redraw up the tree
2655        * at least once so that all clones can get notified.
2656        */
2657       if (self->priv->propagated_one_redraw)
2658         break;
2659 
2660       self->priv->propagated_one_redraw = TRUE;
2661 
2662       self = self->priv->parent;
2663     }
2664 }
2665 
2666 static inline gboolean
clutter_actor_needs_relayout(ClutterActor * self)2667 clutter_actor_needs_relayout (ClutterActor *self)
2668 {
2669   ClutterActorPrivate *priv = self->priv;
2670 
2671   return (priv->needs_width_request ||
2672           priv->needs_height_request ||
2673           priv->needs_allocation);
2674 }
2675 
2676 static void
clutter_actor_real_queue_relayout(ClutterActor * self)2677 clutter_actor_real_queue_relayout (ClutterActor *self)
2678 {
2679   ClutterActorPrivate *priv = self->priv;
2680 
2681   /* no point in queueing a redraw on a destroyed actor */
2682   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
2683     return;
2684 
2685   priv->needs_width_request  = TRUE;
2686   priv->needs_height_request = TRUE;
2687   priv->needs_allocation     = TRUE;
2688 
2689   /* reset the cached size requests */
2690   memset (priv->width_requests, 0,
2691           N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest));
2692   memset (priv->height_requests, 0,
2693           N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest));
2694 
2695   /* We may need to go all the way up the hierarchy */
2696   if (priv->parent != NULL)
2697     {
2698       if (priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT)
2699         clutter_actor_queue_shallow_relayout (self);
2700       else
2701         _clutter_actor_queue_only_relayout (priv->parent);
2702     }
2703 }
2704 
2705 /**
2706  * clutter_actor_apply_relative_transform_to_point:
2707  * @self: A #ClutterActor
2708  * @ancestor: (allow-none): A #ClutterActor ancestor, or %NULL to use the
2709  *   default #ClutterStage
2710  * @point: A point as #graphene_point3d_t
2711  * @vertex: (out caller-allocates): The translated #graphene_point3d_t
2712  *
2713  * Transforms @point in coordinates relative to the actor into
2714  * ancestor-relative coordinates using the relevant transform
2715  * stack (i.e. scale, rotation, etc).
2716  *
2717  * If @ancestor is %NULL the ancestor will be the #ClutterStage. In
2718  * this case, the coordinates returned will be the coordinates on
2719  * the stage before the projection is applied. This is different from
2720  * the behaviour of clutter_actor_apply_transform_to_point().
2721  *
2722  * Since: 0.6
2723  */
2724 void
clutter_actor_apply_relative_transform_to_point(ClutterActor * self,ClutterActor * ancestor,const graphene_point3d_t * point,graphene_point3d_t * vertex)2725 clutter_actor_apply_relative_transform_to_point (ClutterActor             *self,
2726                                                  ClutterActor             *ancestor,
2727                                                  const graphene_point3d_t *point,
2728                                                  graphene_point3d_t       *vertex)
2729 {
2730   gfloat w;
2731   graphene_matrix_t matrix;
2732 
2733   g_return_if_fail (CLUTTER_IS_ACTOR (self));
2734   g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor));
2735   g_return_if_fail (point != NULL);
2736   g_return_if_fail (vertex != NULL);
2737 
2738   *vertex = *point;
2739   w = 1.0;
2740 
2741   if (ancestor == NULL)
2742     ancestor = _clutter_actor_get_stage_internal (self);
2743 
2744   if (ancestor == NULL)
2745     {
2746       *vertex = *point;
2747       return;
2748     }
2749 
2750   _clutter_actor_get_relative_transformation_matrix (self, ancestor, &matrix);
2751   cogl_graphene_matrix_project_point (&matrix,
2752                                       &vertex->x,
2753                                       &vertex->y,
2754                                       &vertex->z,
2755                                       &w);
2756 }
2757 
2758 static gboolean
_clutter_actor_fully_transform_vertices(ClutterActor * self,const graphene_point3d_t * vertices_in,graphene_point3d_t * vertices_out,int n_vertices)2759 _clutter_actor_fully_transform_vertices (ClutterActor             *self,
2760                                          const graphene_point3d_t *vertices_in,
2761                                          graphene_point3d_t       *vertices_out,
2762                                          int                       n_vertices)
2763 {
2764   ClutterActor *stage;
2765   graphene_matrix_t modelview;
2766   graphene_matrix_t projection;
2767   float viewport[4];
2768 
2769   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
2770 
2771   stage = _clutter_actor_get_stage_internal (self);
2772 
2773   /* We really can't do anything meaningful in this case so don't try
2774    * to do any transform */
2775   if (stage == NULL)
2776     return FALSE;
2777 
2778   /* Note: we pass NULL as the ancestor because we don't just want the modelview
2779    * that gets us to stage coordinates, we want to go all the way to eye
2780    * coordinates */
2781   _clutter_actor_get_relative_transformation_matrix (self, NULL, &modelview);
2782 
2783   /* Fetch the projection and viewport */
2784   _clutter_stage_get_projection_matrix (CLUTTER_STAGE (stage), &projection);
2785   _clutter_stage_get_viewport (CLUTTER_STAGE (stage),
2786                                &viewport[0],
2787                                &viewport[1],
2788                                &viewport[2],
2789                                &viewport[3]);
2790 
2791   _clutter_util_fully_transform_vertices (&modelview,
2792                                           &projection,
2793                                           viewport,
2794                                           vertices_in,
2795                                           vertices_out,
2796                                           n_vertices);
2797 
2798   return TRUE;
2799 }
2800 
2801 /**
2802  * clutter_actor_apply_transform_to_point:
2803  * @self: A #ClutterActor
2804  * @point: A point as #graphene_point3d_t
2805  * @vertex: (out caller-allocates): The translated #graphene_point3d_t
2806  *
2807  * Transforms @point in coordinates relative to the actor
2808  * into screen-relative coordinates with the current actor
2809  * transformation (i.e. scale, rotation, etc)
2810  *
2811  * Since: 0.4
2812  **/
2813 void
clutter_actor_apply_transform_to_point(ClutterActor * self,const graphene_point3d_t * point,graphene_point3d_t * vertex)2814 clutter_actor_apply_transform_to_point (ClutterActor             *self,
2815                                         const graphene_point3d_t *point,
2816                                         graphene_point3d_t       *vertex)
2817 {
2818   g_return_if_fail (point != NULL);
2819   g_return_if_fail (vertex != NULL);
2820   _clutter_actor_fully_transform_vertices (self, point, vertex, 1);
2821 }
2822 
2823 /*
2824  * _clutter_actor_get_relative_transformation_matrix:
2825  * @self: The actor whose coordinate space you want to transform from.
2826  * @ancestor: The ancestor actor whose coordinate space you want to transform too
2827  *            or %NULL if you want to transform all the way to eye coordinates.
2828  * @matrix: A #graphene_matrix_t to store the transformation
2829  *
2830  * This gets a transformation @matrix that will transform coordinates from the
2831  * coordinate space of @self into the coordinate space of @ancestor.
2832  *
2833  * For example if you need a matrix that can transform the local actor
2834  * coordinates of @self into stage coordinates you would pass the actor's stage
2835  * pointer as the @ancestor.
2836  *
2837  * If you pass %NULL then the transformation will take you all the way through
2838  * to eye coordinates. This can be useful if you want to extract the entire
2839  * modelview transform that Clutter applies before applying the projection
2840  * transformation. If you want to explicitly set a modelview on a CoglFramebuffer
2841  * using cogl_set_modelview_matrix() for example then you would want a matrix
2842  * that transforms into eye coordinates.
2843  *
2844  * Note: This function explicitly initializes the given @matrix. If you just
2845  * want clutter to multiply a relative transformation with an existing matrix
2846  * you can use clutter_actor_apply_relative_transformation_matrix()
2847  * instead.
2848  *
2849  */
2850 /* XXX: We should consider caching the stage relative modelview along with
2851  * the actor itself */
2852 static void
_clutter_actor_get_relative_transformation_matrix(ClutterActor * self,ClutterActor * ancestor,graphene_matrix_t * matrix)2853 _clutter_actor_get_relative_transformation_matrix (ClutterActor      *self,
2854                                                    ClutterActor      *ancestor,
2855                                                    graphene_matrix_t *matrix)
2856 {
2857   graphene_matrix_init_identity (matrix);
2858 
2859   _clutter_actor_apply_relative_transformation_matrix (self, ancestor, matrix);
2860 }
2861 
2862 /* Project the given @box into stage window coordinates, writing the
2863  * transformed vertices to @verts[]. */
2864 static gboolean
_clutter_actor_transform_and_project_box(ClutterActor * self,const ClutterActorBox * box,graphene_point3d_t * verts)2865 _clutter_actor_transform_and_project_box (ClutterActor          *self,
2866                                           const ClutterActorBox *box,
2867                                           graphene_point3d_t    *verts)
2868 {
2869   graphene_point3d_t box_vertices[4];
2870 
2871   box_vertices[0].x = box->x1;
2872   box_vertices[0].y = box->y1;
2873   box_vertices[0].z = 0;
2874   box_vertices[1].x = box->x2;
2875   box_vertices[1].y = box->y1;
2876   box_vertices[1].z = 0;
2877   box_vertices[2].x = box->x1;
2878   box_vertices[2].y = box->y2;
2879   box_vertices[2].z = 0;
2880   box_vertices[3].x = box->x2;
2881   box_vertices[3].y = box->y2;
2882   box_vertices[3].z = 0;
2883 
2884   return
2885     _clutter_actor_fully_transform_vertices (self, box_vertices, verts, 4);
2886 }
2887 
2888 /**
2889  * clutter_actor_get_abs_allocation_vertices:
2890  * @self: A #ClutterActor
2891  * @verts: (out) (array fixed-size=4): Pointer to a location of an array
2892  *   of 4 #graphene_point3d_t where to store the result.
2893  *
2894  * Calculates the transformed screen coordinates of the four corners of
2895  * the actor; the returned vertices relate to the #ClutterActorBox
2896  * coordinates  as follows:
2897  *
2898  *  - v[0] contains (x1, y1)
2899  *  - v[1] contains (x2, y1)
2900  *  - v[2] contains (x1, y2)
2901  *  - v[3] contains (x2, y2)
2902  *
2903  * Since: 0.4
2904  */
2905 void
clutter_actor_get_abs_allocation_vertices(ClutterActor * self,graphene_point3d_t * verts)2906 clutter_actor_get_abs_allocation_vertices (ClutterActor       *self,
2907                                            graphene_point3d_t *verts)
2908 {
2909   ClutterActorPrivate *priv;
2910   ClutterActorBox actor_space_allocation;
2911 
2912   g_return_if_fail (CLUTTER_IS_ACTOR (self));
2913 
2914   priv = self->priv;
2915 
2916   /* if the actor needs to be allocated we force a relayout, so that
2917    * the actor allocation box will be valid for
2918    * _clutter_actor_transform_and_project_box()
2919    */
2920   if (priv->needs_allocation)
2921     {
2922       ClutterActor *stage = _clutter_actor_get_stage_internal (self);
2923       /* There's nothing meaningful we can do now */
2924       if (!stage)
2925         return;
2926 
2927       clutter_stage_maybe_relayout (stage);
2928     }
2929 
2930   /* NB: _clutter_actor_transform_and_project_box expects a box in the actor's
2931    * own coordinate space... */
2932   actor_space_allocation.x1 = 0;
2933   actor_space_allocation.y1 = 0;
2934   actor_space_allocation.x2 = priv->allocation.x2 - priv->allocation.x1;
2935   actor_space_allocation.y2 = priv->allocation.y2 - priv->allocation.y1;
2936   _clutter_actor_transform_and_project_box (self,
2937 					    &actor_space_allocation,
2938 					    verts);
2939 }
2940 
2941 static void
clutter_actor_real_apply_transform(ClutterActor * self,graphene_matrix_t * matrix)2942 clutter_actor_real_apply_transform (ClutterActor      *self,
2943                                     graphene_matrix_t *matrix)
2944 {
2945   ClutterActorPrivate *priv = self->priv;
2946   const ClutterTransformInfo *info;
2947   graphene_point3d_t p;
2948   float pivot_x = 0.f, pivot_y = 0.f;
2949 
2950   info = _clutter_actor_get_transform_info_or_defaults (self);
2951 
2952   /* compute the pivot point given the allocated size */
2953   pivot_x = (priv->allocation.x2 - priv->allocation.x1)
2954           * info->pivot.x;
2955   pivot_y = (priv->allocation.y2 - priv->allocation.y1)
2956           * info->pivot.y;
2957 
2958   CLUTTER_NOTE (PAINT,
2959                 "Allocation: (%.2f, %2.f), "
2960                 "pivot: (%.2f, %.2f), "
2961                 "translation: (%.2f, %.2f) -> "
2962                 "new origin: (%.2f, %.2f)",
2963                 priv->allocation.x1, priv->allocation.y1,
2964                 info->pivot.x, info->pivot.y,
2965                 info->translation.x, info->translation.y,
2966                 priv->allocation.x1 + pivot_x + info->translation.x,
2967                 priv->allocation.y1 + pivot_y + info->translation.y);
2968 
2969   /* roll back the pivot translation */
2970   if (pivot_x != 0.f || pivot_y != 0.f || info->pivot_z != 0.f)
2971     {
2972       graphene_point3d_init (&p, -pivot_x, -pivot_y, -info->pivot_z);
2973       graphene_matrix_translate (matrix, &p);
2974     }
2975 
2976   /* if we have an overriding transformation, we use that, and get out */
2977   if (info->transform_set)
2978     {
2979       graphene_matrix_multiply (matrix, &info->transform, matrix);
2980 
2981       /* we still need to apply the :allocation's origin and :pivot-point
2982        * translations, since :transform is relative to the actor's coordinate
2983        * space, and to the pivot point
2984        */
2985       graphene_point3d_init (&p,
2986                              priv->allocation.x1 + pivot_x,
2987                              priv->allocation.y1 + pivot_y,
2988                              info->pivot_z);
2989       graphene_matrix_translate (matrix, &p);
2990       goto roll_back;
2991     }
2992 
2993   if (info->rx_angle)
2994     graphene_matrix_rotate (matrix, info->rx_angle, graphene_vec3_x_axis ());
2995 
2996   if (info->ry_angle)
2997     graphene_matrix_rotate (matrix, info->ry_angle, graphene_vec3_y_axis ());
2998 
2999   if (info->rz_angle)
3000     graphene_matrix_rotate (matrix, info->rz_angle, graphene_vec3_z_axis ());
3001 
3002   if (info->scale_x != 1.0 || info->scale_y != 1.0 || info->scale_z != 1.0)
3003     graphene_matrix_scale (matrix, info->scale_x, info->scale_y, info->scale_z);
3004 
3005   /* basic translation: :allocation's origin and :z-position; instead
3006    * of decomposing the pivot and translation info separate operations,
3007    * we just compose everything into a single translation
3008    */
3009   graphene_point3d_init (&p,
3010                          priv->allocation.x1 + pivot_x + info->translation.x,
3011                          priv->allocation.y1 + pivot_y + info->translation.y,
3012                          info->z_position + info->pivot_z + info->translation.z);
3013   graphene_matrix_translate (matrix, &p);
3014 
3015 roll_back:
3016   /* we apply the :child-transform from the parent actor, if we have one */
3017   if (priv->parent != NULL)
3018     {
3019       const ClutterTransformInfo *parent_info;
3020 
3021       parent_info = _clutter_actor_get_transform_info_or_defaults (priv->parent);
3022       graphene_matrix_multiply (matrix, &parent_info->child_transform, matrix);
3023     }
3024 }
3025 
3026 /* Applies the transforms associated with this actor to the given
3027  * matrix. */
3028 
3029 static void
ensure_valid_actor_transform(ClutterActor * actor)3030 ensure_valid_actor_transform (ClutterActor *actor)
3031 {
3032   ClutterActorPrivate *priv = actor->priv;
3033 
3034   if (priv->transform_valid)
3035     return;
3036 
3037   graphene_matrix_init_identity (&priv->transform);
3038 
3039   CLUTTER_ACTOR_GET_CLASS (actor)->apply_transform (actor, &priv->transform);
3040 
3041   priv->transform_valid = TRUE;
3042 }
3043 
3044 void
_clutter_actor_apply_modelview_transform(ClutterActor * self,graphene_matrix_t * matrix)3045 _clutter_actor_apply_modelview_transform (ClutterActor      *self,
3046                                           graphene_matrix_t *matrix)
3047 {
3048   ClutterActorPrivate *priv = self->priv;
3049 
3050   ensure_valid_actor_transform (self);
3051   graphene_matrix_multiply (&priv->transform, matrix, matrix);
3052 }
3053 
3054 /*
3055  * clutter_actor_apply_relative_transformation_matrix:
3056  * @self: The actor whose coordinate space you want to transform from.
3057  * @ancestor: The ancestor actor whose coordinate space you want to transform too
3058  *            or %NULL if you want to transform all the way to eye coordinates.
3059  * @matrix: A #graphene_matrix_t to apply the transformation too.
3060  *
3061  * This multiplies a transform with @matrix that will transform coordinates
3062  * from the coordinate space of @self into the coordinate space of @ancestor.
3063  *
3064  * For example if you need a matrix that can transform the local actor
3065  * coordinates of @self into stage coordinates you would pass the actor's stage
3066  * pointer as the @ancestor.
3067  *
3068  * If you pass %NULL then the transformation will take you all the way through
3069  * to eye coordinates. This can be useful if you want to extract the entire
3070  * modelview transform that Clutter applies before applying the projection
3071  * transformation. If you want to explicitly set a modelview on a CoglFramebuffer
3072  * using cogl_set_modelview_matrix() for example then you would want a matrix
3073  * that transforms into eye coordinates.
3074  *
3075  * This function doesn't initialize the given @matrix, it simply
3076  * multiplies the requested transformation matrix with the existing contents of
3077  * @matrix. You can use graphene_matrix_init_identity() to initialize the @matrix
3078  * before calling this function, or you can use
3079  * clutter_actor_get_relative_transformation_matrix() instead.
3080  */
3081 void
_clutter_actor_apply_relative_transformation_matrix(ClutterActor * self,ClutterActor * ancestor,graphene_matrix_t * matrix)3082 _clutter_actor_apply_relative_transformation_matrix (ClutterActor      *self,
3083                                                      ClutterActor      *ancestor,
3084                                                      graphene_matrix_t *matrix)
3085 {
3086   /* Note we terminate before ever calling stage->apply_transform()
3087    * since that would conceptually be relative to the underlying
3088    * window OpenGL coordinates so we'd need a special @ancestor
3089    * value to represent the fake parent of the stage. */
3090   if (self == ancestor)
3091     return;
3092 
3093   if (self->priv->parent != NULL)
3094     _clutter_actor_apply_relative_transformation_matrix (self->priv->parent,
3095                                                          ancestor,
3096                                                          matrix);
3097 
3098   _clutter_actor_apply_modelview_transform (self, matrix);
3099 }
3100 
3101 static void
_clutter_actor_draw_paint_volume_full(ClutterActor * self,ClutterPaintVolume * pv,const char * label,const ClutterColor * color,ClutterPaintNode * node)3102 _clutter_actor_draw_paint_volume_full (ClutterActor       *self,
3103                                        ClutterPaintVolume *pv,
3104                                        const char         *label,
3105                                        const ClutterColor *color,
3106                                        ClutterPaintNode   *node)
3107 {
3108   g_autoptr (ClutterPaintNode) pipeline_node = NULL;
3109   static CoglPipeline *outline = NULL;
3110   CoglPrimitive *prim;
3111   graphene_point3d_t line_ends[12 * 2];
3112   int n_vertices;
3113   CoglContext *ctx =
3114     clutter_backend_get_cogl_context (clutter_get_default_backend ());
3115   CoglColor cogl_color;
3116 
3117   if (outline == NULL)
3118     outline = cogl_pipeline_new (ctx);
3119 
3120   _clutter_paint_volume_complete (pv);
3121 
3122   n_vertices = pv->is_2d ? 4 * 2 : 12 * 2;
3123 
3124   /* Front face */
3125   line_ends[0] = pv->vertices[0]; line_ends[1] = pv->vertices[1];
3126   line_ends[2] = pv->vertices[1]; line_ends[3] = pv->vertices[2];
3127   line_ends[4] = pv->vertices[2]; line_ends[5] = pv->vertices[3];
3128   line_ends[6] = pv->vertices[3]; line_ends[7] = pv->vertices[0];
3129 
3130   if (!pv->is_2d)
3131     {
3132       /* Back face */
3133       line_ends[8] = pv->vertices[4]; line_ends[9] = pv->vertices[5];
3134       line_ends[10] = pv->vertices[5]; line_ends[11] = pv->vertices[6];
3135       line_ends[12] = pv->vertices[6]; line_ends[13] = pv->vertices[7];
3136       line_ends[14] = pv->vertices[7]; line_ends[15] = pv->vertices[4];
3137 
3138       /* Lines connecting front face to back face */
3139       line_ends[16] = pv->vertices[0]; line_ends[17] = pv->vertices[4];
3140       line_ends[18] = pv->vertices[1]; line_ends[19] = pv->vertices[5];
3141       line_ends[20] = pv->vertices[2]; line_ends[21] = pv->vertices[6];
3142       line_ends[22] = pv->vertices[3]; line_ends[23] = pv->vertices[7];
3143     }
3144 
3145   prim = cogl_primitive_new_p3 (ctx, COGL_VERTICES_MODE_LINES,
3146                                 n_vertices,
3147                                 (CoglVertexP3 *)line_ends);
3148 
3149   cogl_color_init_from_4ub (&cogl_color,
3150                             color->red,
3151                             color->green,
3152                             color->blue,
3153                             color->alpha);
3154   cogl_pipeline_set_color (outline, &cogl_color);
3155 
3156   pipeline_node = clutter_pipeline_node_new (outline);
3157   clutter_paint_node_set_static_name (pipeline_node,
3158                                       "ClutterActor (paint volume outline)");
3159   clutter_paint_node_add_primitive (pipeline_node, prim);
3160   clutter_paint_node_add_child (node, pipeline_node);
3161   cogl_object_unref (prim);
3162 
3163   if (label)
3164     {
3165       g_autoptr (ClutterPaintNode) text_node = NULL;
3166       PangoLayout *layout;
3167 
3168       layout = pango_layout_new (clutter_actor_get_pango_context (self));
3169       pango_layout_set_text (layout, label, -1);
3170 
3171       text_node = clutter_text_node_new (layout, color);
3172       clutter_paint_node_set_static_name (text_node,
3173                                           "ClutterActor (paint volume label)");
3174       clutter_paint_node_add_rectangle (text_node,
3175                                         &(ClutterActorBox) {
3176                                           .x1 = pv->vertices[0].x,
3177                                           .y1 = pv->vertices[0].y,
3178                                           .x2 = pv->vertices[2].x,
3179                                           .y2 = pv->vertices[2].y,
3180                                         });
3181       clutter_paint_node_add_child (node, text_node);
3182 
3183       g_object_unref (layout);
3184     }
3185 }
3186 
3187 static void
_clutter_actor_draw_paint_volume(ClutterActor * self,ClutterPaintNode * node)3188 _clutter_actor_draw_paint_volume (ClutterActor     *self,
3189                                   ClutterPaintNode *node)
3190 {
3191   ClutterPaintVolume *pv;
3192   ClutterColor color;
3193 
3194   pv = _clutter_actor_get_paint_volume_mutable (self);
3195   if (!pv)
3196     {
3197       gfloat width, height;
3198       ClutterPaintVolume fake_pv;
3199 
3200       ClutterActor *stage = _clutter_actor_get_stage_internal (self);
3201       _clutter_paint_volume_init_static (&fake_pv, stage);
3202 
3203       clutter_actor_get_size (self, &width, &height);
3204       clutter_paint_volume_set_width (&fake_pv, width);
3205       clutter_paint_volume_set_height (&fake_pv, height);
3206 
3207       clutter_color_init (&color, 0, 0, 255, 255);
3208       _clutter_actor_draw_paint_volume_full (self, &fake_pv,
3209                                              _clutter_actor_get_debug_name (self),
3210                                              &color,
3211                                              node);
3212 
3213       clutter_paint_volume_free (&fake_pv);
3214     }
3215   else
3216     {
3217       clutter_color_init (&color, 0, 255, 0, 255);
3218       _clutter_actor_draw_paint_volume_full (self, pv,
3219                                              _clutter_actor_get_debug_name (self),
3220                                              &color,
3221                                              node);
3222     }
3223 }
3224 
3225 static void
_clutter_actor_paint_cull_result(ClutterActor * self,gboolean success,ClutterCullResult result,ClutterPaintNode * node)3226 _clutter_actor_paint_cull_result (ClutterActor      *self,
3227                                   gboolean           success,
3228                                   ClutterCullResult  result,
3229                                   ClutterPaintNode  *node)
3230 {
3231   ClutterActorPrivate *priv = self->priv;
3232   ClutterPaintVolume *pv;
3233   ClutterColor color;
3234 
3235   if (success)
3236     {
3237       switch (result)
3238         {
3239         case CLUTTER_CULL_RESULT_IN:
3240           clutter_color_init (&color, 0, 255, 0, 255);
3241           break;
3242         case CLUTTER_CULL_RESULT_OUT:
3243           clutter_color_init (&color, 0, 0, 255, 255);
3244           break;
3245         default:
3246           clutter_color_init (&color, 0, 255, 255, 255);
3247           break;
3248         }
3249     }
3250   else
3251     clutter_color_init (&color, 255, 255, 255, 255);
3252 
3253   if (success && (pv = _clutter_actor_get_paint_volume_mutable (self)))
3254     _clutter_actor_draw_paint_volume_full (self, pv,
3255                                            _clutter_actor_get_debug_name (self),
3256                                            &color,
3257                                            node);
3258   else
3259     {
3260       g_autoptr (ClutterPaintNode) text_node = NULL;
3261       PangoLayout *layout;
3262       float width;
3263       float height;
3264       char *label =
3265         g_strdup_printf ("CULL FAILURE: %s", _clutter_actor_get_debug_name (self));
3266       clutter_color_init (&color, 255, 255, 255, 255);
3267 
3268       width = clutter_actor_box_get_width (&priv->allocation);
3269       height = clutter_actor_box_get_height (&priv->allocation);
3270 
3271       layout = pango_layout_new (clutter_actor_get_pango_context (self));
3272       pango_layout_set_text (layout, label, -1);
3273 
3274       text_node = clutter_text_node_new (layout, &color);
3275       clutter_paint_node_set_static_name (text_node,
3276                                           "ClutterActor (paint volume text)");
3277       clutter_paint_node_add_rectangle (text_node,
3278                                         &(ClutterActorBox) {
3279                                           .x1 = 0.f,
3280                                           .y1 = 0.f,
3281                                           .x2 = width,
3282                                           .y2 = height,
3283                                         });
3284       clutter_paint_node_add_child (node, text_node);
3285 
3286       g_free (label);
3287       g_object_unref (layout);
3288     }
3289 }
3290 
3291 static int clone_paint_level = 0;
3292 
3293 void
_clutter_actor_push_clone_paint(void)3294 _clutter_actor_push_clone_paint (void)
3295 {
3296   clone_paint_level++;
3297 }
3298 
3299 void
_clutter_actor_pop_clone_paint(void)3300 _clutter_actor_pop_clone_paint (void)
3301 {
3302   clone_paint_level--;
3303 }
3304 
3305 static gboolean
in_clone_paint(void)3306 in_clone_paint (void)
3307 {
3308   return clone_paint_level > 0;
3309 }
3310 
3311 /* Returns TRUE if the actor can be ignored */
3312 /* FIXME: we should return a ClutterCullResult, and
3313  * clutter_actor_paint should understand that a CLUTTER_CULL_RESULT_IN
3314  * means there's no point in trying to cull descendants of the current
3315  * node. */
3316 static gboolean
cull_actor(ClutterActor * self,ClutterPaintContext * paint_context,ClutterCullResult * result_out)3317 cull_actor (ClutterActor        *self,
3318             ClutterPaintContext *paint_context,
3319             ClutterCullResult   *result_out)
3320 {
3321   ClutterActorPrivate *priv = self->priv;
3322   const GArray *clip_frusta;
3323   ClutterCullResult result = CLUTTER_CULL_RESULT_IN;
3324   int i;
3325 
3326   if (!priv->last_paint_volume_valid)
3327     {
3328       CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): "
3329                     "->last_paint_volume_valid == FALSE",
3330                     _clutter_actor_get_debug_name (self));
3331       return FALSE;
3332     }
3333 
3334   if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CULLING))
3335     return FALSE;
3336 
3337   if (clutter_paint_context_is_drawing_off_stage (paint_context))
3338     {
3339       CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): "
3340                     "Drawing off stage",
3341                     _clutter_actor_get_debug_name (self));
3342       return FALSE;
3343     }
3344 
3345   clip_frusta = clutter_paint_context_get_clip_frusta (paint_context);
3346   if (!clip_frusta)
3347     {
3348       *result_out = result;
3349       return TRUE;
3350     }
3351 
3352   for (i = 0; i < clip_frusta->len; i++)
3353     {
3354       const graphene_frustum_t *clip_frustum =
3355         &g_array_index (clip_frusta, graphene_frustum_t, i);
3356 
3357       result = _clutter_paint_volume_cull (&priv->last_paint_volume,
3358                                            clip_frustum);
3359 
3360       if (result != CLUTTER_CULL_RESULT_OUT)
3361         break;
3362     }
3363 
3364   *result_out = result;
3365 
3366   return TRUE;
3367 }
3368 
3369 static void
_clutter_actor_update_last_paint_volume(ClutterActor * self)3370 _clutter_actor_update_last_paint_volume (ClutterActor *self)
3371 {
3372   ClutterActorPrivate *priv = self->priv;
3373   const ClutterPaintVolume *pv;
3374 
3375   if (priv->last_paint_volume_valid)
3376     {
3377       clutter_paint_volume_free (&priv->last_paint_volume);
3378       priv->last_paint_volume_valid = FALSE;
3379     }
3380 
3381   pv = clutter_actor_get_paint_volume (self);
3382   if (!pv)
3383     {
3384       CLUTTER_NOTE (CLIPPING, "Bail from update_last_paint_volume (%s): "
3385                     "Actor failed to report a paint volume",
3386                     _clutter_actor_get_debug_name (self));
3387       return;
3388     }
3389 
3390   _clutter_paint_volume_copy_static (pv, &priv->last_paint_volume);
3391 
3392   _clutter_paint_volume_transform_relative (&priv->last_paint_volume,
3393                                             NULL); /* eye coordinates */
3394 
3395   priv->last_paint_volume_valid = TRUE;
3396 }
3397 
3398 /* This is the same as clutter_actor_add_effect except that it doesn't
3399    queue a redraw and it doesn't notify on the effect property */
3400 static void
_clutter_actor_add_effect_internal(ClutterActor * self,ClutterEffect * effect)3401 _clutter_actor_add_effect_internal (ClutterActor  *self,
3402                                     ClutterEffect *effect)
3403 {
3404   ClutterActorPrivate *priv = self->priv;
3405 
3406   if (priv->effects == NULL)
3407     {
3408       priv->effects = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
3409       priv->effects->actor = self;
3410     }
3411 
3412   _clutter_meta_group_add_meta (priv->effects, CLUTTER_ACTOR_META (effect));
3413 }
3414 
3415 /* This is the same as clutter_actor_remove_effect except that it doesn't
3416    queue a redraw and it doesn't notify on the effect property */
3417 static void
_clutter_actor_remove_effect_internal(ClutterActor * self,ClutterEffect * effect)3418 _clutter_actor_remove_effect_internal (ClutterActor  *self,
3419                                        ClutterEffect *effect)
3420 {
3421   ClutterActorPrivate *priv = self->priv;
3422 
3423   if (priv->effects == NULL)
3424     return;
3425 
3426   _clutter_meta_group_remove_meta (priv->effects, CLUTTER_ACTOR_META (effect));
3427 
3428   if (_clutter_meta_group_peek_metas (priv->effects) == NULL)
3429     g_clear_object (&priv->effects);
3430 }
3431 
3432 static gboolean
needs_flatten_effect(ClutterActor * self)3433 needs_flatten_effect (ClutterActor *self)
3434 {
3435   ClutterActorPrivate *priv = self->priv;
3436 
3437   if (G_UNLIKELY (clutter_paint_debug_flags &
3438                   CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT))
3439     return FALSE;
3440 
3441   /* We need to enable the effect immediately even in ON_IDLE because that can
3442    * only be implemented efficiently within the effect itself.
3443    * If it was implemented here using just priv->is_dirty then we would lose
3444    * the ability to animate opacity without repaints.
3445    */
3446   if ((priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ALWAYS) ||
3447       (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE))
3448     return TRUE;
3449   else if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY)
3450     {
3451       if (clutter_actor_get_paint_opacity (self) < 255 &&
3452           clutter_actor_has_overlaps (self))
3453         return TRUE;
3454     }
3455 
3456   return FALSE;
3457 }
3458 
3459 static void
add_or_remove_flatten_effect(ClutterActor * self)3460 add_or_remove_flatten_effect (ClutterActor *self)
3461 {
3462   ClutterActorPrivate *priv = self->priv;
3463 
3464   /* Add or remove the flatten effect depending on the
3465      offscreen-redirect property. */
3466   if (needs_flatten_effect (self))
3467     {
3468       if (priv->flatten_effect == NULL)
3469         {
3470           ClutterActorMeta *actor_meta;
3471           gint priority;
3472 
3473           priv->flatten_effect = _clutter_flatten_effect_new ();
3474           /* Keep a reference to the effect so that we can queue
3475              redraws from it */
3476           g_object_ref_sink (priv->flatten_effect);
3477 
3478           /* Set the priority of the effect to high so that it will
3479              always be applied to the actor first. It uses an internal
3480              priority so that it won't be visible to applications */
3481           actor_meta = CLUTTER_ACTOR_META (priv->flatten_effect);
3482           priority = CLUTTER_ACTOR_META_PRIORITY_INTERNAL_HIGH;
3483           _clutter_actor_meta_set_priority (actor_meta, priority);
3484 
3485           /* This will add the effect without queueing a redraw */
3486           _clutter_actor_add_effect_internal (self, priv->flatten_effect);
3487         }
3488     }
3489   else
3490     {
3491       if (priv->flatten_effect != NULL)
3492         {
3493           /* Destroy the effect so that it will lose its fbo cache of
3494              the actor */
3495           _clutter_actor_remove_effect_internal (self, priv->flatten_effect);
3496           g_clear_object (&priv->flatten_effect);
3497         }
3498     }
3499 }
3500 
3501 static void
clutter_actor_real_paint(ClutterActor * actor,ClutterPaintContext * paint_context)3502 clutter_actor_real_paint (ClutterActor        *actor,
3503                           ClutterPaintContext *paint_context)
3504 {
3505   ClutterActorPrivate *priv = actor->priv;
3506   ClutterActor *iter;
3507 
3508   for (iter = priv->first_child;
3509        iter != NULL;
3510        iter = iter->priv->next_sibling)
3511     {
3512       CLUTTER_NOTE (PAINT, "Painting %s, child of %s, at { %.2f, %.2f - %.2f x %.2f }",
3513                     _clutter_actor_get_debug_name (iter),
3514                     _clutter_actor_get_debug_name (actor),
3515                     iter->priv->allocation.x1,
3516                     iter->priv->allocation.y1,
3517                     iter->priv->allocation.x2 - iter->priv->allocation.x1,
3518                     iter->priv->allocation.y2 - iter->priv->allocation.y1);
3519 
3520       clutter_actor_paint (iter, paint_context);
3521     }
3522 }
3523 
3524 static gboolean
clutter_actor_paint_node(ClutterActor * actor,ClutterPaintNode * root,ClutterPaintContext * paint_context)3525 clutter_actor_paint_node (ClutterActor        *actor,
3526                           ClutterPaintNode    *root,
3527                           ClutterPaintContext *paint_context)
3528 {
3529   ClutterActorPrivate *priv = actor->priv;
3530   ClutterActorBox box;
3531   ClutterColor bg_color;
3532 
3533   box.x1 = 0.f;
3534   box.y1 = 0.f;
3535   box.x2 = clutter_actor_box_get_width (&priv->allocation);
3536   box.y2 = clutter_actor_box_get_height (&priv->allocation);
3537 
3538   bg_color = priv->bg_color;
3539 
3540   if (CLUTTER_ACTOR_IS_TOPLEVEL (actor))
3541     {
3542       ClutterPaintNode *node;
3543       CoglFramebuffer *fb;
3544       CoglBufferBit clear_flags;
3545 
3546       fb = clutter_paint_context_get_base_framebuffer (paint_context);
3547 
3548       bg_color.alpha = 255;
3549 
3550       CLUTTER_NOTE (PAINT, "Stage clear color: (%d, %d, %d, %d)",
3551                     bg_color.red,
3552                     bg_color.green,
3553                     bg_color.blue,
3554                     bg_color.alpha);
3555 
3556       clear_flags = COGL_BUFFER_BIT_DEPTH;
3557 
3558       node = clutter_root_node_new (fb, &bg_color, clear_flags);
3559       clutter_paint_node_set_static_name (node, "stageClear");
3560       clutter_paint_node_add_rectangle (node, &box);
3561       clutter_paint_node_add_child (root, node);
3562       clutter_paint_node_unref (node);
3563     }
3564   else if (priv->bg_color_set &&
3565            !clutter_color_equal (&priv->bg_color, CLUTTER_COLOR_Transparent))
3566     {
3567       ClutterPaintNode *node;
3568 
3569       bg_color.alpha = clutter_actor_get_paint_opacity_internal (actor)
3570                      * priv->bg_color.alpha
3571                      / 255;
3572 
3573       node = clutter_color_node_new (&bg_color);
3574       clutter_paint_node_set_static_name (node, "backgroundColor");
3575       clutter_paint_node_add_rectangle (node, &box);
3576       clutter_paint_node_add_child (root, node);
3577       clutter_paint_node_unref (node);
3578     }
3579 
3580   if (priv->content != NULL)
3581     _clutter_content_paint_content (priv->content, actor, root, paint_context);
3582 
3583   if (CLUTTER_ACTOR_GET_CLASS (actor)->paint_node != NULL)
3584     CLUTTER_ACTOR_GET_CLASS (actor)->paint_node (actor, root);
3585 
3586   if (clutter_paint_node_get_n_children (root) == 0)
3587     return FALSE;
3588 
3589 #ifdef CLUTTER_ENABLE_DEBUG
3590   if (CLUTTER_HAS_DEBUG (PAINT))
3591     {
3592       /* dump the tree only if we have one */
3593       _clutter_paint_node_dump_tree (root);
3594     }
3595 #endif /* CLUTTER_ENABLE_DEBUG */
3596 
3597   clutter_paint_node_paint (root, paint_context);
3598 
3599   return TRUE;
3600 }
3601 
3602 /**
3603  * clutter_actor_paint:
3604  * @self: A #ClutterActor
3605  *
3606  * Renders the actor to display.
3607  *
3608  * This function should not be called directly by applications.
3609  * Call clutter_actor_queue_redraw() to queue paints, instead.
3610  *
3611  * This function is context-aware, and will either cause a
3612  * regular paint or a pick paint.
3613  *
3614  * This function will call the #ClutterActorClass.paint() virtual
3615  * function.
3616  *
3617  * This function does not paint the actor if the actor is set to 0,
3618  * unless it is performing a pick paint.
3619  */
3620 void
clutter_actor_paint(ClutterActor * self,ClutterPaintContext * paint_context)3621 clutter_actor_paint (ClutterActor        *self,
3622                      ClutterPaintContext *paint_context)
3623 {
3624   g_autoptr (ClutterPaintNode) actor_node = NULL;
3625   g_autoptr (ClutterPaintNode) root_node = NULL;
3626   ClutterActorPrivate *priv;
3627   ClutterActorBox clip;
3628   gboolean culling_inhibited;
3629   gboolean clip_set = FALSE;
3630 
3631   g_return_if_fail (CLUTTER_IS_ACTOR (self));
3632 
3633   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
3634     return;
3635 
3636   priv = self->priv;
3637   priv->propagated_one_redraw = FALSE;
3638 
3639   /* It's an important optimization that we consider painting of
3640    * actors with 0 opacity to be a NOP... */
3641   if (/* ignore top-levels, since they might be transparent */
3642       !CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
3643       /* Use the override opacity if its been set */
3644       ((priv->opacity_override >= 0) ?
3645        priv->opacity_override : priv->opacity) == 0)
3646     return;
3647 
3648   /* if we aren't paintable (not in a toplevel with all
3649    * parents paintable) then do nothing.
3650    */
3651   if (!CLUTTER_ACTOR_IS_MAPPED (self))
3652     return;
3653 
3654 #ifdef COGL_HAS_TRACING
3655   COGL_TRACE_SCOPED_ANCHOR (ClutterActorPaint);
3656 
3657   if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_DETAILED_TRACE))
3658     {
3659       COGL_TRACE_BEGIN_ANCHORED (ClutterActorPaint,
3660                                  "ClutterActor (paint)");
3661       COGL_TRACE_DESCRIBE (ClutterActorPaint,
3662                            _clutter_actor_get_debug_name (self));
3663     }
3664 #endif
3665 
3666   actor_node = clutter_actor_node_new (self, -1);
3667   root_node = clutter_paint_node_ref (actor_node);
3668 
3669   if (priv->has_clip)
3670     {
3671       clip.x1 = priv->clip.origin.x;
3672       clip.y1 = priv->clip.origin.y;
3673       clip.x2 = priv->clip.origin.x + priv->clip.size.width;
3674       clip.y2 = priv->clip.origin.y + priv->clip.size.height;
3675       clip_set = TRUE;
3676     }
3677   else if (priv->clip_to_allocation)
3678     {
3679       clip.x1 = 0.f;
3680       clip.y1 = 0.f;
3681       clip.x2 = priv->allocation.x2 - priv->allocation.x1;
3682       clip.y2 = priv->allocation.y2 - priv->allocation.y1;
3683       clip_set = TRUE;
3684     }
3685 
3686   if (clip_set)
3687     {
3688       ClutterPaintNode *clip_node;
3689 
3690       clip_node = clutter_clip_node_new ();
3691       clutter_paint_node_add_rectangle (clip_node, &clip);
3692       clutter_paint_node_add_child (clip_node, root_node);
3693       clutter_paint_node_unref (root_node);
3694 
3695       root_node = g_steal_pointer (&clip_node);
3696     }
3697 
3698   if (priv->enable_model_view_transform)
3699     {
3700       ClutterPaintNode *transform_node;
3701       graphene_matrix_t transform;
3702 
3703       clutter_actor_get_transform (self, &transform);
3704 
3705       if (!graphene_matrix_is_identity (&transform))
3706         {
3707           transform_node = clutter_transform_node_new (&transform);
3708           clutter_paint_node_add_child (transform_node, root_node);
3709           clutter_paint_node_unref (root_node);
3710 
3711           root_node = g_steal_pointer (&transform_node);
3712         }
3713 
3714 #ifdef CLUTTER_ENABLE_DEBUG
3715       /* Catch when out-of-band transforms have been made by actors not as part
3716        * of an apply_transform vfunc... */
3717       if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_OOB_TRANSFORMS))
3718         {
3719           graphene_matrix_t expected_matrix;
3720 
3721           _clutter_actor_get_relative_transformation_matrix (self, NULL,
3722                                                              &expected_matrix);
3723 
3724           if (!graphene_matrix_equal_fast (&transform, &expected_matrix))
3725             {
3726               GString *buf = g_string_sized_new (1024);
3727               ClutterActor *parent;
3728 
3729               parent = self;
3730               while (parent != NULL)
3731                 {
3732                   g_string_append (buf, _clutter_actor_get_debug_name (parent));
3733 
3734                   if (parent->priv->parent != NULL)
3735                     g_string_append (buf, "->");
3736 
3737                   parent = parent->priv->parent;
3738                 }
3739 
3740               g_warning ("Unexpected transform found when painting actor "
3741                          "\"%s\". This will be caused by one of the actor's "
3742                          "ancestors (%s) using the Cogl API directly to transform "
3743                          "children instead of using ::apply_transform().",
3744                          _clutter_actor_get_debug_name (self),
3745                          buf->str);
3746 
3747               g_string_free (buf, TRUE);
3748             }
3749         }
3750 #endif /* CLUTTER_ENABLE_DEBUG */
3751     }
3752 
3753   /* We check whether we need to add the flatten effect before
3754    * each paint so that we can avoid having a mechanism for
3755    * applications to notify when the value of the
3756    * has_overlaps virtual changes.
3757    */
3758   add_or_remove_flatten_effect (self);
3759 
3760   /* We save the current paint volume so that the next time the
3761    * actor queues a redraw we can constrain the redraw to just
3762    * cover the union of the new bounding box and the old.
3763    *
3764    * We also fetch the current paint volume to perform culling so
3765    * we can avoid painting actors outside the current clip region.
3766    *
3767    * If we are painting inside a clone, we should neither update
3768    * the paint volume or use it to cull painting, since the paint
3769    * box represents the location of the source actor on the
3770    * screen.
3771    *
3772    * XXX: We are starting to do a lot of vertex transforms on
3773    * the CPU in a typical paint, so at some point we should
3774    * audit these and consider caching some things.
3775    *
3776    * NB: We don't perform culling while picking at this point because
3777    * clutter-stage.c doesn't setup the clipping planes appropriately.
3778    *
3779    * NB: We don't want to update the last-paint-volume during picking
3780    * because the last-paint-volume is used to determine the old screen
3781    * space location of an actor that has moved so we can know the
3782    * minimal region to redraw to clear an old view of the actor. If we
3783    * update this during picking then by the time we come around to
3784    * paint then the last-paint-volume would likely represent the new
3785    * actor position not the old.
3786    */
3787   culling_inhibited = priv->inhibit_culling_counter > 0;
3788   if (!culling_inhibited && !in_clone_paint ())
3789     {
3790       gboolean success;
3791       gboolean should_cull_out = (clutter_paint_debug_flags &
3792                                   (CLUTTER_DEBUG_DISABLE_CULLING |
3793                                    CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)) !=
3794                                  (CLUTTER_DEBUG_DISABLE_CULLING |
3795                                   CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS);
3796       /* annoyingly gcc warns if uninitialized even though
3797        * the initialization is redundant :-( */
3798       ClutterCullResult result = CLUTTER_CULL_RESULT_IN;
3799 
3800       success = should_cull_out
3801         ? cull_actor (self, paint_context, &result)
3802         : FALSE;
3803 
3804       if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS))
3805         _clutter_actor_paint_cull_result (self, success, result, actor_node);
3806       else if (result == CLUTTER_CULL_RESULT_OUT && success)
3807         return;
3808     }
3809 
3810   if (priv->effects == NULL)
3811     priv->next_effect_to_paint = NULL;
3812   else
3813     priv->next_effect_to_paint =
3814       _clutter_meta_group_peek_metas (priv->effects);
3815 
3816   if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES))
3817     _clutter_actor_draw_paint_volume (self, actor_node);
3818 
3819   clutter_paint_node_paint (root_node, paint_context);
3820 
3821   /* If we make it here then the actor has run through a complete
3822      paint run including all the effects so it's no longer dirty */
3823   priv->is_dirty = FALSE;
3824 }
3825 
3826 /**
3827  * clutter_actor_continue_paint:
3828  * @self: A #ClutterActor
3829  *
3830  * Run the next stage of the paint sequence. This function should only
3831  * be called within the implementation of the ‘run’ virtual of a
3832  * #ClutterEffect. It will cause the run method of the next effect to
3833  * be applied, or it will paint the actual actor if the current effect
3834  * is the last effect in the chain.
3835  *
3836  * Since: 1.8
3837  */
3838 void
clutter_actor_continue_paint(ClutterActor * self,ClutterPaintContext * paint_context)3839 clutter_actor_continue_paint (ClutterActor        *self,
3840                               ClutterPaintContext *paint_context)
3841 {
3842   ClutterActorPrivate *priv;
3843 
3844   g_return_if_fail (CLUTTER_IS_ACTOR (self));
3845   /* This should only be called from with in the ‘run’ implementation
3846      of a ClutterEffect */
3847   g_return_if_fail (CLUTTER_ACTOR_IN_PAINT (self));
3848 
3849   priv = self->priv;
3850 
3851   /* Skip any effects that are disabled */
3852   while (priv->next_effect_to_paint &&
3853          !clutter_actor_meta_get_enabled (priv->next_effect_to_paint->data))
3854     priv->next_effect_to_paint = priv->next_effect_to_paint->next;
3855 
3856   /* If this has come from the last effect then we'll just paint the
3857      actual actor */
3858   if (priv->next_effect_to_paint == NULL)
3859     {
3860       CoglFramebuffer *framebuffer;
3861       ClutterPaintNode *dummy;
3862 
3863       /* XXX - this will go away in 2.0, when we can get rid of this
3864        * stuff and switch to a pure retained render tree of PaintNodes
3865        * for the entire frame, starting from the Stage; the paint()
3866        * virtual function can then be called directly.
3867        */
3868       framebuffer = clutter_paint_context_get_base_framebuffer (paint_context);
3869       dummy = _clutter_dummy_node_new (self, framebuffer);
3870       clutter_paint_node_set_static_name (dummy, "Root");
3871 
3872       /* XXX - for 1.12, we use the return value of paint_node() to
3873        * decide whether we should call the paint() vfunc.
3874        */
3875       clutter_actor_paint_node (self, dummy, paint_context);
3876       clutter_paint_node_unref (dummy);
3877 
3878       CLUTTER_ACTOR_GET_CLASS (self)->paint (self, paint_context);
3879     }
3880   else
3881     {
3882       g_autoptr (ClutterPaintNode) effect_node = NULL;
3883       ClutterEffect *old_current_effect;
3884       ClutterEffectPaintFlags run_flags = 0;
3885 
3886       /* Cache the current effect so that we can put it back before
3887          returning */
3888       old_current_effect = priv->current_effect;
3889 
3890       priv->current_effect = priv->next_effect_to_paint->data;
3891       priv->next_effect_to_paint = priv->next_effect_to_paint->next;
3892 
3893       if (priv->is_dirty)
3894         {
3895           /* If there's an effect queued with this redraw then all
3896            * effects up to that one will be considered dirty. It
3897            * is expected the queued effect will paint the cached
3898            * image and not call clutter_actor_continue_paint again
3899            * (although it should work ok if it does)
3900            */
3901           if (priv->effect_to_redraw == NULL ||
3902               priv->current_effect != priv->effect_to_redraw)
3903             run_flags |= CLUTTER_EFFECT_PAINT_ACTOR_DIRTY;
3904         }
3905 
3906       if (priv->current_effect == priv->flatten_effect &&
3907           priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE &&
3908           run_flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY)
3909         run_flags |= CLUTTER_EFFECT_PAINT_BYPASS_EFFECT;
3910 
3911       effect_node = clutter_effect_node_new (priv->current_effect);
3912 
3913       _clutter_effect_paint (priv->current_effect,
3914                              effect_node,
3915                              paint_context,
3916                              run_flags);
3917 
3918       clutter_paint_node_paint (effect_node, paint_context);
3919 
3920       priv->current_effect = old_current_effect;
3921     }
3922 }
3923 
3924 /**
3925  * clutter_actor_pick:
3926  * @actor: A #ClutterActor
3927  *
3928  * Asks @actor to perform a pick.
3929  */
3930 void
clutter_actor_pick(ClutterActor * actor,ClutterPickContext * pick_context)3931 clutter_actor_pick (ClutterActor       *actor,
3932                     ClutterPickContext *pick_context)
3933 {
3934   ClutterActorPrivate *priv;
3935   ClutterActorBox clip;
3936   gboolean transform_pushed = FALSE;
3937   gboolean clip_set = FALSE;
3938   gboolean should_cull = (clutter_paint_debug_flags &
3939                           (CLUTTER_DEBUG_DISABLE_CULLING |
3940                            CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)) !=
3941                          (CLUTTER_DEBUG_DISABLE_CULLING |
3942                           CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS);
3943 
3944   if (CLUTTER_ACTOR_IN_DESTRUCTION (actor))
3945     return;
3946 
3947   priv = actor->priv;
3948 
3949   /* if we aren't paintable (not in a toplevel with all
3950    * parents paintable) then do nothing.
3951    */
3952   if (!CLUTTER_ACTOR_IS_MAPPED (actor))
3953     return;
3954 
3955   /* mark that we are in the paint process */
3956   CLUTTER_SET_PRIVATE_FLAGS (actor, CLUTTER_IN_PICK);
3957 
3958   if (should_cull && priv->paint_volume_valid && priv->last_paint_volume_valid)
3959     {
3960       graphene_box_t box;
3961 
3962       clutter_paint_volume_to_box (&priv->last_paint_volume, &box);
3963       if (!clutter_pick_context_intersects_box (pick_context, &box))
3964         goto out;
3965     }
3966 
3967   if (priv->enable_model_view_transform)
3968     {
3969       graphene_matrix_t matrix;
3970 
3971       graphene_matrix_init_identity (&matrix);
3972       _clutter_actor_apply_modelview_transform (actor, &matrix);
3973       if (!graphene_matrix_is_identity (&matrix))
3974         {
3975           clutter_pick_context_push_transform (pick_context, &matrix);
3976           transform_pushed = TRUE;
3977         }
3978     }
3979 
3980   if (priv->has_clip)
3981     {
3982       clip.x1 = priv->clip.origin.x;
3983       clip.y1 = priv->clip.origin.y;
3984       clip.x2 = priv->clip.origin.x + priv->clip.size.width;
3985       clip.y2 = priv->clip.origin.y + priv->clip.size.height;
3986       clip_set = TRUE;
3987     }
3988   else if (priv->clip_to_allocation)
3989     {
3990       clip.x1 = 0.f;
3991       clip.y1 = 0.f;
3992       clip.x2 = priv->allocation.x2 - priv->allocation.x1;
3993       clip.y2 = priv->allocation.y2 - priv->allocation.y1;
3994       clip_set = TRUE;
3995     }
3996 
3997   if (clip_set)
3998     clutter_pick_context_push_clip (pick_context, &clip);
3999 
4000   priv->next_effect_to_paint = NULL;
4001   if (priv->effects)
4002     {
4003       priv->next_effect_to_paint =
4004         _clutter_meta_group_peek_metas (priv->effects);
4005     }
4006 
4007   clutter_actor_continue_pick (actor, pick_context);
4008 
4009   if (clip_set)
4010     clutter_pick_context_pop_clip (pick_context);
4011 
4012   if (transform_pushed)
4013     clutter_pick_context_pop_transform (pick_context);
4014 
4015 out:
4016   /* paint sequence complete */
4017   CLUTTER_UNSET_PRIVATE_FLAGS (actor, CLUTTER_IN_PICK);
4018 }
4019 
4020 /**
4021  * clutter_actor_continue_pick:
4022  * @actor: A #ClutterActor
4023  *
4024  * Run the next stage of the pick sequence. This function should only
4025  * be called within the implementation of the ‘pick’ virtual of a
4026  * #ClutterEffect. It will cause the run method of the next effect to
4027  * be applied, or it will pick the actual actor if the current effect
4028  * is the last effect in the chain.
4029  */
4030 void
clutter_actor_continue_pick(ClutterActor * actor,ClutterPickContext * pick_context)4031 clutter_actor_continue_pick (ClutterActor       *actor,
4032                              ClutterPickContext *pick_context)
4033 {
4034   ClutterActorPrivate *priv;
4035 
4036   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
4037 
4038   g_return_if_fail (CLUTTER_ACTOR_IN_PICK (actor));
4039 
4040   priv = actor->priv;
4041 
4042   /* Skip any effects that are disabled */
4043   while (priv->next_effect_to_paint &&
4044          !clutter_actor_meta_get_enabled (priv->next_effect_to_paint->data))
4045     priv->next_effect_to_paint = priv->next_effect_to_paint->next;
4046 
4047   /* If this has come from the last effect then we'll just pick the
4048    * actual actor.
4049    */
4050   if (priv->next_effect_to_paint == NULL)
4051     {
4052       /* The actor will log a silhouette of itself to the stage pick log.
4053        *
4054        * XXX:2.0 - Call the pick() virtual directly
4055        */
4056       if (g_signal_has_handler_pending (actor, actor_signals[PICK],
4057                                         0, TRUE))
4058         g_signal_emit (actor, actor_signals[PICK], 0, pick_context);
4059       else
4060         CLUTTER_ACTOR_GET_CLASS (actor)->pick (actor, pick_context);
4061     }
4062   else
4063     {
4064       ClutterEffect *old_current_effect;
4065 
4066       /* Cache the current effect so that we can put it back before
4067        * returning.
4068        */
4069       old_current_effect = priv->current_effect;
4070 
4071       priv->current_effect = priv->next_effect_to_paint->data;
4072       priv->next_effect_to_paint = priv->next_effect_to_paint->next;
4073 
4074       _clutter_effect_pick (priv->current_effect, pick_context);
4075 
4076       priv->current_effect = old_current_effect;
4077     }
4078 }
4079 
4080 static void
_clutter_actor_stop_transitions(ClutterActor * self)4081 _clutter_actor_stop_transitions (ClutterActor *self)
4082 {
4083   const ClutterAnimationInfo *info;
4084   GHashTableIter iter;
4085   gpointer value;
4086 
4087   info = _clutter_actor_get_animation_info_or_defaults (self);
4088   if (info->transitions == NULL)
4089     return;
4090 
4091   g_hash_table_iter_init (&iter, info->transitions);
4092   while (g_hash_table_iter_next (&iter, NULL, &value))
4093     {
4094       TransitionClosure *closure = value;
4095 
4096       if (clutter_transition_get_remove_on_complete (closure->transition))
4097         {
4098           g_hash_table_iter_remove (&iter);
4099         }
4100       else
4101         {
4102           /* otherwise we stop it, and the transition will be removed
4103            * later, either by the actor's destruction or by explicit
4104            * removal
4105            */
4106           clutter_timeline_stop (CLUTTER_TIMELINE (closure->transition));
4107         }
4108     }
4109 }
4110 
4111 static inline void
remove_child(ClutterActor * self,ClutterActor * child)4112 remove_child (ClutterActor *self,
4113               ClutterActor *child)
4114 {
4115   ClutterActor *prev_sibling, *next_sibling;
4116 
4117   prev_sibling = child->priv->prev_sibling;
4118   next_sibling = child->priv->next_sibling;
4119 
4120   if (prev_sibling != NULL)
4121     prev_sibling->priv->next_sibling = next_sibling;
4122 
4123   if (next_sibling != NULL)
4124     next_sibling->priv->prev_sibling = prev_sibling;
4125 
4126   if (self->priv->first_child == child)
4127     self->priv->first_child = next_sibling;
4128 
4129   if (self->priv->last_child == child)
4130     self->priv->last_child = prev_sibling;
4131 
4132   child->priv->parent = NULL;
4133   child->priv->prev_sibling = NULL;
4134   child->priv->next_sibling = NULL;
4135 }
4136 
4137 typedef enum
4138 {
4139   REMOVE_CHILD_DESTROY_META       = 1 << 0,
4140   REMOVE_CHILD_EMIT_PARENT_SET    = 1 << 1,
4141   REMOVE_CHILD_EMIT_ACTOR_REMOVED = 1 << 2,
4142   REMOVE_CHILD_CHECK_STATE        = 1 << 3,
4143   REMOVE_CHILD_NOTIFY_FIRST_LAST  = 1 << 4,
4144   REMOVE_CHILD_STOP_TRANSITIONS   = 1 << 5,
4145   REMOVE_CHILD_CLEAR_STAGE_VIEWS  = 1 << 6,
4146 
4147   /* default flags for public API */
4148   REMOVE_CHILD_DEFAULT_FLAGS      = REMOVE_CHILD_STOP_TRANSITIONS |
4149                                     REMOVE_CHILD_DESTROY_META |
4150                                     REMOVE_CHILD_EMIT_PARENT_SET |
4151                                     REMOVE_CHILD_EMIT_ACTOR_REMOVED |
4152                                     REMOVE_CHILD_CHECK_STATE |
4153                                     REMOVE_CHILD_NOTIFY_FIRST_LAST |
4154                                     REMOVE_CHILD_CLEAR_STAGE_VIEWS,
4155 } ClutterActorRemoveChildFlags;
4156 
4157 /*< private >
4158  * clutter_actor_remove_child_internal:
4159  * @self: a #ClutterActor
4160  * @child: the child of @self that has to be removed
4161  * @flags: control the removal operations
4162  *
4163  * Removes @child from the list of children of @self.
4164  */
4165 static void
clutter_actor_remove_child_internal(ClutterActor * self,ClutterActor * child,ClutterActorRemoveChildFlags flags)4166 clutter_actor_remove_child_internal (ClutterActor                 *self,
4167                                      ClutterActor                 *child,
4168                                      ClutterActorRemoveChildFlags  flags)
4169 {
4170   ClutterActor *old_first, *old_last;
4171   gboolean destroy_meta, emit_parent_set, emit_actor_removed, check_state;
4172   gboolean notify_first_last;
4173   gboolean stop_transitions;
4174   gboolean clear_stage_views;
4175   GObject *obj;
4176 
4177   if (self == child)
4178     {
4179       g_warning ("Cannot remove actor '%s' from itself.",
4180                  _clutter_actor_get_debug_name (self));
4181       return;
4182     }
4183 
4184   destroy_meta = (flags & REMOVE_CHILD_DESTROY_META) != 0;
4185   emit_parent_set = (flags & REMOVE_CHILD_EMIT_PARENT_SET) != 0;
4186   emit_actor_removed = (flags & REMOVE_CHILD_EMIT_ACTOR_REMOVED) != 0;
4187   check_state = (flags & REMOVE_CHILD_CHECK_STATE) != 0;
4188   notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0;
4189   stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0;
4190   clear_stage_views = (flags & REMOVE_CHILD_CLEAR_STAGE_VIEWS) != 0;
4191 
4192   obj = G_OBJECT (self);
4193   g_object_freeze_notify (obj);
4194 
4195   if (stop_transitions)
4196     _clutter_actor_stop_transitions (child);
4197 
4198   if (destroy_meta)
4199     clutter_container_destroy_child_meta (CLUTTER_CONTAINER (self), child);
4200 
4201   if (check_state)
4202     {
4203       /* we need to unrealize *before* we set parent_actor to NULL,
4204        * because in an unrealize method actors are dissociating from the
4205        * stage, which means they need to be able to
4206        * clutter_actor_get_stage().
4207        *
4208        * yhis should unmap and unrealize, unless we're reparenting.
4209        */
4210       clutter_actor_update_map_state (child, MAP_STATE_MAKE_UNREALIZED);
4211     }
4212 
4213   old_first = self->priv->first_child;
4214   old_last = self->priv->last_child;
4215 
4216   remove_child (self, child);
4217 
4218   self->priv->n_children -= 1;
4219 
4220   self->priv->age += 1;
4221 
4222   if (self->priv->in_cloned_branch)
4223     clutter_actor_pop_in_cloned_branch (child, self->priv->in_cloned_branch);
4224 
4225   if (self->priv->unmapped_paint_branch_counter)
4226     pop_in_paint_unmapped_branch (child, self->priv->unmapped_paint_branch_counter);
4227 
4228   /* if the child that got removed was visible and set to
4229    * expand then we want to reset the parent's state in
4230    * case the child was the only thing that was making it
4231    * expand.
4232    */
4233   if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
4234       (child->priv->needs_compute_expand ||
4235        child->priv->needs_x_expand ||
4236        child->priv->needs_y_expand))
4237     {
4238       clutter_actor_queue_compute_expand (self);
4239     }
4240 
4241   /* Only actors which are attached to a stage get notified about changes
4242    * to the stage views, so make sure all the stage-views lists are
4243    * cleared as the child and its children leave the actor tree.
4244    */
4245   if (clear_stage_views && !CLUTTER_ACTOR_IN_DESTRUCTION (child))
4246     clutter_actor_clear_stage_views_recursive (child);
4247 
4248   if (emit_parent_set && !CLUTTER_ACTOR_IN_DESTRUCTION (child))
4249     g_signal_emit (child, actor_signals[PARENT_SET], 0, self);
4250 
4251   /* we need to emit the signal before dropping the reference */
4252   if (emit_actor_removed)
4253     _clutter_container_emit_actor_removed (CLUTTER_CONTAINER (self), child);
4254 
4255   if (notify_first_last)
4256     {
4257       if (old_first != self->priv->first_child)
4258         g_object_notify_by_pspec (obj, obj_props[PROP_FIRST_CHILD]);
4259 
4260       if (old_last != self->priv->last_child)
4261         g_object_notify_by_pspec (obj, obj_props[PROP_LAST_CHILD]);
4262     }
4263 
4264   g_object_thaw_notify (obj);
4265 
4266   /* remove the reference we acquired in clutter_actor_add_child() */
4267   g_object_unref (child);
4268 }
4269 
4270 static ClutterTransformInfo default_transform_info = {
4271   0.0,                          /* rotation-x */
4272   0.0,                          /* rotation-y */
4273   0.0,                          /* rotation-z */
4274 
4275   1.0, 1.0, 1.0,                /* scale */
4276 
4277   GRAPHENE_POINT3D_INIT_ZERO,   /* translation */
4278 
4279   0.f,                          /* z-position */
4280 
4281   GRAPHENE_POINT_INIT_ZERO,     /* pivot */
4282   0.f,                          /* pivot-z */
4283 
4284   { },
4285   FALSE,                        /* transform */
4286   { },
4287   FALSE,                        /* child-transform */
4288 };
4289 
4290 static inline const ClutterTransformInfo *
get_default_transform_info(void)4291 get_default_transform_info (void)
4292 {
4293   static gsize initialized = FALSE;
4294 
4295   if (G_UNLIKELY (g_once_init_enter (&initialized)))
4296     {
4297       graphene_matrix_init_identity (&default_transform_info.transform);
4298       graphene_matrix_init_identity (&default_transform_info.child_transform);
4299       g_once_init_leave (&initialized, TRUE);
4300     }
4301 
4302   return &default_transform_info;
4303 }
4304 
4305 /*< private >
4306  * _clutter_actor_get_transform_info_or_defaults:
4307  * @self: a #ClutterActor
4308  *
4309  * Retrieves the ClutterTransformInfo structure associated to an actor.
4310  *
4311  * If the actor does not have a ClutterTransformInfo structure associated
4312  * to it, then the default structure will be returned.
4313  *
4314  * This function should only be used for getters.
4315  *
4316  * Return value: a const pointer to the ClutterTransformInfo structure
4317  */
4318 const ClutterTransformInfo *
_clutter_actor_get_transform_info_or_defaults(ClutterActor * self)4319 _clutter_actor_get_transform_info_or_defaults (ClutterActor *self)
4320 {
4321   ClutterTransformInfo *info;
4322 
4323   info = g_object_get_qdata (G_OBJECT (self), quark_actor_transform_info);
4324   if (info != NULL)
4325     return info;
4326 
4327   return get_default_transform_info ();
4328 }
4329 
4330 static void
clutter_transform_info_free(gpointer data)4331 clutter_transform_info_free (gpointer data)
4332 {
4333   if (data != NULL)
4334     g_free (data);
4335 }
4336 
4337 /*< private >
4338  * _clutter_actor_get_transform_info:
4339  * @self: a #ClutterActor
4340  *
4341  * Retrieves a pointer to the ClutterTransformInfo structure.
4342  *
4343  * If the actor does not have a ClutterTransformInfo associated to it, one
4344  * will be created and initialized to the default values.
4345  *
4346  * This function should be used for setters.
4347  *
4348  * For getters, you should use _clutter_actor_get_transform_info_or_defaults()
4349  * instead.
4350  *
4351  * Return value: (transfer none): a pointer to the ClutterTransformInfo
4352  *   structure
4353  */
4354 ClutterTransformInfo *
_clutter_actor_get_transform_info(ClutterActor * self)4355 _clutter_actor_get_transform_info (ClutterActor *self)
4356 {
4357   ClutterTransformInfo *info;
4358 
4359   info = g_object_get_qdata (G_OBJECT (self), quark_actor_transform_info);
4360   if (info == NULL)
4361     {
4362       info = g_new0 (ClutterTransformInfo, 1);
4363 
4364       *info = *get_default_transform_info ();
4365 
4366       g_object_set_qdata_full (G_OBJECT (self), quark_actor_transform_info,
4367                                info,
4368                                clutter_transform_info_free);
4369     }
4370 
4371   return info;
4372 }
4373 
4374 static inline void
clutter_actor_set_pivot_point_internal(ClutterActor * self,const graphene_point_t * pivot)4375 clutter_actor_set_pivot_point_internal (ClutterActor           *self,
4376                                         const graphene_point_t *pivot)
4377 {
4378   ClutterTransformInfo *info;
4379 
4380   info = _clutter_actor_get_transform_info (self);
4381   info->pivot = *pivot;
4382 
4383   transform_changed (self);
4384 
4385   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PIVOT_POINT]);
4386 
4387   clutter_actor_queue_redraw (self);
4388 }
4389 
4390 static inline void
clutter_actor_set_pivot_point_z_internal(ClutterActor * self,float pivot_z)4391 clutter_actor_set_pivot_point_z_internal (ClutterActor *self,
4392                                           float         pivot_z)
4393 {
4394   ClutterTransformInfo *info;
4395 
4396   info = _clutter_actor_get_transform_info (self);
4397   info->pivot_z = pivot_z;
4398 
4399   transform_changed (self);
4400 
4401   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PIVOT_POINT_Z]);
4402 
4403   clutter_actor_queue_redraw (self);
4404 }
4405 
4406 /*< private >
4407  * clutter_actor_set_translation_internal:
4408  * @self: a #ClutterActor
4409  * @axis: the axis of the translation to change
4410  * @angle: the translation as a value along @axis
4411  *
4412  * Sets the translation on the given @axis
4413  */
4414 static void
clutter_actor_set_translation_internal(ClutterActor * self,gfloat value,GParamSpec * pspec)4415 clutter_actor_set_translation_internal (ClutterActor *self,
4416                                         gfloat        value,
4417                                         GParamSpec   *pspec)
4418 {
4419   GObject *obj = G_OBJECT (self);
4420   ClutterTransformInfo *info;
4421 
4422   info = _clutter_actor_get_transform_info (self);
4423 
4424   if (pspec == obj_props[PROP_TRANSLATION_X])
4425     info->translation.x = value;
4426   else if (pspec == obj_props[PROP_TRANSLATION_Y])
4427     info->translation.y = value;
4428   else if (pspec == obj_props[PROP_TRANSLATION_Z])
4429     info->translation.z = value;
4430   else
4431     g_assert_not_reached ();
4432 
4433   transform_changed (self);
4434 
4435   clutter_actor_queue_redraw (self);
4436   g_object_notify_by_pspec (obj, pspec);
4437 }
4438 
4439 static inline void
clutter_actor_set_translation_factor(ClutterActor * self,ClutterRotateAxis axis,gdouble value)4440 clutter_actor_set_translation_factor (ClutterActor      *self,
4441                                       ClutterRotateAxis  axis,
4442                                       gdouble            value)
4443 {
4444   const ClutterTransformInfo *info;
4445   const float *translate_p = NULL;
4446   GParamSpec *pspec = NULL;
4447 
4448   info = _clutter_actor_get_transform_info_or_defaults (self);
4449 
4450   switch (axis)
4451     {
4452     case CLUTTER_X_AXIS:
4453       pspec = obj_props[PROP_TRANSLATION_X];
4454       translate_p = &info->translation.x;
4455       break;
4456 
4457     case CLUTTER_Y_AXIS:
4458       pspec = obj_props[PROP_TRANSLATION_Y];
4459       translate_p = &info->translation.y;
4460       break;
4461 
4462     case CLUTTER_Z_AXIS:
4463       pspec = obj_props[PROP_TRANSLATION_Z];
4464       translate_p = &info->translation.z;
4465       break;
4466     }
4467 
4468   g_assert (pspec != NULL);
4469   g_assert (translate_p != NULL);
4470 
4471   _clutter_actor_create_transition (self, pspec, *translate_p, value);
4472 }
4473 
4474 /**
4475  * clutter_actor_set_translation:
4476  * @self: a #ClutterActor
4477  * @translate_x: the translation along the X axis
4478  * @translate_y: the translation along the Y axis
4479  * @translate_z: the translation along the Z axis
4480  *
4481  * Sets an additional translation transformation on a #ClutterActor,
4482  * relative to the #ClutterActor:pivot-point.
4483  *
4484  * Since: 1.12
4485  */
4486 void
clutter_actor_set_translation(ClutterActor * self,gfloat translate_x,gfloat translate_y,gfloat translate_z)4487 clutter_actor_set_translation (ClutterActor *self,
4488                                gfloat        translate_x,
4489                                gfloat        translate_y,
4490                                gfloat        translate_z)
4491 {
4492   g_return_if_fail (CLUTTER_IS_ACTOR (self));
4493 
4494   g_object_freeze_notify (G_OBJECT (self));
4495 
4496   clutter_actor_set_translation_factor (self, CLUTTER_X_AXIS, translate_x);
4497   clutter_actor_set_translation_factor (self, CLUTTER_Y_AXIS, translate_y);
4498   clutter_actor_set_translation_factor (self, CLUTTER_Z_AXIS, translate_z);
4499 
4500   g_object_thaw_notify (G_OBJECT (self));
4501 }
4502 
4503 /**
4504  * clutter_actor_get_translation:
4505  * @self: a #ClutterActor
4506  * @translate_x: (out) (allow-none): return location for the X component
4507  *   of the translation, or %NULL
4508  * @translate_y: (out) (allow-none): return location for the Y component
4509  *   of the translation, or %NULL
4510  * @translate_z: (out) (allow-none): return location for the Z component
4511  *   of the translation, or %NULL
4512  *
4513  * Retrieves the translation set using clutter_actor_set_translation().
4514  *
4515  * Since: 1.12
4516  */
4517 void
clutter_actor_get_translation(ClutterActor * self,gfloat * translate_x,gfloat * translate_y,gfloat * translate_z)4518 clutter_actor_get_translation (ClutterActor *self,
4519                                gfloat       *translate_x,
4520                                gfloat       *translate_y,
4521                                gfloat       *translate_z)
4522 {
4523   const ClutterTransformInfo *info;
4524 
4525   g_return_if_fail (CLUTTER_IS_ACTOR (self));
4526 
4527   info = _clutter_actor_get_transform_info_or_defaults (self);
4528 
4529   if (translate_x != NULL)
4530     *translate_x = info->translation.x;
4531 
4532   if (translate_y != NULL)
4533     *translate_y = info->translation.y;
4534 
4535   if (translate_z != NULL)
4536     *translate_z = info->translation.z;
4537 }
4538 
4539 /*< private >
4540  * clutter_actor_set_rotation_angle_internal:
4541  * @self: a #ClutterActor
4542  * @angle: the angle of rotation
4543  * @pspec: the #GParamSpec of the property
4544  *
4545  * Sets the rotation angle on the given axis without affecting the
4546  * rotation center point.
4547  */
4548 static inline void
clutter_actor_set_rotation_angle_internal(ClutterActor * self,gdouble angle,GParamSpec * pspec)4549 clutter_actor_set_rotation_angle_internal (ClutterActor *self,
4550                                            gdouble       angle,
4551                                            GParamSpec   *pspec)
4552 {
4553   ClutterTransformInfo *info;
4554 
4555   info = _clutter_actor_get_transform_info (self);
4556 
4557   if (pspec == obj_props[PROP_ROTATION_ANGLE_X])
4558     info->rx_angle = angle;
4559   else if (pspec == obj_props[PROP_ROTATION_ANGLE_Y])
4560     info->ry_angle = angle;
4561   else if (pspec == obj_props[PROP_ROTATION_ANGLE_Z])
4562     info->rz_angle = angle;
4563   else
4564     g_assert_not_reached ();
4565 
4566   transform_changed (self);
4567 
4568   clutter_actor_queue_redraw (self);
4569 
4570   g_object_notify_by_pspec (G_OBJECT (self), pspec);
4571 }
4572 
4573 /**
4574  * clutter_actor_set_rotation_angle:
4575  * @self: a #ClutterActor
4576  * @axis: the axis to set the angle one
4577  * @angle: the angle of rotation, in degrees
4578  *
4579  * Sets the @angle of rotation of a #ClutterActor on the given @axis.
4580  *
4581  * This function is a convenience for setting the rotation properties
4582  * #ClutterActor:rotation-angle-x, #ClutterActor:rotation-angle-y,
4583  * and #ClutterActor:rotation-angle-z.
4584  *
4585  * The center of rotation is established by the #ClutterActor:pivot-point
4586  * property.
4587  *
4588  * Since: 1.12
4589  */
4590 void
clutter_actor_set_rotation_angle(ClutterActor * self,ClutterRotateAxis axis,gdouble angle)4591 clutter_actor_set_rotation_angle (ClutterActor      *self,
4592                                   ClutterRotateAxis  axis,
4593                                   gdouble            angle)
4594 {
4595   const ClutterTransformInfo *info;
4596   const double *cur_angle_p = NULL;
4597   GParamSpec *pspec = NULL;
4598 
4599   g_return_if_fail (CLUTTER_IS_ACTOR (self));
4600 
4601   info = _clutter_actor_get_transform_info_or_defaults (self);
4602 
4603   switch (axis)
4604     {
4605     case CLUTTER_X_AXIS:
4606       cur_angle_p = &info->rx_angle;
4607       pspec = obj_props[PROP_ROTATION_ANGLE_X];
4608       break;
4609 
4610     case CLUTTER_Y_AXIS:
4611       cur_angle_p = &info->ry_angle;
4612       pspec = obj_props[PROP_ROTATION_ANGLE_Y];
4613       break;
4614 
4615     case CLUTTER_Z_AXIS:
4616       cur_angle_p = &info->rz_angle;
4617       pspec = obj_props[PROP_ROTATION_ANGLE_Z];
4618       break;
4619     }
4620 
4621   g_assert (pspec != NULL);
4622   g_assert (cur_angle_p != NULL);
4623 
4624   _clutter_actor_create_transition (self, pspec, *cur_angle_p, angle);
4625 }
4626 
4627 /**
4628  * clutter_actor_get_rotation_angle:
4629  * @self: a #ClutterActor
4630  * @axis: the axis of the rotation
4631  *
4632  * Retrieves the angle of rotation set by clutter_actor_set_rotation_angle().
4633  *
4634  * Return value: the angle of rotation, in degrees
4635  *
4636  * Since: 1.12
4637  */
4638 gdouble
clutter_actor_get_rotation_angle(ClutterActor * self,ClutterRotateAxis axis)4639 clutter_actor_get_rotation_angle (ClutterActor      *self,
4640                                   ClutterRotateAxis  axis)
4641 {
4642   const ClutterTransformInfo *info;
4643   gdouble retval;
4644 
4645   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.0);
4646 
4647   info = _clutter_actor_get_transform_info_or_defaults (self);
4648 
4649   switch (axis)
4650     {
4651     case CLUTTER_X_AXIS:
4652       retval = info->rx_angle;
4653       break;
4654 
4655     case CLUTTER_Y_AXIS:
4656       retval = info->ry_angle;
4657       break;
4658 
4659     case CLUTTER_Z_AXIS:
4660       retval = info->rz_angle;
4661       break;
4662 
4663     default:
4664       g_warn_if_reached ();
4665       retval = 0.0;
4666       break;
4667     }
4668 
4669   return retval;
4670 }
4671 
4672 static void
clutter_actor_set_scale_factor_internal(ClutterActor * self,double factor,GParamSpec * pspec)4673 clutter_actor_set_scale_factor_internal (ClutterActor *self,
4674                                          double factor,
4675                                          GParamSpec *pspec)
4676 {
4677   GObject *obj = G_OBJECT (self);
4678   ClutterTransformInfo *info;
4679 
4680   info = _clutter_actor_get_transform_info (self);
4681 
4682   if (pspec == obj_props[PROP_SCALE_X])
4683     info->scale_x = factor;
4684   else if (pspec == obj_props[PROP_SCALE_Y])
4685     info->scale_y = factor;
4686   else if (pspec == obj_props[PROP_SCALE_Z])
4687     info->scale_z = factor;
4688   else
4689     g_assert_not_reached ();
4690 
4691   transform_changed (self);
4692 
4693   clutter_actor_queue_redraw (self);
4694   g_object_notify_by_pspec (obj, pspec);
4695 }
4696 
4697 static inline void
clutter_actor_set_scale_factor(ClutterActor * self,ClutterRotateAxis axis,gdouble factor)4698 clutter_actor_set_scale_factor (ClutterActor      *self,
4699                                 ClutterRotateAxis  axis,
4700                                 gdouble            factor)
4701 {
4702   const ClutterTransformInfo *info;
4703   const double *scale_p = NULL;
4704   GParamSpec *pspec = NULL;
4705 
4706   info = _clutter_actor_get_transform_info_or_defaults (self);
4707 
4708   switch (axis)
4709     {
4710     case CLUTTER_X_AXIS:
4711       pspec = obj_props[PROP_SCALE_X];
4712       scale_p = &info->scale_x;
4713       break;
4714 
4715     case CLUTTER_Y_AXIS:
4716       pspec = obj_props[PROP_SCALE_Y];
4717       scale_p = &info->scale_y;
4718       break;
4719 
4720     case CLUTTER_Z_AXIS:
4721       pspec = obj_props[PROP_SCALE_Z];
4722       scale_p = &info->scale_z;
4723       break;
4724     }
4725 
4726   g_assert (pspec != NULL);
4727   g_assert (scale_p != NULL);
4728 
4729   if (*scale_p != factor)
4730     _clutter_actor_create_transition (self, pspec, *scale_p, factor);
4731 }
4732 
4733 static void
clutter_actor_set_clip_rect(ClutterActor * self,const graphene_rect_t * clip)4734 clutter_actor_set_clip_rect (ClutterActor          *self,
4735                              const graphene_rect_t *clip)
4736 {
4737   ClutterActorPrivate *priv = self->priv;
4738   GObject *obj = G_OBJECT (self);
4739 
4740   if (clip != NULL)
4741     {
4742       priv->clip = *clip;
4743       priv->has_clip = TRUE;
4744     }
4745   else
4746     priv->has_clip = FALSE;
4747 
4748   queue_update_paint_volume (self);
4749   clutter_actor_queue_redraw (self);
4750 
4751   g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]);
4752   g_object_notify_by_pspec (obj, obj_props[PROP_HAS_CLIP]);
4753 }
4754 
4755 static void
clutter_actor_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)4756 clutter_actor_set_property (GObject      *object,
4757 			    guint         prop_id,
4758 			    const GValue *value,
4759 			    GParamSpec   *pspec)
4760 {
4761   ClutterActor *actor = CLUTTER_ACTOR (object);
4762   ClutterActorPrivate *priv = actor->priv;
4763 
4764   switch (prop_id)
4765     {
4766     case PROP_X:
4767       clutter_actor_set_x (actor, g_value_get_float (value));
4768       break;
4769 
4770     case PROP_Y:
4771       clutter_actor_set_y (actor, g_value_get_float (value));
4772       break;
4773 
4774     case PROP_POSITION:
4775       {
4776         const graphene_point_t *pos = g_value_get_boxed (value);
4777 
4778         if (pos != NULL)
4779           clutter_actor_set_position (actor, pos->x, pos->y);
4780         else
4781           clutter_actor_set_fixed_position_set (actor, FALSE);
4782       }
4783       break;
4784 
4785     case PROP_WIDTH:
4786       clutter_actor_set_width (actor, g_value_get_float (value));
4787       break;
4788 
4789     case PROP_HEIGHT:
4790       clutter_actor_set_height (actor, g_value_get_float (value));
4791       break;
4792 
4793     case PROP_SIZE:
4794       {
4795         const graphene_size_t *size = g_value_get_boxed (value);
4796 
4797         if (size != NULL)
4798           clutter_actor_set_size (actor, size->width, size->height);
4799         else
4800           clutter_actor_set_size (actor, -1, -1);
4801       }
4802       break;
4803 
4804     case PROP_FIXED_X:
4805       clutter_actor_set_x (actor, g_value_get_float (value));
4806       break;
4807 
4808     case PROP_FIXED_Y:
4809       clutter_actor_set_y (actor, g_value_get_float (value));
4810       break;
4811 
4812     case PROP_FIXED_POSITION_SET:
4813       clutter_actor_set_fixed_position_set (actor, g_value_get_boolean (value));
4814       break;
4815 
4816     case PROP_MIN_WIDTH:
4817       clutter_actor_set_min_width (actor, g_value_get_float (value));
4818       break;
4819 
4820     case PROP_MIN_HEIGHT:
4821       clutter_actor_set_min_height (actor, g_value_get_float (value));
4822       break;
4823 
4824     case PROP_NATURAL_WIDTH:
4825       clutter_actor_set_natural_width (actor, g_value_get_float (value));
4826       break;
4827 
4828     case PROP_NATURAL_HEIGHT:
4829       clutter_actor_set_natural_height (actor, g_value_get_float (value));
4830       break;
4831 
4832     case PROP_MIN_WIDTH_SET:
4833       clutter_actor_set_min_width_set (actor, g_value_get_boolean (value));
4834       break;
4835 
4836     case PROP_MIN_HEIGHT_SET:
4837       clutter_actor_set_min_height_set (actor, g_value_get_boolean (value));
4838       break;
4839 
4840     case PROP_NATURAL_WIDTH_SET:
4841       clutter_actor_set_natural_width_set (actor, g_value_get_boolean (value));
4842       break;
4843 
4844     case PROP_NATURAL_HEIGHT_SET:
4845       clutter_actor_set_natural_height_set (actor, g_value_get_boolean (value));
4846       break;
4847 
4848     case PROP_REQUEST_MODE:
4849       clutter_actor_set_request_mode (actor, g_value_get_enum (value));
4850       break;
4851 
4852     case PROP_Z_POSITION:
4853       clutter_actor_set_z_position (actor, g_value_get_float (value));
4854       break;
4855 
4856     case PROP_OPACITY:
4857       clutter_actor_set_opacity (actor, g_value_get_uint (value));
4858       break;
4859 
4860     case PROP_OFFSCREEN_REDIRECT:
4861       clutter_actor_set_offscreen_redirect (actor, g_value_get_flags (value));
4862       break;
4863 
4864     case PROP_NAME:
4865       clutter_actor_set_name (actor, g_value_get_string (value));
4866       break;
4867 
4868     case PROP_VISIBLE:
4869       if (g_value_get_boolean (value) == TRUE)
4870 	clutter_actor_show (actor);
4871       else
4872 	clutter_actor_hide (actor);
4873       break;
4874 
4875     case PROP_PIVOT_POINT:
4876       {
4877         const graphene_point_t *pivot = g_value_get_boxed (value);
4878 
4879         if (pivot == NULL)
4880           pivot = graphene_point_zero ();
4881 
4882         clutter_actor_set_pivot_point (actor, pivot->x, pivot->y);
4883       }
4884       break;
4885 
4886     case PROP_PIVOT_POINT_Z:
4887       clutter_actor_set_pivot_point_z (actor, g_value_get_float (value));
4888       break;
4889 
4890     case PROP_TRANSLATION_X:
4891       clutter_actor_set_translation_factor (actor, CLUTTER_X_AXIS,
4892                                             g_value_get_float (value));
4893       break;
4894 
4895     case PROP_TRANSLATION_Y:
4896       clutter_actor_set_translation_factor (actor, CLUTTER_Y_AXIS,
4897                                             g_value_get_float (value));
4898       break;
4899 
4900     case PROP_TRANSLATION_Z:
4901       clutter_actor_set_translation_factor (actor, CLUTTER_Z_AXIS,
4902                                             g_value_get_float (value));
4903       break;
4904 
4905     case PROP_SCALE_X:
4906       clutter_actor_set_scale_factor (actor, CLUTTER_X_AXIS,
4907                                       g_value_get_double (value));
4908       break;
4909 
4910     case PROP_SCALE_Y:
4911       clutter_actor_set_scale_factor (actor, CLUTTER_Y_AXIS,
4912                                       g_value_get_double (value));
4913       break;
4914 
4915     case PROP_SCALE_Z:
4916       clutter_actor_set_scale_factor (actor, CLUTTER_Z_AXIS,
4917                                       g_value_get_double (value));
4918       break;
4919 
4920     case PROP_CLIP_RECT:
4921       clutter_actor_set_clip_rect (actor, g_value_get_boxed (value));
4922       break;
4923 
4924     case PROP_CLIP_TO_ALLOCATION:
4925       clutter_actor_set_clip_to_allocation (actor, g_value_get_boolean (value));
4926       break;
4927 
4928     case PROP_REACTIVE:
4929       clutter_actor_set_reactive (actor, g_value_get_boolean (value));
4930       break;
4931 
4932     case PROP_ROTATION_ANGLE_X:
4933       clutter_actor_set_rotation_angle (actor,
4934                                         CLUTTER_X_AXIS,
4935                                         g_value_get_double (value));
4936       break;
4937 
4938     case PROP_ROTATION_ANGLE_Y:
4939       clutter_actor_set_rotation_angle (actor,
4940                                         CLUTTER_Y_AXIS,
4941                                         g_value_get_double (value));
4942       break;
4943 
4944     case PROP_ROTATION_ANGLE_Z:
4945       clutter_actor_set_rotation_angle (actor,
4946                                         CLUTTER_Z_AXIS,
4947                                         g_value_get_double (value));
4948       break;
4949 
4950     case PROP_TRANSFORM:
4951       clutter_actor_set_transform (actor, g_value_get_boxed (value));
4952       break;
4953 
4954     case PROP_CHILD_TRANSFORM:
4955       clutter_actor_set_child_transform (actor, g_value_get_boxed (value));
4956       break;
4957 
4958     case PROP_SHOW_ON_SET_PARENT: /* XXX:2.0 - remove */
4959       priv->show_on_set_parent = g_value_get_boolean (value);
4960       break;
4961 
4962     case PROP_TEXT_DIRECTION:
4963       clutter_actor_set_text_direction (actor, g_value_get_enum (value));
4964       break;
4965 
4966     case PROP_ACTIONS:
4967       clutter_actor_add_action (actor, g_value_get_object (value));
4968       break;
4969 
4970     case PROP_CONSTRAINTS:
4971       clutter_actor_add_constraint (actor, g_value_get_object (value));
4972       break;
4973 
4974     case PROP_EFFECT:
4975       clutter_actor_add_effect (actor, g_value_get_object (value));
4976       break;
4977 
4978     case PROP_LAYOUT_MANAGER:
4979       clutter_actor_set_layout_manager (actor, g_value_get_object (value));
4980       break;
4981 
4982     case PROP_X_EXPAND:
4983       clutter_actor_set_x_expand (actor, g_value_get_boolean (value));
4984       break;
4985 
4986     case PROP_Y_EXPAND:
4987       clutter_actor_set_y_expand (actor, g_value_get_boolean (value));
4988       break;
4989 
4990     case PROP_X_ALIGN:
4991       clutter_actor_set_x_align (actor, g_value_get_enum (value));
4992       break;
4993 
4994     case PROP_Y_ALIGN:
4995       clutter_actor_set_y_align (actor, g_value_get_enum (value));
4996       break;
4997 
4998     case PROP_MARGIN_TOP:
4999       clutter_actor_set_margin_top (actor, g_value_get_float (value));
5000       break;
5001 
5002     case PROP_MARGIN_BOTTOM:
5003       clutter_actor_set_margin_bottom (actor, g_value_get_float (value));
5004       break;
5005 
5006     case PROP_MARGIN_LEFT:
5007       clutter_actor_set_margin_left (actor, g_value_get_float (value));
5008       break;
5009 
5010     case PROP_MARGIN_RIGHT:
5011       clutter_actor_set_margin_right (actor, g_value_get_float (value));
5012       break;
5013 
5014     case PROP_BACKGROUND_COLOR:
5015       clutter_actor_set_background_color (actor, g_value_get_boxed (value));
5016       break;
5017 
5018     case PROP_CONTENT:
5019       clutter_actor_set_content (actor, g_value_get_object (value));
5020       break;
5021 
5022     case PROP_CONTENT_GRAVITY:
5023       clutter_actor_set_content_gravity (actor, g_value_get_enum (value));
5024       break;
5025 
5026     case PROP_MINIFICATION_FILTER:
5027       clutter_actor_set_content_scaling_filters (actor,
5028                                                  g_value_get_enum (value),
5029                                                  actor->priv->mag_filter);
5030       break;
5031 
5032     case PROP_MAGNIFICATION_FILTER:
5033       clutter_actor_set_content_scaling_filters (actor,
5034                                                  actor->priv->min_filter,
5035                                                  g_value_get_enum (value));
5036       break;
5037 
5038     case PROP_CONTENT_REPEAT:
5039       clutter_actor_set_content_repeat (actor, g_value_get_flags (value));
5040       break;
5041 
5042     default:
5043       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5044       break;
5045     }
5046 }
5047 
5048 static void
clutter_actor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)5049 clutter_actor_get_property (GObject    *object,
5050 			    guint       prop_id,
5051 			    GValue     *value,
5052 			    GParamSpec *pspec)
5053 {
5054   ClutterActor *actor = CLUTTER_ACTOR (object);
5055   ClutterActorPrivate *priv = actor->priv;
5056 
5057   switch (prop_id)
5058     {
5059     case PROP_X:
5060       g_value_set_float (value, clutter_actor_get_x (actor));
5061       break;
5062 
5063     case PROP_Y:
5064       g_value_set_float (value, clutter_actor_get_y (actor));
5065       break;
5066 
5067     case PROP_POSITION:
5068       {
5069         graphene_point_t position;
5070 
5071         graphene_point_init (&position,
5072                              clutter_actor_get_x (actor),
5073                              clutter_actor_get_y (actor));
5074         g_value_set_boxed (value, &position);
5075       }
5076       break;
5077 
5078     case PROP_WIDTH:
5079       g_value_set_float (value, clutter_actor_get_width (actor));
5080       break;
5081 
5082     case PROP_HEIGHT:
5083       g_value_set_float (value, clutter_actor_get_height (actor));
5084       break;
5085 
5086     case PROP_SIZE:
5087       {
5088         graphene_size_t size;
5089 
5090         graphene_size_init (&size,
5091                             clutter_actor_get_width (actor),
5092                             clutter_actor_get_height (actor));
5093         g_value_set_boxed (value, &size);
5094       }
5095       break;
5096 
5097     case PROP_FIXED_X:
5098       {
5099         const ClutterLayoutInfo *info;
5100 
5101         info = _clutter_actor_get_layout_info_or_defaults (actor);
5102         g_value_set_float (value, info->fixed_pos.x);
5103       }
5104       break;
5105 
5106     case PROP_FIXED_Y:
5107       {
5108         const ClutterLayoutInfo *info;
5109 
5110         info = _clutter_actor_get_layout_info_or_defaults (actor);
5111         g_value_set_float (value, info->fixed_pos.y);
5112       }
5113       break;
5114 
5115     case PROP_FIXED_POSITION_SET:
5116       g_value_set_boolean (value, priv->position_set);
5117       break;
5118 
5119     case PROP_MIN_WIDTH:
5120       {
5121         const ClutterLayoutInfo *info;
5122 
5123         info = _clutter_actor_get_layout_info_or_defaults (actor);
5124         g_value_set_float (value, info->minimum.width);
5125       }
5126       break;
5127 
5128     case PROP_MIN_HEIGHT:
5129       {
5130         const ClutterLayoutInfo *info;
5131 
5132         info = _clutter_actor_get_layout_info_or_defaults (actor);
5133         g_value_set_float (value, info->minimum.height);
5134       }
5135       break;
5136 
5137     case PROP_NATURAL_WIDTH:
5138       {
5139         const ClutterLayoutInfo *info;
5140 
5141         info = _clutter_actor_get_layout_info_or_defaults (actor);
5142         g_value_set_float (value, info->natural.width);
5143       }
5144       break;
5145 
5146     case PROP_NATURAL_HEIGHT:
5147       {
5148         const ClutterLayoutInfo *info;
5149 
5150         info = _clutter_actor_get_layout_info_or_defaults (actor);
5151         g_value_set_float (value, info->natural.height);
5152       }
5153       break;
5154 
5155     case PROP_MIN_WIDTH_SET:
5156       g_value_set_boolean (value, priv->min_width_set);
5157       break;
5158 
5159     case PROP_MIN_HEIGHT_SET:
5160       g_value_set_boolean (value, priv->min_height_set);
5161       break;
5162 
5163     case PROP_NATURAL_WIDTH_SET:
5164       g_value_set_boolean (value, priv->natural_width_set);
5165       break;
5166 
5167     case PROP_NATURAL_HEIGHT_SET:
5168       g_value_set_boolean (value, priv->natural_height_set);
5169       break;
5170 
5171     case PROP_REQUEST_MODE:
5172       g_value_set_enum (value, priv->request_mode);
5173       break;
5174 
5175     case PROP_ALLOCATION:
5176       g_value_set_boxed (value, &priv->allocation);
5177       break;
5178 
5179     case PROP_Z_POSITION:
5180       g_value_set_float (value, clutter_actor_get_z_position (actor));
5181       break;
5182 
5183     case PROP_OPACITY:
5184       g_value_set_uint (value, priv->opacity);
5185       break;
5186 
5187     case PROP_OFFSCREEN_REDIRECT:
5188       g_value_set_flags (value, priv->offscreen_redirect);
5189       break;
5190 
5191     case PROP_NAME:
5192       g_value_set_string (value, priv->name);
5193       break;
5194 
5195     case PROP_VISIBLE:
5196       g_value_set_boolean (value, CLUTTER_ACTOR_IS_VISIBLE (actor));
5197       break;
5198 
5199     case PROP_MAPPED:
5200       g_value_set_boolean (value, CLUTTER_ACTOR_IS_MAPPED (actor));
5201       break;
5202 
5203     case PROP_REALIZED:
5204       g_value_set_boolean (value, CLUTTER_ACTOR_IS_REALIZED (actor));
5205       break;
5206 
5207     case PROP_HAS_CLIP:
5208       g_value_set_boolean (value, priv->has_clip);
5209       break;
5210 
5211     case PROP_CLIP_RECT:
5212       g_value_set_boxed (value, &priv->clip);
5213       break;
5214 
5215     case PROP_CLIP_TO_ALLOCATION:
5216       g_value_set_boolean (value, priv->clip_to_allocation);
5217       break;
5218 
5219     case PROP_PIVOT_POINT:
5220       {
5221         const ClutterTransformInfo *info;
5222 
5223         info = _clutter_actor_get_transform_info_or_defaults (actor);
5224         g_value_set_boxed (value, &info->pivot);
5225       }
5226       break;
5227 
5228     case PROP_PIVOT_POINT_Z:
5229       {
5230         const ClutterTransformInfo *info;
5231 
5232         info = _clutter_actor_get_transform_info_or_defaults (actor);
5233         g_value_set_float (value, info->pivot_z);
5234       }
5235       break;
5236 
5237     case PROP_TRANSLATION_X:
5238       {
5239         const ClutterTransformInfo *info;
5240 
5241         info = _clutter_actor_get_transform_info_or_defaults (actor);
5242         g_value_set_float (value, info->translation.x);
5243       }
5244       break;
5245 
5246     case PROP_TRANSLATION_Y:
5247       {
5248         const ClutterTransformInfo *info;
5249 
5250         info = _clutter_actor_get_transform_info_or_defaults (actor);
5251         g_value_set_float (value, info->translation.y);
5252       }
5253       break;
5254 
5255     case PROP_TRANSLATION_Z:
5256       {
5257         const ClutterTransformInfo *info;
5258 
5259         info = _clutter_actor_get_transform_info_or_defaults (actor);
5260         g_value_set_float (value, info->translation.z);
5261       }
5262       break;
5263 
5264     case PROP_SCALE_X:
5265       {
5266         const ClutterTransformInfo *info;
5267 
5268         info = _clutter_actor_get_transform_info_or_defaults (actor);
5269         g_value_set_double (value, info->scale_x);
5270       }
5271       break;
5272 
5273     case PROP_SCALE_Y:
5274       {
5275         const ClutterTransformInfo *info;
5276 
5277         info = _clutter_actor_get_transform_info_or_defaults (actor);
5278         g_value_set_double (value, info->scale_y);
5279       }
5280       break;
5281 
5282     case PROP_SCALE_Z:
5283       {
5284         const ClutterTransformInfo *info;
5285 
5286         info = _clutter_actor_get_transform_info_or_defaults (actor);
5287         g_value_set_double (value, info->scale_z);
5288       }
5289       break;
5290 
5291     case PROP_REACTIVE:
5292       g_value_set_boolean (value, clutter_actor_get_reactive (actor));
5293       break;
5294 
5295     case PROP_ROTATION_ANGLE_X:
5296       {
5297         const ClutterTransformInfo *info;
5298 
5299         info = _clutter_actor_get_transform_info_or_defaults (actor);
5300         g_value_set_double (value, info->rx_angle);
5301       }
5302       break;
5303 
5304     case PROP_ROTATION_ANGLE_Y:
5305       {
5306         const ClutterTransformInfo *info;
5307 
5308         info = _clutter_actor_get_transform_info_or_defaults (actor);
5309         g_value_set_double (value, info->ry_angle);
5310       }
5311       break;
5312 
5313     case PROP_ROTATION_ANGLE_Z:
5314       {
5315         const ClutterTransformInfo *info;
5316 
5317         info = _clutter_actor_get_transform_info_or_defaults (actor);
5318         g_value_set_double (value, info->rz_angle);
5319       }
5320       break;
5321 
5322     case PROP_TRANSFORM:
5323       {
5324         graphene_matrix_t m;
5325 
5326         clutter_actor_get_transform (actor, &m);
5327         g_value_set_boxed (value, &m);
5328       }
5329       break;
5330 
5331     case PROP_TRANSFORM_SET:
5332       {
5333         const ClutterTransformInfo *info;
5334 
5335         info = _clutter_actor_get_transform_info_or_defaults (actor);
5336         g_value_set_boolean (value, info->transform_set);
5337       }
5338       break;
5339 
5340     case PROP_CHILD_TRANSFORM:
5341       {
5342         graphene_matrix_t m;
5343 
5344         clutter_actor_get_child_transform (actor, &m);
5345         g_value_set_boxed (value, &m);
5346       }
5347       break;
5348 
5349     case PROP_CHILD_TRANSFORM_SET:
5350       {
5351         const ClutterTransformInfo *info;
5352 
5353         info = _clutter_actor_get_transform_info_or_defaults (actor);
5354         g_value_set_boolean (value, info->child_transform_set);
5355       }
5356       break;
5357 
5358     case PROP_SHOW_ON_SET_PARENT: /* XXX:2.0 - remove */
5359       g_value_set_boolean (value, priv->show_on_set_parent);
5360       break;
5361 
5362     case PROP_TEXT_DIRECTION:
5363       g_value_set_enum (value, priv->text_direction);
5364       break;
5365 
5366     case PROP_HAS_POINTER:
5367       g_value_set_boolean (value, priv->has_pointer);
5368       break;
5369 
5370     case PROP_LAYOUT_MANAGER:
5371       g_value_set_object (value, priv->layout_manager);
5372       break;
5373 
5374     case PROP_X_EXPAND:
5375       {
5376         const ClutterLayoutInfo *info;
5377 
5378         info = _clutter_actor_get_layout_info_or_defaults (actor);
5379         g_value_set_boolean (value, info->x_expand);
5380       }
5381       break;
5382 
5383     case PROP_Y_EXPAND:
5384       {
5385         const ClutterLayoutInfo *info;
5386 
5387         info = _clutter_actor_get_layout_info_or_defaults (actor);
5388         g_value_set_boolean (value, info->y_expand);
5389       }
5390       break;
5391 
5392     case PROP_X_ALIGN:
5393       {
5394         const ClutterLayoutInfo *info;
5395 
5396         info = _clutter_actor_get_layout_info_or_defaults (actor);
5397         g_value_set_enum (value, info->x_align);
5398       }
5399       break;
5400 
5401     case PROP_Y_ALIGN:
5402       {
5403         const ClutterLayoutInfo *info;
5404 
5405         info = _clutter_actor_get_layout_info_or_defaults (actor);
5406         g_value_set_enum (value, info->y_align);
5407       }
5408       break;
5409 
5410     case PROP_MARGIN_TOP:
5411       {
5412         const ClutterLayoutInfo *info;
5413 
5414         info = _clutter_actor_get_layout_info_or_defaults (actor);
5415         g_value_set_float (value, info->margin.top);
5416       }
5417       break;
5418 
5419     case PROP_MARGIN_BOTTOM:
5420       {
5421         const ClutterLayoutInfo *info;
5422 
5423         info = _clutter_actor_get_layout_info_or_defaults (actor);
5424         g_value_set_float (value, info->margin.bottom);
5425       }
5426       break;
5427 
5428     case PROP_MARGIN_LEFT:
5429       {
5430         const ClutterLayoutInfo *info;
5431 
5432         info = _clutter_actor_get_layout_info_or_defaults (actor);
5433         g_value_set_float (value, info->margin.left);
5434       }
5435       break;
5436 
5437     case PROP_MARGIN_RIGHT:
5438       {
5439         const ClutterLayoutInfo *info;
5440 
5441         info = _clutter_actor_get_layout_info_or_defaults (actor);
5442         g_value_set_float (value, info->margin.right);
5443       }
5444       break;
5445 
5446     case PROP_BACKGROUND_COLOR_SET:
5447       g_value_set_boolean (value, priv->bg_color_set);
5448       break;
5449 
5450     case PROP_BACKGROUND_COLOR:
5451       g_value_set_boxed (value, &priv->bg_color);
5452       break;
5453 
5454     case PROP_FIRST_CHILD:
5455       g_value_set_object (value, priv->first_child);
5456       break;
5457 
5458     case PROP_LAST_CHILD:
5459       g_value_set_object (value, priv->last_child);
5460       break;
5461 
5462     case PROP_CONTENT:
5463       g_value_set_object (value, priv->content);
5464       break;
5465 
5466     case PROP_CONTENT_GRAVITY:
5467       g_value_set_enum (value, priv->content_gravity);
5468       break;
5469 
5470     case PROP_CONTENT_BOX:
5471       {
5472         ClutterActorBox box = { 0, };
5473 
5474         clutter_actor_get_content_box (actor, &box);
5475         g_value_set_boxed (value, &box);
5476       }
5477       break;
5478 
5479     case PROP_MINIFICATION_FILTER:
5480       g_value_set_enum (value, priv->min_filter);
5481       break;
5482 
5483     case PROP_MAGNIFICATION_FILTER:
5484       g_value_set_enum (value, priv->mag_filter);
5485       break;
5486 
5487     case PROP_CONTENT_REPEAT:
5488       g_value_set_flags (value, priv->content_repeat);
5489       break;
5490 
5491     default:
5492       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5493       break;
5494     }
5495 }
5496 
5497 static void
clutter_actor_dispose(GObject * object)5498 clutter_actor_dispose (GObject *object)
5499 {
5500   ClutterActor *self = CLUTTER_ACTOR (object);
5501   ClutterActorPrivate *priv = self->priv;
5502   ClutterBackend *backend = clutter_get_default_backend ();
5503 
5504   CLUTTER_NOTE (MISC, "Dispose actor (name='%s', ref_count:%d) of type '%s'",
5505 		_clutter_actor_get_debug_name (self),
5506                 object->ref_count,
5507 		g_type_name (G_OBJECT_TYPE (self)));
5508 
5509   maybe_unset_key_focus (self);
5510 
5511   /* Stop the emission of any property change */
5512   g_object_freeze_notify (object);
5513 
5514   g_signal_emit (self, actor_signals[DESTROY], 0);
5515 
5516   /* avoid recursing when called from clutter_actor_destroy() */
5517   if (priv->parent != NULL)
5518     {
5519       ClutterActor *parent = priv->parent;
5520       clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self);
5521     }
5522 
5523   /* parent must be gone at this point */
5524   g_assert (priv->parent == NULL);
5525 
5526   if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
5527     {
5528       /* can't be mapped or realized with no parent */
5529       g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
5530       g_assert (!CLUTTER_ACTOR_IS_REALIZED (self));
5531     }
5532 
5533   g_clear_signal_handler (&priv->resolution_changed_id, backend);
5534   g_clear_signal_handler (&priv->font_changed_id, backend);
5535 
5536   g_clear_object (&priv->pango_context);
5537   g_clear_object (&priv->actions);
5538   g_clear_object (&priv->constraints);
5539   g_clear_object (&priv->effects);
5540   g_clear_object (&priv->flatten_effect);
5541 
5542   if (priv->child_model != NULL)
5543     {
5544       if (priv->create_child_notify != NULL)
5545         priv->create_child_notify (priv->create_child_data);
5546 
5547       priv->create_child_func = NULL;
5548       priv->create_child_data = NULL;
5549       priv->create_child_notify = NULL;
5550 
5551       g_clear_object (&priv->child_model);
5552     }
5553 
5554   if (priv->layout_manager != NULL)
5555     {
5556       g_clear_signal_handler (&priv->layout_changed_id, priv->layout_manager);
5557       clutter_layout_manager_set_container (priv->layout_manager, NULL);
5558       g_clear_object (&priv->layout_manager);
5559     }
5560 
5561   if (priv->content != NULL)
5562     {
5563       _clutter_content_detached (priv->content, self);
5564       g_clear_object (&priv->content);
5565     }
5566 
5567   if (priv->clones != NULL)
5568     {
5569       g_hash_table_unref (priv->clones);
5570       priv->clones = NULL;
5571     }
5572 
5573   g_clear_pointer (&priv->stage_views, g_list_free);
5574 
5575   G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object);
5576 }
5577 
5578 static void
clutter_actor_finalize(GObject * object)5579 clutter_actor_finalize (GObject *object)
5580 {
5581   ClutterActorPrivate *priv = CLUTTER_ACTOR (object)->priv;
5582 
5583   CLUTTER_NOTE (MISC, "Finalize actor (name='%s') of type '%s'",
5584                 _clutter_actor_get_debug_name ((ClutterActor *) object),
5585                 g_type_name (G_OBJECT_TYPE (object)));
5586 
5587   g_free (priv->name);
5588 
5589   g_free (priv->debug_name);
5590 
5591   G_OBJECT_CLASS (clutter_actor_parent_class)->finalize (object);
5592 }
5593 
5594 
5595 /**
5596  * clutter_actor_get_accessible:
5597  * @self: a #ClutterActor
5598  *
5599  * Returns the accessible object that describes the actor to an
5600  * assistive technology.
5601  *
5602  * If no class-specific #AtkObject implementation is available for the
5603  * actor instance in question, it will inherit an #AtkObject
5604  * implementation from the first ancestor class for which such an
5605  * implementation is defined.
5606  *
5607  * The documentation of the <ulink
5608  * url="http://developer.gnome.org/doc/API/2.0/atk/index.html">ATK</ulink>
5609  * library contains more information about accessible objects and
5610  * their uses.
5611  *
5612  * Returns: (transfer none): the #AtkObject associated with @actor
5613  */
5614 AtkObject *
clutter_actor_get_accessible(ClutterActor * self)5615 clutter_actor_get_accessible (ClutterActor *self)
5616 {
5617   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
5618 
5619   return CLUTTER_ACTOR_GET_CLASS (self)->get_accessible (self);
5620 }
5621 
5622 static AtkObject *
clutter_actor_real_get_accessible(ClutterActor * actor)5623 clutter_actor_real_get_accessible (ClutterActor *actor)
5624 {
5625   return atk_gobject_accessible_for_object (G_OBJECT (actor));
5626 }
5627 
5628 static AtkObject *
_clutter_actor_ref_accessible(AtkImplementor * implementor)5629 _clutter_actor_ref_accessible (AtkImplementor *implementor)
5630 {
5631   AtkObject *accessible;
5632 
5633   accessible = clutter_actor_get_accessible (CLUTTER_ACTOR (implementor));
5634   if (accessible != NULL)
5635     g_object_ref (accessible);
5636 
5637   return accessible;
5638 }
5639 
5640 static void
atk_implementor_iface_init(AtkImplementorIface * iface)5641 atk_implementor_iface_init (AtkImplementorIface *iface)
5642 {
5643   iface->ref_accessible = _clutter_actor_ref_accessible;
5644 }
5645 
5646 static gboolean
clutter_actor_update_default_paint_volume(ClutterActor * self,ClutterPaintVolume * volume)5647 clutter_actor_update_default_paint_volume (ClutterActor       *self,
5648                                            ClutterPaintVolume *volume)
5649 {
5650   ClutterActorPrivate *priv = self->priv;
5651   gboolean res = TRUE;
5652 
5653   /* this should be checked before we call this function, but it's a
5654    * good idea to be explicit when it costs us nothing
5655    */
5656   if (priv->needs_allocation)
5657     return FALSE;
5658 
5659   if (priv->has_clip)
5660     {
5661       graphene_point3d_t origin;
5662 
5663       origin.x = priv->clip.origin.x;
5664       origin.y = priv->clip.origin.y;
5665       origin.z = 0;
5666 
5667       clutter_paint_volume_set_origin (volume, &origin);
5668       clutter_paint_volume_set_width (volume, priv->clip.size.width);
5669       clutter_paint_volume_set_height (volume, priv->clip.size.height);
5670 
5671       return TRUE;
5672     }
5673 
5674   /* we start from the allocation */
5675   clutter_paint_volume_set_width (volume,
5676                                   priv->allocation.x2 - priv->allocation.x1);
5677   clutter_paint_volume_set_height (volume,
5678                                    priv->allocation.y2 - priv->allocation.y1);
5679 
5680   /* if the actor has a clip set then we have a pretty definite
5681    * size for the paint volume: the actor cannot possibly paint
5682    * outside the clip region.
5683    */
5684   if (priv->clip_to_allocation)
5685     {
5686       /* the allocation has already been set, so we just flip the
5687        * return value
5688        */
5689       res = TRUE;
5690     }
5691   else
5692     {
5693       ClutterActor *child;
5694 
5695       /* if we don't have children we just bail out here... */
5696       if (priv->n_children == 0)
5697         return res;
5698 
5699       /* ...but if we have children then we ask for their paint volume in
5700        * our coordinates. if any of our children replies that it doesn't
5701        * have a paint volume, we bail out
5702        */
5703       for (child = priv->first_child;
5704            child != NULL;
5705            child = child->priv->next_sibling)
5706         {
5707           const ClutterPaintVolume *child_volume;
5708 
5709           /* we ignore unmapped children, since they won't be painted.
5710            *
5711            * XXX: we also have to ignore mapped children without a valid
5712            * allocation, because apparently some code above Clutter allows
5713            * them.
5714            */
5715           if (!CLUTTER_ACTOR_IS_MAPPED (child) || !clutter_actor_has_allocation (child))
5716             continue;
5717 
5718           child_volume = clutter_actor_get_transformed_paint_volume (child, self);
5719           if (child_volume == NULL)
5720             {
5721               res = FALSE;
5722               break;
5723             }
5724 
5725           clutter_paint_volume_union (volume, child_volume);
5726           res = TRUE;
5727         }
5728     }
5729 
5730   return res;
5731 
5732 }
5733 
5734 static gboolean
clutter_actor_real_get_paint_volume(ClutterActor * self,ClutterPaintVolume * volume)5735 clutter_actor_real_get_paint_volume (ClutterActor       *self,
5736                                      ClutterPaintVolume *volume)
5737 {
5738   ClutterActorClass *klass;
5739   gboolean res;
5740 
5741   klass = CLUTTER_ACTOR_GET_CLASS (self);
5742 
5743   /* XXX - this thoroughly sucks, but we don't want to penalize users
5744    * who use ClutterActor as a "new ClutterGroup" by forcing a full-stage
5745    * redraw. This should go away in 2.0.
5746    */
5747   if (klass->paint == clutter_actor_real_paint &&
5748       klass->get_paint_volume == clutter_actor_real_get_paint_volume)
5749     {
5750       res = TRUE;
5751     }
5752   else
5753     {
5754       /* this is the default return value: we cannot know if a class
5755        * is going to paint outside its allocation, so we take the
5756        * conservative approach.
5757        */
5758       res = FALSE;
5759     }
5760 
5761   /* update_default_paint_volume() should only fail if one of the children
5762    * reported an invalid, or no, paint volume
5763    */
5764   if (!clutter_actor_update_default_paint_volume (self, volume))
5765     return FALSE;
5766 
5767   return res;
5768 }
5769 
5770 /**
5771  * clutter_actor_get_default_paint_volume:
5772  * @self: a #ClutterActor
5773  *
5774  * Retrieves the default paint volume for @self.
5775  *
5776  * This function provides the same #ClutterPaintVolume that would be
5777  * computed by the default implementation inside #ClutterActor of the
5778  * #ClutterActorClass.get_paint_volume() virtual function.
5779  *
5780  * This function should only be used by #ClutterActor subclasses that
5781  * cannot chain up to the parent implementation when computing their
5782  * paint volume.
5783  *
5784  * Return value: (transfer none): a pointer to the default
5785  *   #ClutterPaintVolume, relative to the #ClutterActor, or %NULL if
5786  *   the actor could not compute a valid paint volume. The returned value
5787  *   is not guaranteed to be stable across multiple frames, so if you
5788  *   want to retain it, you will need to copy it using
5789  *   clutter_paint_volume_copy().
5790  *
5791  * Since: 1.10
5792  */
5793 const ClutterPaintVolume *
clutter_actor_get_default_paint_volume(ClutterActor * self)5794 clutter_actor_get_default_paint_volume (ClutterActor *self)
5795 {
5796   ClutterPaintVolume volume;
5797   ClutterPaintVolume *res;
5798 
5799   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
5800 
5801   res = NULL;
5802   _clutter_paint_volume_init_static (&volume, self);
5803   if (clutter_actor_update_default_paint_volume (self, &volume))
5804     {
5805       ClutterActor *stage = _clutter_actor_get_stage_internal (self);
5806 
5807       if (stage != NULL)
5808         {
5809           res = _clutter_stage_paint_volume_stack_allocate (CLUTTER_STAGE (stage));
5810           _clutter_paint_volume_copy_static (&volume, res);
5811         }
5812     }
5813 
5814   clutter_paint_volume_free (&volume);
5815 
5816   return res;
5817 }
5818 
5819 static gboolean
clutter_actor_real_has_overlaps(ClutterActor * self)5820 clutter_actor_real_has_overlaps (ClutterActor *self)
5821 {
5822   /* By default we'll assume that all actors need an offscreen redirect to get
5823    * the correct opacity. Actors such as ClutterTexture that would never need
5824    * an offscreen redirect can override this to return FALSE. */
5825   return TRUE;
5826 }
5827 
5828 static float
clutter_actor_real_calculate_resource_scale(ClutterActor * self,int phase)5829 clutter_actor_real_calculate_resource_scale (ClutterActor *self,
5830                                              int           phase)
5831 {
5832   ClutterActorPrivate *priv = self->priv;
5833   GList *l;
5834   float new_resource_scale = -1.f;
5835 
5836   for (l = priv->stage_views; l; l = l->next)
5837     {
5838       ClutterStageView *view = l->data;
5839 
5840       new_resource_scale = MAX (clutter_stage_view_get_scale (view),
5841                                 new_resource_scale);
5842     }
5843 
5844   return new_resource_scale;
5845 }
5846 
5847 static void
clutter_actor_real_destroy(ClutterActor * actor)5848 clutter_actor_real_destroy (ClutterActor *actor)
5849 {
5850   ClutterActorIter iter;
5851 
5852   g_object_freeze_notify (G_OBJECT (actor));
5853 
5854   clutter_actor_iter_init (&iter, actor);
5855   while (clutter_actor_iter_next (&iter, NULL))
5856     clutter_actor_iter_destroy (&iter);
5857 
5858   g_object_thaw_notify (G_OBJECT (actor));
5859 }
5860 
5861 static GObject *
clutter_actor_constructor(GType gtype,guint n_props,GObjectConstructParam * props)5862 clutter_actor_constructor (GType gtype,
5863                            guint n_props,
5864                            GObjectConstructParam *props)
5865 {
5866   GObjectClass *gobject_class;
5867   ClutterActor *self;
5868   GObject *retval;
5869 
5870   gobject_class = G_OBJECT_CLASS (clutter_actor_parent_class);
5871   retval = gobject_class->constructor (gtype, n_props, props);
5872   self = CLUTTER_ACTOR (retval);
5873 
5874   if (self->priv->layout_manager == NULL)
5875     {
5876       ClutterLayoutManager *default_layout;
5877 
5878       CLUTTER_NOTE (LAYOUT, "Creating default layout manager");
5879 
5880       default_layout = clutter_fixed_layout_new ();
5881       clutter_actor_set_layout_manager (self, default_layout);
5882     }
5883 
5884   return retval;
5885 }
5886 
5887 static void
clutter_actor_class_init(ClutterActorClass * klass)5888 clutter_actor_class_init (ClutterActorClass *klass)
5889 {
5890   GObjectClass *object_class = G_OBJECT_CLASS (klass);
5891 
5892   quark_actor_layout_info = g_quark_from_static_string ("-clutter-actor-layout-info");
5893   quark_actor_transform_info = g_quark_from_static_string ("-clutter-actor-transform-info");
5894   quark_actor_animation_info = g_quark_from_static_string ("-clutter-actor-animation-info");
5895 
5896   quark_key = g_quark_from_static_string ("key");
5897   quark_motion = g_quark_from_static_string ("motion");
5898   quark_pointer_focus = g_quark_from_static_string ("pointer-focus");
5899   quark_button = g_quark_from_static_string ("button");
5900   quark_scroll = g_quark_from_static_string ("scroll");
5901   quark_stage = g_quark_from_static_string ("stage");
5902   quark_touch = g_quark_from_static_string ("touch");
5903   quark_touchpad = g_quark_from_static_string ("touchpad");
5904   quark_proximity = g_quark_from_static_string ("proximity");
5905   quark_pad = g_quark_from_static_string ("pad");
5906   quark_im = g_quark_from_static_string ("im");
5907 
5908   object_class->constructor = clutter_actor_constructor;
5909   object_class->set_property = clutter_actor_set_property;
5910   object_class->get_property = clutter_actor_get_property;
5911   object_class->dispose = clutter_actor_dispose;
5912   object_class->finalize = clutter_actor_finalize;
5913 
5914   klass->show = clutter_actor_real_show;
5915   klass->hide = clutter_actor_real_hide;
5916   klass->hide_all = clutter_actor_hide;
5917   klass->map = clutter_actor_real_map;
5918   klass->unmap = clutter_actor_real_unmap;
5919   klass->unrealize = clutter_actor_real_unrealize;
5920   klass->pick = clutter_actor_real_pick;
5921   klass->get_preferred_width = clutter_actor_real_get_preferred_width;
5922   klass->get_preferred_height = clutter_actor_real_get_preferred_height;
5923   klass->allocate = clutter_actor_real_allocate;
5924   klass->queue_relayout = clutter_actor_real_queue_relayout;
5925   klass->apply_transform = clutter_actor_real_apply_transform;
5926   klass->get_accessible = clutter_actor_real_get_accessible;
5927   klass->get_paint_volume = clutter_actor_real_get_paint_volume;
5928   klass->has_overlaps = clutter_actor_real_has_overlaps;
5929   klass->calculate_resource_scale = clutter_actor_real_calculate_resource_scale;
5930   klass->paint = clutter_actor_real_paint;
5931   klass->destroy = clutter_actor_real_destroy;
5932 
5933   /**
5934    * ClutterActor:x:
5935    *
5936    * X coordinate of the actor in pixels. If written, forces a fixed
5937    * position for the actor. If read, returns the fixed position if any,
5938    * otherwise the allocation if available, otherwise 0.
5939    *
5940    * The #ClutterActor:x property is animatable.
5941    */
5942   obj_props[PROP_X] =
5943     g_param_spec_float ("x",
5944                         P_("X coordinate"),
5945                         P_("X coordinate of the actor"),
5946                         -G_MAXFLOAT, G_MAXFLOAT,
5947                         0.0,
5948                         G_PARAM_READWRITE |
5949                         G_PARAM_STATIC_STRINGS |
5950                         G_PARAM_EXPLICIT_NOTIFY |
5951                         CLUTTER_PARAM_ANIMATABLE);
5952 
5953   /**
5954    * ClutterActor:y:
5955    *
5956    * Y coordinate of the actor in pixels. If written, forces a fixed
5957    * position for the actor.  If read, returns the fixed position if
5958    * any, otherwise the allocation if available, otherwise 0.
5959    *
5960    * The #ClutterActor:y property is animatable.
5961    */
5962   obj_props[PROP_Y] =
5963     g_param_spec_float ("y",
5964                         P_("Y coordinate"),
5965                         P_("Y coordinate of the actor"),
5966                         -G_MAXFLOAT, G_MAXFLOAT,
5967                         0.0,
5968                         G_PARAM_READWRITE |
5969                         G_PARAM_STATIC_STRINGS |
5970                         G_PARAM_EXPLICIT_NOTIFY |
5971                         CLUTTER_PARAM_ANIMATABLE);
5972 
5973   /**
5974    * ClutterActor:position:
5975    *
5976    * The position of the origin of the actor.
5977    *
5978    * This property is a shorthand for setting and getting the
5979    * #ClutterActor:x and #ClutterActor:y properties at the same
5980    * time.
5981    *
5982    * The #ClutterActor:position property is animatable.
5983    *
5984    * Since: 1.12
5985    */
5986   obj_props[PROP_POSITION] =
5987     g_param_spec_boxed ("position",
5988                         P_("Position"),
5989                         P_("The position of the origin of the actor"),
5990                         GRAPHENE_TYPE_POINT,
5991                         G_PARAM_READWRITE |
5992                         G_PARAM_STATIC_STRINGS |
5993                         G_PARAM_EXPLICIT_NOTIFY |
5994                         CLUTTER_PARAM_ANIMATABLE);
5995 
5996   /**
5997    * ClutterActor:width:
5998    *
5999    * Width of the actor (in pixels). If written, forces the minimum and
6000    * natural size request of the actor to the given width. If read, returns
6001    * the allocated width if available, otherwise the width request.
6002    *
6003    * The #ClutterActor:width property is animatable.
6004    */
6005   obj_props[PROP_WIDTH] =
6006     g_param_spec_float ("width",
6007                         P_("Width"),
6008                         P_("Width of the actor"),
6009                         -1.0f, G_MAXFLOAT,
6010                         0.0,
6011                         G_PARAM_READWRITE |
6012                         G_PARAM_STATIC_STRINGS |
6013                         G_PARAM_EXPLICIT_NOTIFY |
6014                         CLUTTER_PARAM_ANIMATABLE);
6015 
6016   /**
6017    * ClutterActor:height:
6018    *
6019    * Height of the actor (in pixels).  If written, forces the minimum and
6020    * natural size request of the actor to the given height. If read, returns
6021    * the allocated height if available, otherwise the height request.
6022    *
6023    * The #ClutterActor:height property is animatable.
6024    */
6025   obj_props[PROP_HEIGHT] =
6026     g_param_spec_float ("height",
6027                         P_("Height"),
6028                         P_("Height of the actor"),
6029                         -1.0f, G_MAXFLOAT,
6030                         0.0,
6031                         G_PARAM_READWRITE |
6032                         G_PARAM_STATIC_STRINGS |
6033                         G_PARAM_EXPLICIT_NOTIFY |
6034                         CLUTTER_PARAM_ANIMATABLE);
6035 
6036   /**
6037    * ClutterActor:size:
6038    *
6039    * The size of the actor.
6040    *
6041    * This property is a shorthand for setting and getting the
6042    * #ClutterActor:width and #ClutterActor:height at the same time.
6043    *
6044    * The #ClutterActor:size property is animatable.
6045    *
6046    * Since: 1.12
6047    */
6048   obj_props[PROP_SIZE] =
6049     g_param_spec_boxed ("size",
6050                         P_("Size"),
6051                         P_("The size of the actor"),
6052                         GRAPHENE_TYPE_SIZE,
6053                         G_PARAM_READWRITE |
6054                         G_PARAM_STATIC_STRINGS |
6055                         G_PARAM_EXPLICIT_NOTIFY |
6056                         CLUTTER_PARAM_ANIMATABLE);
6057 
6058   /**
6059    * ClutterActor:fixed-x:
6060    *
6061    * The fixed X position of the actor in pixels.
6062    *
6063    * Writing this property sets #ClutterActor:fixed-position-set
6064    * property as well, as a side effect
6065    *
6066    * Since: 0.8
6067    */
6068   obj_props[PROP_FIXED_X] =
6069     g_param_spec_float ("fixed-x",
6070                         P_("Fixed X"),
6071                         P_("Forced X position of the actor"),
6072                         -G_MAXFLOAT, G_MAXFLOAT,
6073                         0.0,
6074                         CLUTTER_PARAM_READWRITE |
6075                         G_PARAM_EXPLICIT_NOTIFY);
6076 
6077   /**
6078    * ClutterActor:fixed-y:
6079    *
6080    * The fixed Y position of the actor in pixels.
6081    *
6082    * Writing this property sets the #ClutterActor:fixed-position-set
6083    * property as well, as a side effect
6084    *
6085    * Since: 0.8
6086    */
6087   obj_props[PROP_FIXED_Y] =
6088     g_param_spec_float ("fixed-y",
6089                         P_("Fixed Y"),
6090                         P_("Forced Y position of the actor"),
6091                         -G_MAXFLOAT, G_MAXFLOAT,
6092                         0,
6093                         CLUTTER_PARAM_READWRITE |
6094                         G_PARAM_EXPLICIT_NOTIFY);
6095 
6096   /**
6097    * ClutterActor:fixed-position-set:
6098    *
6099    * This flag controls whether the #ClutterActor:fixed-x and
6100    * #ClutterActor:fixed-y properties are used
6101    *
6102    * Since: 0.8
6103    */
6104   obj_props[PROP_FIXED_POSITION_SET] =
6105     g_param_spec_boolean ("fixed-position-set",
6106                           P_("Fixed position set"),
6107                           P_("Whether to use fixed positioning for the actor"),
6108                           FALSE,
6109                           CLUTTER_PARAM_READWRITE |
6110                           G_PARAM_EXPLICIT_NOTIFY);
6111 
6112   /**
6113    * ClutterActor:min-width:
6114    *
6115    * A forced minimum width request for the actor, in pixels
6116    *
6117    * Writing this property sets the #ClutterActor:min-width-set property
6118    * as well, as a side effect.
6119    *
6120    *This property overrides the usual width request of the actor.
6121    *
6122    * Since: 0.8
6123    */
6124   obj_props[PROP_MIN_WIDTH] =
6125     g_param_spec_float ("min-width",
6126                         P_("Min Width"),
6127                         P_("Forced minimum width request for the actor"),
6128                         0.0, G_MAXFLOAT,
6129                         0.0,
6130                         CLUTTER_PARAM_READWRITE |
6131                         G_PARAM_EXPLICIT_NOTIFY);
6132 
6133   /**
6134    * ClutterActor:min-height:
6135    *
6136    * A forced minimum height request for the actor, in pixels
6137    *
6138    * Writing this property sets the #ClutterActor:min-height-set property
6139    * as well, as a side effect. This property overrides the usual height
6140    * request of the actor.
6141    *
6142    * Since: 0.8
6143    */
6144   obj_props[PROP_MIN_HEIGHT] =
6145     g_param_spec_float ("min-height",
6146                         P_("Min Height"),
6147                         P_("Forced minimum height request for the actor"),
6148                         0.0, G_MAXFLOAT,
6149                         0.0,
6150                         CLUTTER_PARAM_READWRITE |
6151                         G_PARAM_EXPLICIT_NOTIFY);
6152 
6153   /**
6154    * ClutterActor:natural-width:
6155    *
6156    * A forced natural width request for the actor, in pixels
6157    *
6158    * Writing this property sets the #ClutterActor:natural-width-set
6159    * property as well, as a side effect. This property overrides the
6160    * usual width request of the actor
6161    *
6162    * Since: 0.8
6163    */
6164   obj_props[PROP_NATURAL_WIDTH] =
6165     g_param_spec_float ("natural-width",
6166                         P_("Natural Width"),
6167                         P_("Forced natural width request for the actor"),
6168                         0.0, G_MAXFLOAT,
6169                         0.0,
6170                         CLUTTER_PARAM_READWRITE |
6171                         G_PARAM_EXPLICIT_NOTIFY);
6172 
6173   /**
6174    * ClutterActor:natural-height:
6175    *
6176    * A forced natural height request for the actor, in pixels
6177    *
6178    * Writing this property sets the #ClutterActor:natural-height-set
6179    * property as well, as a side effect. This property overrides the
6180    * usual height request of the actor
6181    *
6182    * Since: 0.8
6183    */
6184   obj_props[PROP_NATURAL_HEIGHT] =
6185     g_param_spec_float ("natural-height",
6186                         P_("Natural Height"),
6187                         P_("Forced natural height request for the actor"),
6188                         0.0, G_MAXFLOAT,
6189                         0.0,
6190                         CLUTTER_PARAM_READWRITE |
6191                         G_PARAM_EXPLICIT_NOTIFY);
6192 
6193   /**
6194    * ClutterActor:min-width-set:
6195    *
6196    * This flag controls whether the #ClutterActor:min-width property
6197    * is used
6198    *
6199    * Since: 0.8
6200    */
6201   obj_props[PROP_MIN_WIDTH_SET] =
6202     g_param_spec_boolean ("min-width-set",
6203                           P_("Minimum width set"),
6204                           P_("Whether to use the min-width property"),
6205                           FALSE,
6206                           CLUTTER_PARAM_READWRITE |
6207                           G_PARAM_EXPLICIT_NOTIFY);
6208 
6209   /**
6210    * ClutterActor:min-height-set:
6211    *
6212    * This flag controls whether the #ClutterActor:min-height property
6213    * is used
6214    *
6215    * Since: 0.8
6216    */
6217   obj_props[PROP_MIN_HEIGHT_SET] =
6218     g_param_spec_boolean ("min-height-set",
6219                           P_("Minimum height set"),
6220                           P_("Whether to use the min-height property"),
6221                           FALSE,
6222                           CLUTTER_PARAM_READWRITE |
6223                           G_PARAM_EXPLICIT_NOTIFY);
6224 
6225   /**
6226    * ClutterActor:natural-width-set:
6227    *
6228    * This flag controls whether the #ClutterActor:natural-width property
6229    * is used
6230    *
6231    * Since: 0.8
6232    */
6233   obj_props[PROP_NATURAL_WIDTH_SET] =
6234     g_param_spec_boolean ("natural-width-set",
6235                           P_("Natural width set"),
6236                           P_("Whether to use the natural-width property"),
6237                           FALSE,
6238                           CLUTTER_PARAM_READWRITE |
6239                           G_PARAM_EXPLICIT_NOTIFY);
6240 
6241   /**
6242    * ClutterActor:natural-height-set:
6243    *
6244    * This flag controls whether the #ClutterActor:natural-height property
6245    * is used
6246    *
6247    * Since: 0.8
6248    */
6249   obj_props[PROP_NATURAL_HEIGHT_SET] =
6250     g_param_spec_boolean ("natural-height-set",
6251                           P_("Natural height set"),
6252                           P_("Whether to use the natural-height property"),
6253                           FALSE,
6254                           CLUTTER_PARAM_READWRITE |
6255                           G_PARAM_EXPLICIT_NOTIFY);
6256 
6257   /**
6258    * ClutterActor:allocation:
6259    *
6260    * The allocation for the actor, in pixels
6261    *
6262    * This is property is read-only, but you might monitor it to know when an
6263    * actor moves or resizes
6264    *
6265    * Since: 0.8
6266    */
6267   obj_props[PROP_ALLOCATION] =
6268     g_param_spec_boxed ("allocation",
6269                         P_("Allocation"),
6270                         P_("The actor's allocation"),
6271                         CLUTTER_TYPE_ACTOR_BOX,
6272                         G_PARAM_READABLE |
6273                         G_PARAM_STATIC_STRINGS |
6274                         G_PARAM_EXPLICIT_NOTIFY |
6275                         CLUTTER_PARAM_ANIMATABLE);
6276 
6277   /**
6278    * ClutterActor:request-mode:
6279    *
6280    * Request mode for the #ClutterActor. The request mode determines the
6281    * type of geometry management used by the actor, either height for width
6282    * (the default) or width for height.
6283    *
6284    * For actors implementing height for width, the parent container should get
6285    * the preferred width first, and then the preferred height for that width.
6286    *
6287    * For actors implementing width for height, the parent container should get
6288    * the preferred height first, and then the preferred width for that height.
6289    *
6290    * For instance:
6291    *
6292    * |[<!-- language="C" -->
6293    *   ClutterRequestMode mode;
6294    *   gfloat natural_width, min_width;
6295    *   gfloat natural_height, min_height;
6296    *
6297    *   mode = clutter_actor_get_request_mode (child);
6298    *   if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
6299    *     {
6300    *       clutter_actor_get_preferred_width (child, -1,
6301    *                                          &min_width,
6302    *                                          &natural_width);
6303    *       clutter_actor_get_preferred_height (child, natural_width,
6304    *                                           &min_height,
6305    *                                           &natural_height);
6306    *     }
6307    *   else if (mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
6308    *     {
6309    *       clutter_actor_get_preferred_height (child, -1,
6310    *                                           &min_height,
6311    *                                           &natural_height);
6312    *       clutter_actor_get_preferred_width (child, natural_height,
6313    *                                          &min_width,
6314    *                                          &natural_width);
6315    *     }
6316    *   else if (mode == CLUTTER_REQUEST_CONTENT_SIZE)
6317    *     {
6318    *       ClutterContent *content = clutter_actor_get_content (child);
6319    *
6320    *       min_width, min_height = 0;
6321    *       natural_width = natural_height = 0;
6322    *
6323    *       if (content != NULL)
6324    *         clutter_content_get_preferred_size (content, &natural_width, &natural_height);
6325    *     }
6326    * ]|
6327    *
6328    * will retrieve the minimum and natural width and height depending on the
6329    * preferred request mode of the #ClutterActor "child".
6330    *
6331    * The clutter_actor_get_preferred_size() function will implement this
6332    * check for you.
6333    *
6334    * Since: 0.8
6335    */
6336   obj_props[PROP_REQUEST_MODE] =
6337     g_param_spec_enum ("request-mode",
6338                        P_("Request Mode"),
6339                        P_("The actor's request mode"),
6340                        CLUTTER_TYPE_REQUEST_MODE,
6341                        CLUTTER_REQUEST_HEIGHT_FOR_WIDTH,
6342                        CLUTTER_PARAM_READWRITE |
6343                        G_PARAM_EXPLICIT_NOTIFY);
6344 
6345   /**
6346    * ClutterActor:z-position:
6347    *
6348    * The actor's position on the Z axis, relative to the parent's
6349    * transformations.
6350    *
6351    * Positive values will bring the actor's position nearer to the user,
6352    * whereas negative values will bring the actor's position farther from
6353    * the user.
6354    *
6355    * The #ClutterActor:z-position does not affect the paint or allocation
6356    * order.
6357    *
6358    * The #ClutterActor:z-position property is animatable.
6359    *
6360    * Since: 1.12
6361    */
6362   obj_props[PROP_Z_POSITION] =
6363     g_param_spec_float ("z-position",
6364                         P_("Z Position"),
6365                         P_("The actor's position on the Z axis"),
6366                         -G_MAXFLOAT, G_MAXFLOAT,
6367                         0.0f,
6368                         G_PARAM_READWRITE |
6369                         G_PARAM_STATIC_STRINGS |
6370                         G_PARAM_EXPLICIT_NOTIFY |
6371                         CLUTTER_PARAM_ANIMATABLE);
6372 
6373   /**
6374    * ClutterActor:opacity:
6375    *
6376    * Opacity of an actor, between 0 (fully transparent) and
6377    * 255 (fully opaque)
6378    *
6379    * The #ClutterActor:opacity property is animatable.
6380    */
6381   obj_props[PROP_OPACITY] =
6382     g_param_spec_uint ("opacity",
6383                        P_("Opacity"),
6384                        P_("Opacity of an actor"),
6385                        0, 255,
6386                        255,
6387                        G_PARAM_READWRITE |
6388                        G_PARAM_STATIC_STRINGS |
6389                        G_PARAM_EXPLICIT_NOTIFY |
6390                        CLUTTER_PARAM_ANIMATABLE);
6391 
6392   /**
6393    * ClutterActor:offscreen-redirect:
6394    *
6395    * Determines the conditions in which the actor will be redirected
6396    * to an offscreen framebuffer while being painted. For example this
6397    * can be used to cache an actor in a framebuffer or for improved
6398    * handling of transparent actors. See
6399    * clutter_actor_set_offscreen_redirect() for details.
6400    *
6401    * Since: 1.8
6402    */
6403   obj_props[PROP_OFFSCREEN_REDIRECT] =
6404     g_param_spec_flags ("offscreen-redirect",
6405                         P_("Offscreen redirect"),
6406                         P_("Flags controlling when to flatten the actor into a single image"),
6407                         CLUTTER_TYPE_OFFSCREEN_REDIRECT,
6408                         0,
6409                         CLUTTER_PARAM_READWRITE);
6410 
6411   /**
6412    * ClutterActor:visible:
6413    *
6414    * Whether the actor is set to be visible or not
6415    *
6416    * See also #ClutterActor:mapped
6417    */
6418   obj_props[PROP_VISIBLE] =
6419     g_param_spec_boolean ("visible",
6420                           P_("Visible"),
6421                           P_("Whether the actor is visible or not"),
6422                           FALSE,
6423                           CLUTTER_PARAM_READWRITE |
6424                           G_PARAM_EXPLICIT_NOTIFY);
6425 
6426   /**
6427    * ClutterActor:mapped:
6428    *
6429    * Whether the actor is mapped (will be painted when the stage
6430    * to which it belongs is mapped)
6431    *
6432    * Since: 1.0
6433    */
6434   obj_props[PROP_MAPPED] =
6435     g_param_spec_boolean ("mapped",
6436                           P_("Mapped"),
6437                           P_("Whether the actor will be painted"),
6438                           FALSE,
6439                           CLUTTER_PARAM_READABLE |
6440                           G_PARAM_EXPLICIT_NOTIFY);
6441 
6442   /**
6443    * ClutterActor:realized:
6444    *
6445    * Whether the actor has been realized
6446    *
6447    * Since: 1.0
6448    */
6449   obj_props[PROP_REALIZED] =
6450     g_param_spec_boolean ("realized",
6451                           P_("Realized"),
6452                           P_("Whether the actor has been realized"),
6453                           FALSE,
6454                           CLUTTER_PARAM_READABLE |
6455                           G_PARAM_EXPLICIT_NOTIFY);
6456 
6457   /**
6458    * ClutterActor:reactive:
6459    *
6460    * Whether the actor is reactive to events or not
6461    *
6462    * Only reactive actors will emit event-related signals
6463    *
6464    * Since: 0.6
6465    */
6466   obj_props[PROP_REACTIVE] =
6467     g_param_spec_boolean ("reactive",
6468                           P_("Reactive"),
6469                           P_("Whether the actor is reactive to events"),
6470                           FALSE,
6471                           CLUTTER_PARAM_READWRITE |
6472                           G_PARAM_EXPLICIT_NOTIFY);
6473 
6474   /**
6475    * ClutterActor:has-clip:
6476    *
6477    * Whether the actor has the #ClutterActor:clip property set or not
6478    */
6479   obj_props[PROP_HAS_CLIP] =
6480     g_param_spec_boolean ("has-clip",
6481                           P_("Has Clip"),
6482                           P_("Whether the actor has a clip set"),
6483                           FALSE,
6484                           CLUTTER_PARAM_READABLE |
6485                           G_PARAM_EXPLICIT_NOTIFY);
6486 
6487   /**
6488    * ClutterActor:clip-rect:
6489    *
6490    * The visible region of the actor, in actor-relative coordinates,
6491    * expressed as a #graphene_rect_t.
6492    *
6493    * Setting this property to %NULL will unset the existing clip.
6494    *
6495    * Setting this property will change the #ClutterActor:has-clip
6496    * property as a side effect.
6497    *
6498    * Since: 1.12
6499    */
6500   obj_props[PROP_CLIP_RECT] =
6501     g_param_spec_boxed ("clip-rect",
6502                         P_("Clip Rectangle"),
6503                         P_("The visible region of the actor"),
6504                         GRAPHENE_TYPE_RECT,
6505                         G_PARAM_READWRITE |
6506                         G_PARAM_STATIC_STRINGS |
6507                         G_PARAM_EXPLICIT_NOTIFY);
6508 
6509   /**
6510    * ClutterActor:name:
6511    *
6512    * The name of the actor
6513    *
6514    * Since: 0.2
6515    */
6516   obj_props[PROP_NAME] =
6517     g_param_spec_string ("name",
6518                          P_("Name"),
6519                          P_("Name of the actor"),
6520                          NULL,
6521                          CLUTTER_PARAM_READWRITE |
6522                          G_PARAM_EXPLICIT_NOTIFY);
6523 
6524   /**
6525    * ClutterActor:pivot-point:
6526    *
6527    * The point around which the scaling and rotation transformations occur.
6528    *
6529    * The pivot point is expressed in normalized coordinates space, with (0, 0)
6530    * being the top left corner of the actor and (1, 1) the bottom right corner
6531    * of the actor.
6532    *
6533    * The default pivot point is located at (0, 0).
6534    *
6535    * The #ClutterActor:pivot-point property is animatable.
6536    *
6537    * Since: 1.12
6538    */
6539   obj_props[PROP_PIVOT_POINT] =
6540     g_param_spec_boxed ("pivot-point",
6541                         P_("Pivot Point"),
6542                         P_("The point around which the scaling and rotation occur"),
6543                         GRAPHENE_TYPE_POINT,
6544                         G_PARAM_READWRITE |
6545                         G_PARAM_STATIC_STRINGS |
6546                         G_PARAM_EXPLICIT_NOTIFY |
6547                         CLUTTER_PARAM_ANIMATABLE);
6548 
6549   /**
6550    * ClutterActor:pivot-point-z:
6551    *
6552    * The Z component of the #ClutterActor:pivot-point, expressed as a value
6553    * along the Z axis.
6554    *
6555    * The #ClutterActor:pivot-point-z property is animatable.
6556    *
6557    * Since: 1.12
6558    */
6559   obj_props[PROP_PIVOT_POINT_Z] =
6560     g_param_spec_float ("pivot-point-z",
6561                         P_("Pivot Point Z"),
6562                         P_("Z component of the pivot point"),
6563                         -G_MAXFLOAT, G_MAXFLOAT,
6564                         0.f,
6565                         G_PARAM_READWRITE |
6566                         G_PARAM_STATIC_STRINGS |
6567                         G_PARAM_EXPLICIT_NOTIFY |
6568                         CLUTTER_PARAM_ANIMATABLE);
6569 
6570   /**
6571    * ClutterActor:scale-x:
6572    *
6573    * The horizontal scale of the actor.
6574    *
6575    * The #ClutterActor:scale-x property is animatable.
6576    *
6577    * Since: 0.6
6578    */
6579   obj_props[PROP_SCALE_X] =
6580     g_param_spec_double ("scale-x",
6581                          P_("Scale X"),
6582                          P_("Scale factor on the X axis"),
6583                          -G_MAXDOUBLE, G_MAXDOUBLE,
6584                          1.0,
6585                          G_PARAM_READWRITE |
6586                          G_PARAM_STATIC_STRINGS |
6587                          G_PARAM_EXPLICIT_NOTIFY |
6588                          CLUTTER_PARAM_ANIMATABLE);
6589 
6590   /**
6591    * ClutterActor:scale-y:
6592    *
6593    * The vertical scale of the actor.
6594    *
6595    * The #ClutterActor:scale-y property is animatable.
6596    *
6597    * Since: 0.6
6598    */
6599   obj_props[PROP_SCALE_Y] =
6600     g_param_spec_double ("scale-y",
6601                          P_("Scale Y"),
6602                          P_("Scale factor on the Y axis"),
6603                          -G_MAXDOUBLE, G_MAXDOUBLE,
6604                          1.0,
6605                          G_PARAM_READWRITE |
6606                          G_PARAM_STATIC_STRINGS |
6607                          G_PARAM_EXPLICIT_NOTIFY |
6608                          CLUTTER_PARAM_ANIMATABLE);
6609 
6610   /**
6611    * ClutterActor:scale-z:
6612    *
6613    * The scale factor of the actor along the Z axis.
6614    *
6615    * The #ClutterActor:scale-y property is animatable.
6616    *
6617    * Since: 1.12
6618    */
6619   obj_props[PROP_SCALE_Z] =
6620     g_param_spec_double ("scale-z",
6621                          P_("Scale Z"),
6622                          P_("Scale factor on the Z axis"),
6623                          -G_MAXDOUBLE, G_MAXDOUBLE,
6624                          1.0,
6625                          G_PARAM_READWRITE |
6626                          G_PARAM_STATIC_STRINGS |
6627                          G_PARAM_EXPLICIT_NOTIFY |
6628                          CLUTTER_PARAM_ANIMATABLE);
6629 
6630   /**
6631    * ClutterActor:rotation-angle-x:
6632    *
6633    * The rotation angle on the X axis.
6634    *
6635    * The #ClutterActor:rotation-angle-x property is animatable.
6636    *
6637    * Since: 0.6
6638    */
6639   obj_props[PROP_ROTATION_ANGLE_X] =
6640     g_param_spec_double ("rotation-angle-x",
6641                          P_("Rotation Angle X"),
6642                          P_("The rotation angle on the X axis"),
6643                          -G_MAXDOUBLE, G_MAXDOUBLE,
6644                          0.0,
6645                          G_PARAM_READWRITE |
6646                          G_PARAM_STATIC_STRINGS |
6647                          G_PARAM_EXPLICIT_NOTIFY |
6648                          CLUTTER_PARAM_ANIMATABLE);
6649 
6650   /**
6651    * ClutterActor:rotation-angle-y:
6652    *
6653    * The rotation angle on the Y axis
6654    *
6655    * The #ClutterActor:rotation-angle-y property is animatable.
6656    *
6657    * Since: 0.6
6658    */
6659   obj_props[PROP_ROTATION_ANGLE_Y] =
6660     g_param_spec_double ("rotation-angle-y",
6661                          P_("Rotation Angle Y"),
6662                          P_("The rotation angle on the Y axis"),
6663                          -G_MAXDOUBLE, G_MAXDOUBLE,
6664                          0.0,
6665                          G_PARAM_READWRITE |
6666                          G_PARAM_STATIC_STRINGS |
6667                          G_PARAM_EXPLICIT_NOTIFY |
6668                          CLUTTER_PARAM_ANIMATABLE);
6669 
6670   /**
6671    * ClutterActor:rotation-angle-z:
6672    *
6673    * The rotation angle on the Z axis
6674    *
6675    * The #ClutterActor:rotation-angle-z property is animatable.
6676    *
6677    * Since: 0.6
6678    */
6679   obj_props[PROP_ROTATION_ANGLE_Z] =
6680     g_param_spec_double ("rotation-angle-z",
6681                          P_("Rotation Angle Z"),
6682                          P_("The rotation angle on the Z axis"),
6683                          -G_MAXDOUBLE, G_MAXDOUBLE,
6684                          0.0,
6685                          G_PARAM_READWRITE |
6686                          G_PARAM_STATIC_STRINGS |
6687                          CLUTTER_PARAM_ANIMATABLE);
6688 
6689   /**
6690    * ClutterActor:translation-x:
6691    *
6692    * An additional translation applied along the X axis, relative
6693    * to the actor's #ClutterActor:pivot-point.
6694    *
6695    * The #ClutterActor:translation-x property is animatable.
6696    *
6697    * Since: 1.12
6698    */
6699   obj_props[PROP_TRANSLATION_X] =
6700     g_param_spec_float ("translation-x",
6701                         P_("Translation X"),
6702                         P_("Translation along the X axis"),
6703                         -G_MAXFLOAT, G_MAXFLOAT,
6704                         0.f,
6705                         G_PARAM_READWRITE |
6706                         G_PARAM_STATIC_STRINGS |
6707                         G_PARAM_EXPLICIT_NOTIFY |
6708                         CLUTTER_PARAM_ANIMATABLE);
6709 
6710   /**
6711    * ClutterActor:translation-y:
6712    *
6713    * An additional translation applied along the Y axis, relative
6714    * to the actor's #ClutterActor:pivot-point.
6715    *
6716    * The #ClutterActor:translation-y property is animatable.
6717    *
6718    * Since: 1.12
6719    */
6720   obj_props[PROP_TRANSLATION_Y] =
6721     g_param_spec_float ("translation-y",
6722                         P_("Translation Y"),
6723                         P_("Translation along the Y axis"),
6724                         -G_MAXFLOAT, G_MAXFLOAT,
6725                         0.f,
6726                         G_PARAM_READWRITE |
6727                         G_PARAM_STATIC_STRINGS |
6728                         G_PARAM_EXPLICIT_NOTIFY |
6729                         CLUTTER_PARAM_ANIMATABLE);
6730 
6731   /**
6732    * ClutterActor:translation-z:
6733    *
6734    * An additional translation applied along the Z axis, relative
6735    * to the actor's #ClutterActor:pivot-point.
6736    *
6737    * The #ClutterActor:translation-z property is animatable.
6738    *
6739    * Since: 1.12
6740    */
6741   obj_props[PROP_TRANSLATION_Z] =
6742     g_param_spec_float ("translation-z",
6743                         P_("Translation Z"),
6744                         P_("Translation along the Z axis"),
6745                         -G_MAXFLOAT, G_MAXFLOAT,
6746                         0.f,
6747                         G_PARAM_READWRITE |
6748                         G_PARAM_STATIC_STRINGS |
6749                         G_PARAM_EXPLICIT_NOTIFY |
6750                         CLUTTER_PARAM_ANIMATABLE);
6751 
6752   /**
6753    * ClutterActor:transform:
6754    *
6755    * Overrides the transformations of a #ClutterActor with a custom
6756    * matrix.
6757    *
6758    * The matrix specified by the #ClutterActor:transform property is
6759    * applied to the actor and its children relative to the actor's
6760    * #ClutterActor:allocation and #ClutterActor:pivot-point.
6761    *
6762    * Application code should rarely need to use this function directly.
6763    *
6764    * Setting this property with a #graphene_matrix_t will set the
6765    * #ClutterActor:transform-set property to %TRUE as a side effect;
6766    * setting this property with %NULL will set the
6767    * #ClutterActor:transform-set property to %FALSE.
6768    *
6769    * The #ClutterActor:transform property is animatable.
6770    *
6771    * Since: 1.12
6772    */
6773   obj_props[PROP_TRANSFORM] =
6774     g_param_spec_boxed ("transform",
6775                         P_("Transform"),
6776                         P_("Transformation matrix"),
6777                         GRAPHENE_TYPE_MATRIX,
6778                         G_PARAM_READWRITE |
6779                         G_PARAM_STATIC_STRINGS |
6780                         G_PARAM_EXPLICIT_NOTIFY |
6781                         CLUTTER_PARAM_ANIMATABLE);
6782 
6783   /**
6784    * ClutterActor:transform-set:
6785    *
6786    * Whether the #ClutterActor:transform property is set.
6787    *
6788    * Since: 1.12
6789    */
6790   obj_props[PROP_TRANSFORM_SET] =
6791     g_param_spec_boolean ("transform-set",
6792                           P_("Transform Set"),
6793                           P_("Whether the transform property is set"),
6794                           FALSE,
6795                           G_PARAM_READABLE |
6796                           G_PARAM_STATIC_STRINGS |
6797                           G_PARAM_EXPLICIT_NOTIFY);
6798 
6799   /**
6800    * ClutterActor:child-transform:
6801    *
6802    * Applies a transformation matrix on each child of an actor.
6803    *
6804    * Setting this property with a #graphene_matrix_t will set the
6805    * #ClutterActor:child-transform-set property to %TRUE as a side effect;
6806    * setting this property with %NULL will set the
6807    * #ClutterActor:child-transform-set property to %FALSE.
6808    *
6809    * The #ClutterActor:child-transform property is animatable.
6810    *
6811    * Since: 1.12
6812    */
6813   obj_props[PROP_CHILD_TRANSFORM] =
6814     g_param_spec_boxed ("child-transform",
6815                         P_("Child Transform"),
6816                         P_("Children transformation matrix"),
6817                         GRAPHENE_TYPE_MATRIX,
6818                         G_PARAM_READWRITE |
6819                         G_PARAM_STATIC_STRINGS |
6820                         G_PARAM_EXPLICIT_NOTIFY |
6821                         CLUTTER_PARAM_ANIMATABLE);
6822 
6823   /**
6824    * ClutterActor:child-transform-set:
6825    *
6826    * Whether the #ClutterActor:child-transform property is set.
6827    *
6828    * Since: 1.12
6829    */
6830   obj_props[PROP_CHILD_TRANSFORM_SET] =
6831     g_param_spec_boolean ("child-transform-set",
6832                           P_("Child Transform Set"),
6833                           P_("Whether the child-transform property is set"),
6834                           FALSE,
6835                           G_PARAM_READABLE |
6836                           G_PARAM_STATIC_STRINGS |
6837                           G_PARAM_EXPLICIT_NOTIFY);
6838 
6839   /**
6840    * ClutterActor:show-on-set-parent:
6841    *
6842    * If %TRUE, the actor is automatically shown when parented.
6843    *
6844    * Calling clutter_actor_hide() on an actor which has not been
6845    * parented will set this property to %FALSE as a side effect.
6846    *
6847    * Since: 0.8
6848    */
6849   obj_props[PROP_SHOW_ON_SET_PARENT] = /* XXX:2.0 - remove */
6850     g_param_spec_boolean ("show-on-set-parent",
6851                           P_("Show on set parent"),
6852                           P_("Whether the actor is shown when parented"),
6853                           TRUE,
6854                           CLUTTER_PARAM_READWRITE);
6855 
6856   /**
6857    * ClutterActor:clip-to-allocation:
6858    *
6859    * Whether the clip region should track the allocated area
6860    * of the actor.
6861    *
6862    * This property is ignored if a clip area has been explicitly
6863    * set using clutter_actor_set_clip().
6864    *
6865    * Since: 1.0
6866    */
6867   obj_props[PROP_CLIP_TO_ALLOCATION] =
6868     g_param_spec_boolean ("clip-to-allocation",
6869                           P_("Clip to Allocation"),
6870                           P_("Sets the clip region to track the actor's allocation"),
6871                           FALSE,
6872                           CLUTTER_PARAM_READWRITE |
6873                           G_PARAM_EXPLICIT_NOTIFY);
6874 
6875   /**
6876    * ClutterActor:text-direction:
6877    *
6878    * The direction of the text inside a #ClutterActor.
6879    *
6880    * Since: 1.0
6881    */
6882   obj_props[PROP_TEXT_DIRECTION] =
6883     g_param_spec_enum ("text-direction",
6884                        P_("Text Direction"),
6885                        P_("Direction of the text"),
6886                        CLUTTER_TYPE_TEXT_DIRECTION,
6887                        CLUTTER_TEXT_DIRECTION_LTR,
6888                        CLUTTER_PARAM_READWRITE |
6889                        G_PARAM_EXPLICIT_NOTIFY);
6890 
6891   /**
6892    * ClutterActor:has-pointer:
6893    *
6894    * Whether the actor contains the pointer of a #ClutterInputDevice
6895    * or not.
6896    *
6897    * Since: 1.2
6898    */
6899   obj_props[PROP_HAS_POINTER] =
6900     g_param_spec_boolean ("has-pointer",
6901                           P_("Has Pointer"),
6902                           P_("Whether the actor contains the pointer of an input device"),
6903                           FALSE,
6904                           CLUTTER_PARAM_READABLE |
6905                           G_PARAM_EXPLICIT_NOTIFY);
6906 
6907   /**
6908    * ClutterActor:actions:
6909    *
6910    * Adds a #ClutterAction to the actor
6911    *
6912    * Since: 1.4
6913    */
6914   obj_props[PROP_ACTIONS] =
6915     g_param_spec_object ("actions",
6916                          P_("Actions"),
6917                          P_("Adds an action to the actor"),
6918                          CLUTTER_TYPE_ACTION,
6919                          CLUTTER_PARAM_WRITABLE |
6920                          G_PARAM_EXPLICIT_NOTIFY);
6921 
6922   /**
6923    * ClutterActor:constraints:
6924    *
6925    * Adds a #ClutterConstraint to the actor
6926    *
6927    * Since: 1.4
6928    */
6929   obj_props[PROP_CONSTRAINTS] =
6930     g_param_spec_object ("constraints",
6931                          P_("Constraints"),
6932                          P_("Adds a constraint to the actor"),
6933                          CLUTTER_TYPE_CONSTRAINT,
6934                          CLUTTER_PARAM_WRITABLE |
6935                          G_PARAM_EXPLICIT_NOTIFY);
6936 
6937   /**
6938    * ClutterActor:effect:
6939    *
6940    * Adds #ClutterEffect to the list of effects be applied on a #ClutterActor
6941    *
6942    * Since: 1.4
6943    */
6944   obj_props[PROP_EFFECT] =
6945     g_param_spec_object ("effect",
6946                          P_("Effect"),
6947                          P_("Add an effect to be applied on the actor"),
6948                          CLUTTER_TYPE_EFFECT,
6949                          CLUTTER_PARAM_WRITABLE |
6950                          G_PARAM_EXPLICIT_NOTIFY);
6951 
6952   /**
6953    * ClutterActor:layout-manager:
6954    *
6955    * A delegate object for controlling the layout of the children of
6956    * an actor.
6957    *
6958    * Since: 1.10
6959    */
6960   obj_props[PROP_LAYOUT_MANAGER] =
6961     g_param_spec_object ("layout-manager",
6962                          P_("Layout Manager"),
6963                          P_("The object controlling the layout of an actor's children"),
6964                          CLUTTER_TYPE_LAYOUT_MANAGER,
6965                          CLUTTER_PARAM_READWRITE |
6966                          G_PARAM_EXPLICIT_NOTIFY);
6967 
6968   /**
6969    * ClutterActor:x-expand:
6970    *
6971    * Whether a layout manager should assign more space to the actor on
6972    * the X axis.
6973    *
6974    * Since: 1.12
6975    */
6976   obj_props[PROP_X_EXPAND] =
6977     g_param_spec_boolean ("x-expand",
6978                           P_("X Expand"),
6979                           P_("Whether extra horizontal space should be assigned to the actor"),
6980                           FALSE,
6981                           G_PARAM_READWRITE |
6982                           G_PARAM_STATIC_STRINGS |
6983                           G_PARAM_EXPLICIT_NOTIFY);
6984 
6985   /**
6986    * ClutterActor:y-expand:
6987    *
6988    * Whether a layout manager should assign more space to the actor on
6989    * the Y axis.
6990    *
6991    * Since: 1.12
6992    */
6993   obj_props[PROP_Y_EXPAND] =
6994     g_param_spec_boolean ("y-expand",
6995                           P_("Y Expand"),
6996                           P_("Whether extra vertical space should be assigned to the actor"),
6997                           FALSE,
6998                           G_PARAM_READWRITE |
6999                           G_PARAM_STATIC_STRINGS |
7000                           G_PARAM_EXPLICIT_NOTIFY);
7001 
7002   /**
7003    * ClutterActor:x-align:
7004    *
7005    * The alignment of an actor on the X axis, if the actor has been given
7006    * extra space for its allocation. See also the #ClutterActor:x-expand
7007    * property.
7008    *
7009    * Since: 1.10
7010    */
7011   obj_props[PROP_X_ALIGN] =
7012     g_param_spec_enum ("x-align",
7013                        P_("X Alignment"),
7014                        P_("The alignment of the actor on the X axis within its allocation"),
7015                        CLUTTER_TYPE_ACTOR_ALIGN,
7016                        CLUTTER_ACTOR_ALIGN_FILL,
7017                        CLUTTER_PARAM_READWRITE |
7018                        G_PARAM_EXPLICIT_NOTIFY);
7019 
7020   /**
7021    * ClutterActor:y-align:
7022    *
7023    * The alignment of an actor on the Y axis, if the actor has been given
7024    * extra space for its allocation.
7025    *
7026    * Since: 1.10
7027    */
7028   obj_props[PROP_Y_ALIGN] =
7029     g_param_spec_enum ("y-align",
7030                        P_("Y Alignment"),
7031                        P_("The alignment of the actor on the Y axis within its allocation"),
7032                        CLUTTER_TYPE_ACTOR_ALIGN,
7033                        CLUTTER_ACTOR_ALIGN_FILL,
7034                        CLUTTER_PARAM_READWRITE |
7035                        G_PARAM_EXPLICIT_NOTIFY);
7036 
7037   /**
7038    * ClutterActor:margin-top:
7039    *
7040    * The margin (in pixels) from the top of the actor.
7041    *
7042    * This property adds a margin to the actor's preferred size; the margin
7043    * will be automatically taken into account when allocating the actor.
7044    *
7045    * The #ClutterActor:margin-top property is animatable.
7046    *
7047    * Since: 1.10
7048    */
7049   obj_props[PROP_MARGIN_TOP] =
7050     g_param_spec_float ("margin-top",
7051                         P_("Margin Top"),
7052                         P_("Extra space at the top"),
7053                         0.0, G_MAXFLOAT,
7054                         0.0,
7055                         G_PARAM_READWRITE |
7056                         G_PARAM_STATIC_STRINGS |
7057                         G_PARAM_EXPLICIT_NOTIFY |
7058                         CLUTTER_PARAM_ANIMATABLE);
7059 
7060   /**
7061    * ClutterActor:margin-bottom:
7062    *
7063    * The margin (in pixels) from the bottom of the actor.
7064    *
7065    * This property adds a margin to the actor's preferred size; the margin
7066    * will be automatically taken into account when allocating the actor.
7067    *
7068    * The #ClutterActor:margin-bottom property is animatable.
7069    *
7070    * Since: 1.10
7071    */
7072   obj_props[PROP_MARGIN_BOTTOM] =
7073     g_param_spec_float ("margin-bottom",
7074                         P_("Margin Bottom"),
7075                         P_("Extra space at the bottom"),
7076                         0.0, G_MAXFLOAT,
7077                         0.0,
7078                         G_PARAM_READWRITE |
7079                         G_PARAM_STATIC_STRINGS |
7080                         G_PARAM_EXPLICIT_NOTIFY |
7081                         CLUTTER_PARAM_ANIMATABLE);
7082 
7083   /**
7084    * ClutterActor:margin-left:
7085    *
7086    * The margin (in pixels) from the left of the actor.
7087    *
7088    * This property adds a margin to the actor's preferred size; the margin
7089    * will be automatically taken into account when allocating the actor.
7090    *
7091    * The #ClutterActor:margin-left property is animatable.
7092    *
7093    * Since: 1.10
7094    */
7095   obj_props[PROP_MARGIN_LEFT] =
7096     g_param_spec_float ("margin-left",
7097                         P_("Margin Left"),
7098                         P_("Extra space at the left"),
7099                         0.0, G_MAXFLOAT,
7100                         0.0,
7101                         G_PARAM_READWRITE |
7102                         G_PARAM_STATIC_STRINGS |
7103                         G_PARAM_EXPLICIT_NOTIFY |
7104                         CLUTTER_PARAM_ANIMATABLE);
7105 
7106   /**
7107    * ClutterActor:margin-right:
7108    *
7109    * The margin (in pixels) from the right of the actor.
7110    *
7111    * This property adds a margin to the actor's preferred size; the margin
7112    * will be automatically taken into account when allocating the actor.
7113    *
7114    * The #ClutterActor:margin-right property is animatable.
7115    *
7116    * Since: 1.10
7117    */
7118   obj_props[PROP_MARGIN_RIGHT] =
7119     g_param_spec_float ("margin-right",
7120                         P_("Margin Right"),
7121                         P_("Extra space at the right"),
7122                         0.0, G_MAXFLOAT,
7123                         0.0,
7124                         G_PARAM_READWRITE |
7125                         G_PARAM_STATIC_STRINGS |
7126                         G_PARAM_EXPLICIT_NOTIFY |
7127                         CLUTTER_PARAM_ANIMATABLE);
7128 
7129   /**
7130    * ClutterActor:background-color-set:
7131    *
7132    * Whether the #ClutterActor:background-color property has been set.
7133    *
7134    * Since: 1.10
7135    */
7136   obj_props[PROP_BACKGROUND_COLOR_SET] =
7137     g_param_spec_boolean ("background-color-set",
7138                           P_("Background Color Set"),
7139                           P_("Whether the background color is set"),
7140                           FALSE,
7141                           CLUTTER_PARAM_READABLE |
7142                           G_PARAM_EXPLICIT_NOTIFY);
7143 
7144   /**
7145    * ClutterActor:background-color:
7146    *
7147    * Paints a solid fill of the actor's allocation using the specified
7148    * color.
7149    *
7150    * The #ClutterActor:background-color property is animatable.
7151    *
7152    * Since: 1.10
7153    */
7154   obj_props[PROP_BACKGROUND_COLOR] =
7155     clutter_param_spec_color ("background-color",
7156                               P_("Background color"),
7157                               P_("The actor's background color"),
7158                               CLUTTER_COLOR_Transparent,
7159                               G_PARAM_READWRITE |
7160                               G_PARAM_STATIC_STRINGS |
7161                               G_PARAM_EXPLICIT_NOTIFY |
7162                               CLUTTER_PARAM_ANIMATABLE);
7163 
7164   /**
7165    * ClutterActor:first-child:
7166    *
7167    * The actor's first child.
7168    *
7169    * Since: 1.10
7170    */
7171   obj_props[PROP_FIRST_CHILD] =
7172     g_param_spec_object ("first-child",
7173                          P_("First Child"),
7174                          P_("The actor's first child"),
7175                          CLUTTER_TYPE_ACTOR,
7176                          CLUTTER_PARAM_READABLE |
7177                          G_PARAM_EXPLICIT_NOTIFY);
7178 
7179   /**
7180    * ClutterActor:last-child:
7181    *
7182    * The actor's last child.
7183    *
7184    * Since: 1.10
7185    */
7186   obj_props[PROP_LAST_CHILD] =
7187     g_param_spec_object ("last-child",
7188                          P_("Last Child"),
7189                          P_("The actor's last child"),
7190                          CLUTTER_TYPE_ACTOR,
7191                          CLUTTER_PARAM_READABLE |
7192                          G_PARAM_EXPLICIT_NOTIFY);
7193 
7194   /**
7195    * ClutterActor:content:
7196    *
7197    * The #ClutterContent implementation that controls the content
7198    * of the actor.
7199    *
7200    * Since: 1.10
7201    */
7202   obj_props[PROP_CONTENT] =
7203     g_param_spec_object ("content",
7204                          P_("Content"),
7205                          P_("Delegate object for painting the actor's content"),
7206                          CLUTTER_TYPE_CONTENT,
7207                          CLUTTER_PARAM_READWRITE |
7208                          G_PARAM_EXPLICIT_NOTIFY);
7209 
7210   /**
7211    * ClutterActor:content-gravity:
7212    *
7213    * The alignment that should be honoured by the #ClutterContent
7214    * set with the #ClutterActor:content property.
7215    *
7216    * Changing the value of this property will change the bounding box of
7217    * the content; you can use the #ClutterActor:content-box property to
7218    * get the position and size of the content within the actor's
7219    * allocation.
7220    *
7221    * This property is meaningful only for #ClutterContent implementations
7222    * that have a preferred size, and if the preferred size is smaller than
7223    * the actor's allocation.
7224    *
7225    * The #ClutterActor:content-gravity property is animatable.
7226    *
7227    * Since: 1.10
7228    */
7229   obj_props[PROP_CONTENT_GRAVITY] =
7230     g_param_spec_enum ("content-gravity",
7231                        P_("Content Gravity"),
7232                        P_("Alignment of the actor's content"),
7233                        CLUTTER_TYPE_CONTENT_GRAVITY,
7234                        CLUTTER_CONTENT_GRAVITY_RESIZE_FILL,
7235                        CLUTTER_PARAM_READWRITE |
7236                        G_PARAM_EXPLICIT_NOTIFY);
7237 
7238   /**
7239    * ClutterActor:content-box:
7240    *
7241    * The bounding box for the #ClutterContent used by the actor.
7242    *
7243    * The value of this property is controlled by the #ClutterActor:allocation
7244    * and #ClutterActor:content-gravity properties of #ClutterActor.
7245    *
7246    * The bounding box for the content is guaranteed to never exceed the
7247    * allocation's of the actor.
7248    *
7249    * Since: 1.10
7250    */
7251   obj_props[PROP_CONTENT_BOX] =
7252     g_param_spec_boxed ("content-box",
7253                         P_("Content Box"),
7254                         P_("The bounding box of the actor's content"),
7255                         CLUTTER_TYPE_ACTOR_BOX,
7256                         G_PARAM_READABLE |
7257                         G_PARAM_STATIC_STRINGS |
7258                         G_PARAM_EXPLICIT_NOTIFY |
7259                         CLUTTER_PARAM_ANIMATABLE);
7260 
7261   obj_props[PROP_MINIFICATION_FILTER] =
7262     g_param_spec_enum ("minification-filter",
7263                        P_("Minification Filter"),
7264                        P_("The filter used when reducing the size of the content"),
7265                        CLUTTER_TYPE_SCALING_FILTER,
7266                        CLUTTER_SCALING_FILTER_LINEAR,
7267                        CLUTTER_PARAM_READWRITE |
7268                        G_PARAM_EXPLICIT_NOTIFY);
7269 
7270   obj_props[PROP_MAGNIFICATION_FILTER] =
7271     g_param_spec_enum ("magnification-filter",
7272                        P_("Magnification Filter"),
7273                        P_("The filter used when increasing the size of the content"),
7274                        CLUTTER_TYPE_SCALING_FILTER,
7275                        CLUTTER_SCALING_FILTER_LINEAR,
7276                        CLUTTER_PARAM_READWRITE |
7277                        G_PARAM_EXPLICIT_NOTIFY);
7278 
7279   /**
7280    * ClutterActor:content-repeat:
7281    *
7282    * The repeat policy for the actor's #ClutterActor:content.
7283    *
7284    * Since: 1.12
7285    */
7286   obj_props[PROP_CONTENT_REPEAT] =
7287     g_param_spec_flags ("content-repeat",
7288                         P_("Content Repeat"),
7289                         P_("The repeat policy for the actor's content"),
7290                         CLUTTER_TYPE_CONTENT_REPEAT,
7291                         CLUTTER_REPEAT_NONE,
7292                         G_PARAM_READWRITE |
7293                         G_PARAM_STATIC_STRINGS |
7294                         G_PARAM_EXPLICIT_NOTIFY);
7295 
7296   g_object_class_install_properties (object_class, PROP_LAST, obj_props);
7297 
7298   /**
7299    * ClutterActor::destroy:
7300    * @actor: the #ClutterActor which emitted the signal
7301    *
7302    * The ::destroy signal notifies that all references held on the
7303    * actor which emitted it should be released.
7304    *
7305    * The ::destroy signal should be used by all holders of a reference
7306    * on @actor.
7307    *
7308    * This signal might result in the finalization of the #ClutterActor
7309    * if all references are released.
7310    *
7311    * Composite actors and actors implementing the #ClutterContainer
7312    * interface should override the default implementation of the
7313    * class handler of this signal and call clutter_actor_destroy() on
7314    * their children. When overriding the default class handler, it is
7315    * required to chain up to the parent's implementation.
7316    *
7317    * Since: 0.2
7318    */
7319   actor_signals[DESTROY] =
7320     g_signal_new (I_("destroy"),
7321 		  G_TYPE_FROM_CLASS (object_class),
7322                   G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
7323 		  G_STRUCT_OFFSET (ClutterActorClass, destroy),
7324 		  NULL, NULL, NULL,
7325 		  G_TYPE_NONE, 0);
7326   /**
7327    * ClutterActor::show:
7328    * @actor: the object which received the signal
7329    *
7330    * The ::show signal is emitted when an actor is visible and
7331    * rendered on the stage.
7332    *
7333    * Since: 0.2
7334    */
7335   actor_signals[SHOW] =
7336     g_signal_new (I_("show"),
7337 		  G_TYPE_FROM_CLASS (object_class),
7338 		  G_SIGNAL_RUN_FIRST,
7339 		  G_STRUCT_OFFSET (ClutterActorClass, show),
7340 		  NULL, NULL, NULL,
7341 		  G_TYPE_NONE, 0);
7342   /**
7343    * ClutterActor::hide:
7344    * @actor: the object which received the signal
7345    *
7346    * The ::hide signal is emitted when an actor is no longer rendered
7347    * on the stage.
7348    *
7349    * Since: 0.2
7350    */
7351   actor_signals[HIDE] =
7352     g_signal_new (I_("hide"),
7353 		  G_TYPE_FROM_CLASS (object_class),
7354 		  G_SIGNAL_RUN_FIRST,
7355 		  G_STRUCT_OFFSET (ClutterActorClass, hide),
7356 		  NULL, NULL, NULL,
7357 		  G_TYPE_NONE, 0);
7358   /**
7359    * ClutterActor::parent-set:
7360    * @actor: the object which received the signal
7361    * @old_parent: (allow-none): the previous parent of the actor, or %NULL
7362    *
7363    * This signal is emitted when the parent of the actor changes.
7364    *
7365    * Since: 0.2
7366    */
7367   actor_signals[PARENT_SET] =
7368     g_signal_new (I_("parent-set"),
7369                   G_TYPE_FROM_CLASS (object_class),
7370                   G_SIGNAL_RUN_LAST,
7371                   G_STRUCT_OFFSET (ClutterActorClass, parent_set),
7372                   NULL, NULL, NULL,
7373                   G_TYPE_NONE, 1,
7374                   CLUTTER_TYPE_ACTOR);
7375 
7376   /**
7377    * ClutterActor::queue-relayout:
7378    * @actor: the actor being queued for relayout
7379    *
7380    * The ::queue_layout signal is emitted when clutter_actor_queue_relayout()
7381    * is called on an actor.
7382    *
7383    * The default implementation for #ClutterActor chains up to the
7384    * parent actor and queues a relayout on the parent, thus "bubbling"
7385    * the relayout queue up through the actor graph.
7386    *
7387    * The main purpose of this signal is to allow relayout to be propagated
7388    * properly in the procense of #ClutterClone actors. Applications will
7389    * not normally need to connect to this signal.
7390    *
7391    * Since: 1.2
7392    */
7393   actor_signals[QUEUE_RELAYOUT] =
7394     g_signal_new (I_("queue-relayout"),
7395 		  G_TYPE_FROM_CLASS (object_class),
7396 		  G_SIGNAL_RUN_LAST |
7397                   G_SIGNAL_NO_HOOKS,
7398 		  G_STRUCT_OFFSET (ClutterActorClass, queue_relayout),
7399 		  NULL, NULL, NULL,
7400 		  G_TYPE_NONE, 0);
7401 
7402   /**
7403    * ClutterActor::event:
7404    * @actor: the actor which received the event
7405    * @event: a #ClutterEvent
7406    *
7407    * The ::event signal is emitted each time an event is received
7408    * by the @actor. This signal will be emitted on every actor,
7409    * following the hierarchy chain, until it reaches the top-level
7410    * container (the #ClutterStage).
7411    *
7412    * Return value: %TRUE if the event has been handled by the actor,
7413    *   or %FALSE to continue the emission.
7414    *
7415    * Since: 0.6
7416    */
7417   actor_signals[EVENT] =
7418     g_signal_new (I_("event"),
7419 		  G_TYPE_FROM_CLASS (object_class),
7420 		  G_SIGNAL_RUN_LAST,
7421 		  G_STRUCT_OFFSET (ClutterActorClass, event),
7422 		  _clutter_boolean_handled_accumulator, NULL,
7423 		  _clutter_marshal_BOOLEAN__BOXED,
7424 		  G_TYPE_BOOLEAN, 1,
7425 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7426   g_signal_set_va_marshaller (actor_signals[EVENT],
7427                               G_TYPE_FROM_CLASS (object_class),
7428                               _clutter_marshal_BOOLEAN__BOXEDv);
7429   /**
7430    * ClutterActor::button-press-event:
7431    * @actor: the actor which received the event
7432    * @event: (type ClutterButtonEvent): a #ClutterButtonEvent
7433    *
7434    * The ::button-press-event signal is emitted each time a mouse button
7435    * is pressed on @actor.
7436    *
7437    * Return value: %TRUE if the event has been handled by the actor,
7438    *   or %FALSE to continue the emission.
7439    *
7440    * Since: 0.6
7441    */
7442   actor_signals[BUTTON_PRESS_EVENT] =
7443     g_signal_new (I_("button-press-event"),
7444 		  G_TYPE_FROM_CLASS (object_class),
7445 		  G_SIGNAL_RUN_LAST,
7446 		  G_STRUCT_OFFSET (ClutterActorClass, button_press_event),
7447 		  _clutter_boolean_handled_accumulator, NULL,
7448 		  _clutter_marshal_BOOLEAN__BOXED,
7449 		  G_TYPE_BOOLEAN, 1,
7450 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7451   g_signal_set_va_marshaller (actor_signals[BUTTON_PRESS_EVENT],
7452                               G_TYPE_FROM_CLASS (object_class),
7453                               _clutter_marshal_BOOLEAN__BOXEDv);
7454   /**
7455    * ClutterActor::button-release-event:
7456    * @actor: the actor which received the event
7457    * @event: (type ClutterButtonEvent): a #ClutterButtonEvent
7458    *
7459    * The ::button-release-event signal is emitted each time a mouse button
7460    * is released on @actor.
7461    *
7462    * Return value: %TRUE if the event has been handled by the actor,
7463    *   or %FALSE to continue the emission.
7464    *
7465    * Since: 0.6
7466    */
7467   actor_signals[BUTTON_RELEASE_EVENT] =
7468     g_signal_new (I_("button-release-event"),
7469 		  G_TYPE_FROM_CLASS (object_class),
7470 		  G_SIGNAL_RUN_LAST,
7471 		  G_STRUCT_OFFSET (ClutterActorClass, button_release_event),
7472 		  _clutter_boolean_handled_accumulator, NULL,
7473 		  _clutter_marshal_BOOLEAN__BOXED,
7474 		  G_TYPE_BOOLEAN, 1,
7475 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7476   g_signal_set_va_marshaller (actor_signals[BUTTON_RELEASE_EVENT],
7477                               G_TYPE_FROM_CLASS (object_class),
7478                               _clutter_marshal_BOOLEAN__BOXEDv);
7479   /**
7480    * ClutterActor::scroll-event:
7481    * @actor: the actor which received the event
7482    * @event: (type ClutterScrollEvent): a #ClutterScrollEvent
7483    *
7484    * The ::scroll-event signal is emitted each time the mouse is
7485    * scrolled on @actor
7486    *
7487    * Return value: %TRUE if the event has been handled by the actor,
7488    *   or %FALSE to continue the emission.
7489    *
7490    * Since: 0.6
7491    */
7492   actor_signals[SCROLL_EVENT] =
7493     g_signal_new (I_("scroll-event"),
7494 		  G_TYPE_FROM_CLASS (object_class),
7495 		  G_SIGNAL_RUN_LAST,
7496 		  G_STRUCT_OFFSET (ClutterActorClass, scroll_event),
7497 		  _clutter_boolean_handled_accumulator, NULL,
7498 		  _clutter_marshal_BOOLEAN__BOXED,
7499 		  G_TYPE_BOOLEAN, 1,
7500 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7501   g_signal_set_va_marshaller (actor_signals[SCROLL_EVENT],
7502                               G_TYPE_FROM_CLASS (object_class),
7503                               _clutter_marshal_BOOLEAN__BOXEDv);
7504   /**
7505    * ClutterActor::key-press-event:
7506    * @actor: the actor which received the event
7507    * @event: (type ClutterKeyEvent): a #ClutterKeyEvent
7508    *
7509    * The ::key-press-event signal is emitted each time a keyboard button
7510    * is pressed while @actor has key focus (see clutter_stage_set_key_focus()).
7511    *
7512    * Return value: %TRUE if the event has been handled by the actor,
7513    *   or %FALSE to continue the emission.
7514    *
7515    * Since: 0.6
7516    */
7517   actor_signals[KEY_PRESS_EVENT] =
7518     g_signal_new (I_("key-press-event"),
7519 		  G_TYPE_FROM_CLASS (object_class),
7520 		  G_SIGNAL_RUN_LAST,
7521 		  G_STRUCT_OFFSET (ClutterActorClass, key_press_event),
7522 		  _clutter_boolean_handled_accumulator, NULL,
7523 		  _clutter_marshal_BOOLEAN__BOXED,
7524 		  G_TYPE_BOOLEAN, 1,
7525 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7526   g_signal_set_va_marshaller (actor_signals[KEY_PRESS_EVENT],
7527                               G_TYPE_FROM_CLASS (object_class),
7528                               _clutter_marshal_BOOLEAN__BOXEDv);
7529   /**
7530    * ClutterActor::key-release-event:
7531    * @actor: the actor which received the event
7532    * @event: (type ClutterKeyEvent): a #ClutterKeyEvent
7533    *
7534    * The ::key-release-event signal is emitted each time a keyboard button
7535    * is released while @actor has key focus (see
7536    * clutter_stage_set_key_focus()).
7537    *
7538    * Return value: %TRUE if the event has been handled by the actor,
7539    *   or %FALSE to continue the emission.
7540    *
7541    * Since: 0.6
7542    */
7543   actor_signals[KEY_RELEASE_EVENT] =
7544     g_signal_new (I_("key-release-event"),
7545 		  G_TYPE_FROM_CLASS (object_class),
7546 		  G_SIGNAL_RUN_LAST,
7547 		  G_STRUCT_OFFSET (ClutterActorClass, key_release_event),
7548 		  _clutter_boolean_handled_accumulator, NULL,
7549 		  _clutter_marshal_BOOLEAN__BOXED,
7550 		  G_TYPE_BOOLEAN, 1,
7551 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7552   g_signal_set_va_marshaller (actor_signals[KEY_RELEASE_EVENT],
7553                               G_TYPE_FROM_CLASS (object_class),
7554                               _clutter_marshal_BOOLEAN__BOXEDv);
7555   /**
7556    * ClutterActor::motion-event:
7557    * @actor: the actor which received the event
7558    * @event: (type ClutterMotionEvent): a #ClutterMotionEvent
7559    *
7560    * The ::motion-event signal is emitted each time the mouse pointer is
7561    * moved over @actor.
7562    *
7563    * Return value: %TRUE if the event has been handled by the actor,
7564    *   or %FALSE to continue the emission.
7565    *
7566    * Since: 0.6
7567    */
7568   actor_signals[MOTION_EVENT] =
7569     g_signal_new (I_("motion-event"),
7570 		  G_TYPE_FROM_CLASS (object_class),
7571 		  G_SIGNAL_RUN_LAST,
7572 		  G_STRUCT_OFFSET (ClutterActorClass, motion_event),
7573 		  _clutter_boolean_handled_accumulator, NULL,
7574 		  _clutter_marshal_BOOLEAN__BOXED,
7575 		  G_TYPE_BOOLEAN, 1,
7576 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7577   g_signal_set_va_marshaller (actor_signals[MOTION_EVENT],
7578                               G_TYPE_FROM_CLASS (object_class),
7579                               _clutter_marshal_BOOLEAN__BOXEDv);
7580 
7581   /**
7582    * ClutterActor::key-focus-in:
7583    * @actor: the actor which now has key focus
7584    *
7585    * The ::key-focus-in signal is emitted when @actor receives key focus.
7586    *
7587    * Since: 0.6
7588    */
7589   actor_signals[KEY_FOCUS_IN] =
7590     g_signal_new (I_("key-focus-in"),
7591 		  G_TYPE_FROM_CLASS (object_class),
7592 		  G_SIGNAL_RUN_LAST,
7593 		  G_STRUCT_OFFSET (ClutterActorClass, key_focus_in),
7594 		  NULL, NULL, NULL,
7595 		  G_TYPE_NONE, 0);
7596 
7597   /**
7598    * ClutterActor::key-focus-out:
7599    * @actor: the actor which now has key focus
7600    *
7601    * The ::key-focus-out signal is emitted when @actor loses key focus.
7602    *
7603    * Since: 0.6
7604    */
7605   actor_signals[KEY_FOCUS_OUT] =
7606     g_signal_new (I_("key-focus-out"),
7607 		  G_TYPE_FROM_CLASS (object_class),
7608 		  G_SIGNAL_RUN_LAST,
7609 		  G_STRUCT_OFFSET (ClutterActorClass, key_focus_out),
7610 		  NULL, NULL, NULL,
7611 		  G_TYPE_NONE, 0);
7612 
7613   /**
7614    * ClutterActor::enter-event:
7615    * @actor: the actor which the pointer has entered.
7616    * @event: (type ClutterCrossingEvent): a #ClutterCrossingEvent
7617    *
7618    * The ::enter-event signal is emitted when the pointer enters the @actor
7619    *
7620    * Return value: %TRUE if the event has been handled by the actor,
7621    *   or %FALSE to continue the emission.
7622    *
7623    * Since: 0.6
7624    */
7625   actor_signals[ENTER_EVENT] =
7626     g_signal_new (I_("enter-event"),
7627 		  G_TYPE_FROM_CLASS (object_class),
7628 		  G_SIGNAL_RUN_LAST,
7629 		  G_STRUCT_OFFSET (ClutterActorClass, enter_event),
7630 		  _clutter_boolean_handled_accumulator, NULL,
7631 		  _clutter_marshal_BOOLEAN__BOXED,
7632 		  G_TYPE_BOOLEAN, 1,
7633 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7634   g_signal_set_va_marshaller (actor_signals[ENTER_EVENT],
7635                               G_TYPE_FROM_CLASS (object_class),
7636                               _clutter_marshal_BOOLEAN__BOXEDv);
7637 
7638   /**
7639    * ClutterActor::leave-event:
7640    * @actor: the actor which the pointer has left
7641    * @event: (type ClutterCrossingEvent): a #ClutterCrossingEvent
7642    *
7643    * The ::leave-event signal is emitted when the pointer leaves the @actor.
7644    *
7645    * Return value: %TRUE if the event has been handled by the actor,
7646    *   or %FALSE to continue the emission.
7647    *
7648    * Since: 0.6
7649    */
7650   actor_signals[LEAVE_EVENT] =
7651     g_signal_new (I_("leave-event"),
7652 		  G_TYPE_FROM_CLASS (object_class),
7653 		  G_SIGNAL_RUN_LAST,
7654 		  G_STRUCT_OFFSET (ClutterActorClass, leave_event),
7655 		  _clutter_boolean_handled_accumulator, NULL,
7656 		  _clutter_marshal_BOOLEAN__BOXED,
7657 		  G_TYPE_BOOLEAN, 1,
7658 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7659   g_signal_set_va_marshaller (actor_signals[LEAVE_EVENT],
7660                               G_TYPE_FROM_CLASS (object_class),
7661                               _clutter_marshal_BOOLEAN__BOXEDv);
7662 
7663   /**
7664    * ClutterActor::captured-event:
7665    * @actor: the actor which received the signal
7666    * @event: a #ClutterEvent
7667    *
7668    * The ::captured-event signal is emitted when an event is captured
7669    * by Clutter. This signal will be emitted starting from the top-level
7670    * container (the #ClutterStage) to the actor which received the event
7671    * going down the hierarchy. This signal can be used to intercept every
7672    * event before the specialized events (like
7673    * ClutterActor::button-press-event or ::key-released-event) are
7674    * emitted.
7675    *
7676    * Return value: %TRUE if the event has been handled by the actor,
7677    *   or %FALSE to continue the emission.
7678    *
7679    * Since: 0.6
7680    */
7681   actor_signals[CAPTURED_EVENT] =
7682     g_signal_new (I_("captured-event"),
7683 		  G_TYPE_FROM_CLASS (object_class),
7684 		  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
7685 		  G_STRUCT_OFFSET (ClutterActorClass, captured_event),
7686 		  _clutter_boolean_handled_accumulator, NULL,
7687 		  _clutter_marshal_BOOLEAN__BOXED,
7688 		  G_TYPE_BOOLEAN, 1,
7689 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7690   g_signal_set_va_marshaller (actor_signals[CAPTURED_EVENT],
7691                               G_TYPE_FROM_CLASS (object_class),
7692                               _clutter_marshal_BOOLEAN__BOXEDv);
7693 
7694   /**
7695    * ClutterActor::realize:
7696    * @actor: the #ClutterActor that received the signal
7697    *
7698    * The ::realize signal is emitted each time an actor is being
7699    * realized.
7700    *
7701    * Since: 0.8
7702    *
7703    * Deprecated: 1.16: The signal should not be used in newly
7704    *   written code
7705    */
7706   actor_signals[REALIZE] =
7707     g_signal_new (I_("realize"),
7708                   G_TYPE_FROM_CLASS (object_class),
7709                   G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
7710                   G_STRUCT_OFFSET (ClutterActorClass, realize),
7711                   NULL, NULL, NULL,
7712                   G_TYPE_NONE, 0);
7713   /**
7714    * ClutterActor::unrealize:
7715    * @actor: the #ClutterActor that received the signal
7716    *
7717    * The ::unrealize signal is emitted each time an actor is being
7718    * unrealized.
7719    *
7720    * Since: 0.8
7721    *
7722    * Deprecated: 1.16: The signal should not be used in newly
7723    *   written code
7724    */
7725   actor_signals[UNREALIZE] =
7726     g_signal_new (I_("unrealize"),
7727                   G_TYPE_FROM_CLASS (object_class),
7728                   G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
7729                   G_STRUCT_OFFSET (ClutterActorClass, unrealize),
7730                   NULL, NULL, NULL,
7731                   G_TYPE_NONE, 0);
7732 
7733   /**
7734    * ClutterActor::pick:
7735    * @actor: the #ClutterActor that received the signal
7736    * @pick_context: a #ClutterPickContext
7737    *
7738    * The ::pick signal is emitted each time an actor is being painted
7739    * in "pick mode". The pick mode is used to identify the actor during
7740    * the event handling phase, or by clutter_stage_get_actor_at_pos().
7741    *
7742    * Subclasses of #ClutterActor should override the class signal handler
7743    * and paint themselves in that function.
7744    *
7745    * It is possible to connect a handler to the ::pick signal in order
7746    * to set up some custom aspect of a paint in pick mode.
7747    *
7748    * Since: 1.0
7749    * Deprecated: 1.12: Override the #ClutterActorClass.pick virtual function
7750    *   instead.
7751    */
7752   actor_signals[PICK] =
7753     g_signal_new (I_("pick"),
7754                   G_TYPE_FROM_CLASS (object_class),
7755                   G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
7756                   G_STRUCT_OFFSET (ClutterActorClass, pick),
7757                   NULL, NULL, NULL,
7758                   G_TYPE_NONE, 1,
7759                   CLUTTER_TYPE_PICK_CONTEXT);
7760 
7761   /**
7762    * ClutterActor::transitions-completed:
7763    * @actor: a #ClutterActor
7764    *
7765    * The ::transitions-completed signal is emitted once all transitions
7766    * involving @actor are complete.
7767    *
7768    * Since: 1.10
7769    */
7770   actor_signals[TRANSITIONS_COMPLETED] =
7771     g_signal_new (I_("transitions-completed"),
7772                   G_TYPE_FROM_CLASS (object_class),
7773                   G_SIGNAL_RUN_LAST,
7774                   0,
7775                   NULL, NULL, NULL,
7776                   G_TYPE_NONE, 0);
7777 
7778   /**
7779    * ClutterActor::transition-stopped:
7780    * @actor: a #ClutterActor
7781    * @name: the name of the transition
7782    * @is_finished: whether the transition was finished, or stopped
7783    *
7784    * The ::transition-stopped signal is emitted once a transition
7785    * is stopped; a transition is stopped once it reached its total
7786    * duration (including eventual repeats), it has been stopped
7787    * using clutter_timeline_stop(), or it has been removed from the
7788    * transitions applied on @actor, using clutter_actor_remove_transition().
7789    *
7790    * Since: 1.12
7791    */
7792   actor_signals[TRANSITION_STOPPED] =
7793     g_signal_new (I_("transition-stopped"),
7794                   G_TYPE_FROM_CLASS (object_class),
7795                   G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE |
7796                   G_SIGNAL_NO_HOOKS | G_SIGNAL_DETAILED,
7797                   0,
7798                   NULL, NULL,
7799                   _clutter_marshal_VOID__STRING_BOOLEAN,
7800                   G_TYPE_NONE, 2,
7801                   G_TYPE_STRING,
7802                   G_TYPE_BOOLEAN);
7803   g_signal_set_va_marshaller (actor_signals[TRANSITION_STOPPED],
7804                               G_TYPE_FROM_CLASS (object_class),
7805                               _clutter_marshal_VOID__STRING_BOOLEANv);
7806 
7807   /**
7808    * ClutterActor::touch-event:
7809    * @actor: a #ClutterActor
7810    * @event: a #ClutterEvent
7811    *
7812    * The ::touch-event signal is emitted each time a touch
7813    * begin/end/update/cancel event.
7814    *
7815    * Return value: %CLUTTER_EVENT_STOP if the event has been handled by
7816    *   the actor, or %CLUTTER_EVENT_PROPAGATE to continue the emission.
7817    *
7818    * Since: 1.12
7819    */
7820   actor_signals[TOUCH_EVENT] =
7821     g_signal_new (I_("touch-event"),
7822 		  G_TYPE_FROM_CLASS (object_class),
7823 		  G_SIGNAL_RUN_LAST,
7824 		  G_STRUCT_OFFSET (ClutterActorClass, touch_event),
7825 		  _clutter_boolean_handled_accumulator, NULL,
7826 		  _clutter_marshal_BOOLEAN__BOXED,
7827 		  G_TYPE_BOOLEAN, 1,
7828 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
7829   g_signal_set_va_marshaller (actor_signals[TOUCH_EVENT],
7830                               G_TYPE_FROM_CLASS (object_class),
7831                               _clutter_marshal_BOOLEAN__BOXEDv);
7832 
7833   /**
7834    * ClutterActor::stage-views-changed:
7835    * @actor: a #ClutterActor
7836    *
7837    * The ::stage-views-changed signal is emitted when the position or
7838    * size an actor is being painted at have changed so that it's visible
7839    * on different stage views.
7840    *
7841    * This signal is also emitted when the actor gets detached from the stage
7842    * or when the views of the stage have been invalidated and will be
7843    * replaced; it's not emitted when the actor gets hidden.
7844    */
7845   actor_signals[STAGE_VIEWS_CHANGED] =
7846     g_signal_new (I_("stage-views-changed"),
7847                   G_TYPE_FROM_CLASS (object_class),
7848                   G_SIGNAL_RUN_LAST,
7849                   0,
7850                   NULL, NULL, NULL,
7851                   G_TYPE_NONE, 0);
7852 
7853   /**
7854    * ClutterActor::resource-scale-changed:
7855    * @actor: a #ClutterActor
7856    *
7857    * The ::resource-scale-changed signal is emitted when the resource scale
7858    * value returned by clutter_actor_get_resource_scale() changes.
7859    *
7860    * This signal can be used to get notified about the correct resource scale
7861    * when the scale had to be queried outside of the paint cycle.
7862    */
7863   actor_signals[RESOURCE_SCALE_CHANGED] =
7864     g_signal_new (I_("resource-scale-changed"),
7865                   G_TYPE_FROM_CLASS (object_class),
7866                   G_SIGNAL_RUN_LAST,
7867                   G_STRUCT_OFFSET (ClutterActorClass, resource_scale_changed),
7868                   NULL, NULL, NULL,
7869                   G_TYPE_NONE, 0);
7870 }
7871 
7872 static void
clutter_actor_init(ClutterActor * self)7873 clutter_actor_init (ClutterActor *self)
7874 {
7875   ClutterActorPrivate *priv;
7876 
7877   self->priv = priv = clutter_actor_get_instance_private (self);
7878 
7879   priv->allocation = (ClutterActorBox) CLUTTER_ACTOR_BOX_UNINITIALIZED;
7880 
7881   priv->opacity = 0xff;
7882   priv->show_on_set_parent = TRUE;
7883   priv->resource_scale = -1.0f;
7884 
7885   priv->needs_width_request = TRUE;
7886   priv->needs_height_request = TRUE;
7887   priv->needs_allocation = TRUE;
7888   priv->needs_paint_volume_update = TRUE;
7889   priv->needs_update_stage_views = TRUE;
7890 
7891   priv->cached_width_age = 1;
7892   priv->cached_height_age = 1;
7893 
7894   priv->opacity_override = -1;
7895   priv->enable_model_view_transform = TRUE;
7896 
7897   /* Initialize an empty paint volume to start with */
7898   _clutter_paint_volume_init_static (&priv->last_paint_volume, NULL);
7899   priv->last_paint_volume_valid = TRUE;
7900 
7901   priv->transform_valid = FALSE;
7902 
7903   /* the default is to stretch the content, to match the
7904    * current behaviour of basically all actors. also, it's
7905    * the easiest thing to compute.
7906    */
7907   priv->content_gravity = CLUTTER_CONTENT_GRAVITY_RESIZE_FILL;
7908   priv->min_filter = CLUTTER_SCALING_FILTER_LINEAR;
7909   priv->mag_filter = CLUTTER_SCALING_FILTER_LINEAR;
7910 
7911   /* this flag will be set to TRUE if the actor gets a child
7912    * or if the [xy]-expand flags are explicitly set; until
7913    * then, the actor does not need to expand.
7914    *
7915    * this also allows us to avoid computing the expand flag
7916    * when building up a scene.
7917    */
7918   priv->needs_compute_expand = FALSE;
7919 
7920   /* we start with an easing state with duration forcibly set
7921    * to 0, for backward compatibility.
7922    */
7923   clutter_actor_save_easing_state (self);
7924   clutter_actor_set_easing_duration (self, 0);
7925 }
7926 
7927 /**
7928  * clutter_actor_new:
7929  *
7930  * Creates a new #ClutterActor.
7931  *
7932  * A newly created actor has a floating reference, which will be sunk
7933  * when it is added to another actor.
7934  *
7935  * Return value: the newly created #ClutterActor
7936  *
7937  * Since: 1.10
7938  */
7939 ClutterActor *
clutter_actor_new(void)7940 clutter_actor_new (void)
7941 {
7942   return g_object_new (CLUTTER_TYPE_ACTOR, NULL);
7943 }
7944 
7945 /**
7946  * clutter_actor_destroy:
7947  * @self: a #ClutterActor
7948  *
7949  * Destroys an actor.  When an actor is destroyed, it will break any
7950  * references it holds to other objects.  If the actor is inside a
7951  * container, the actor will be removed.
7952  *
7953  * When you destroy a container, its children will be destroyed as well.
7954  */
7955 void
clutter_actor_destroy(ClutterActor * self)7956 clutter_actor_destroy (ClutterActor *self)
7957 {
7958   g_return_if_fail (CLUTTER_IS_ACTOR (self));
7959 
7960   g_object_ref (self);
7961 
7962   /* avoid recursion while destroying */
7963   if (!CLUTTER_ACTOR_IN_DESTRUCTION (self))
7964     {
7965       CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_DESTRUCTION);
7966 
7967       g_object_run_dispose (G_OBJECT (self));
7968 
7969       CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_DESTRUCTION);
7970     }
7971 
7972   g_object_unref (self);
7973 }
7974 
7975 void
_clutter_actor_queue_redraw_full(ClutterActor * self,const ClutterPaintVolume * volume,ClutterEffect * effect)7976 _clutter_actor_queue_redraw_full (ClutterActor             *self,
7977                                   const ClutterPaintVolume *volume,
7978                                   ClutterEffect            *effect)
7979 {
7980   ClutterActorPrivate *priv = self->priv;
7981   ClutterActor *stage;
7982 
7983   /* Here's an outline of the actor queue redraw mechanism:
7984    *
7985    * The process starts in clutter_actor_queue_redraw() which is a
7986    * wrapper for this function. Additionally, an effect can queue a
7987    * redraw by wrapping this function in clutter_effect_queue_repaint().
7988    *
7989    * This functions queues an entry in a list associated with the
7990    * stage which is a list of actors that queued a redraw while
7991    * updating the timelines, performing layouting and processing other
7992    * mainloop sources before the next paint starts.
7993    *
7994    * When all updates are complete and we come to paint the stage then
7995    * we iterate this list and build the redraw clip of the stage by
7996    * either using the clip that was supplied to
7997    * _clutter_actor_queue_redraw_full() or by asking the actor for its
7998    * redraw clip using clutter_actor_get_redraw_clip().
7999    *
8000    * Doing this later during the stage update instead of now is an
8001    * important optimization, because later it's more likely we will be
8002    * able to determine the paint volume of an actor (its allocation
8003    * should be up to date).
8004    */
8005 
8006   /* ignore queueing a redraw for actors being destroyed */
8007   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
8008     return;
8009 
8010   /* we can ignore unmapped actors, unless they are inside a cloned branch
8011    * of the scene graph, as unmapped actors will simply be left unpainted.
8012    *
8013    * this allows us to ignore redraws queued on leaf nodes when one
8014    * of their parents has been hidden
8015    */
8016   if (!CLUTTER_ACTOR_IS_MAPPED (self) &&
8017       !clutter_actor_has_mapped_clones (self))
8018     {
8019       CLUTTER_NOTE (PAINT,
8020                     "Skipping queue_redraw('%s'): mapped=%s, "
8021                     "has_mapped_clones=%s",
8022                     _clutter_actor_get_debug_name (self),
8023                     CLUTTER_ACTOR_IS_MAPPED (self) ? "yes" : "no",
8024                     clutter_actor_has_mapped_clones (self) ? "yes" : "no");
8025       return;
8026     }
8027 
8028   /* given the check above we could end up queueing a redraw on an
8029    * unmapped actor with mapped clones, so we cannot assume that
8030    * get_stage() will return a Stage
8031    */
8032   stage = _clutter_actor_get_stage_internal (self);
8033   if (stage == NULL)
8034     return;
8035 
8036   /* ignore queueing a redraw on stages that are being destroyed */
8037   if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
8038     return;
8039 
8040   clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage),
8041                                     self,
8042                                     volume);
8043 
8044   /* If this is the first redraw queued then we can directly use the
8045      effect parameter */
8046   if (!priv->is_dirty)
8047     priv->effect_to_redraw = effect;
8048   /* Otherwise we need to merge it with the existing effect parameter */
8049   else if (effect != NULL)
8050     {
8051       /* If there's already an effect then we need to use whichever is
8052          later in the chain of actors. Otherwise a full redraw has
8053          already been queued on the actor so we need to ignore the
8054          effect parameter */
8055       if (priv->effect_to_redraw != NULL)
8056         {
8057           if (priv->effects == NULL)
8058             g_warning ("Redraw queued with an effect that is "
8059                        "not applied to the actor");
8060           else
8061             {
8062               const GList *l;
8063 
8064               for (l = _clutter_meta_group_peek_metas (priv->effects);
8065                    l != NULL;
8066                    l = l->next)
8067                 {
8068                   if (l->data == priv->effect_to_redraw ||
8069                       l->data == effect)
8070                     priv->effect_to_redraw = l->data;
8071                 }
8072             }
8073         }
8074     }
8075   else
8076     {
8077       /* If no effect is specified then we need to redraw the whole
8078          actor */
8079       priv->effect_to_redraw = NULL;
8080     }
8081 
8082   priv->is_dirty = TRUE;
8083 
8084   if (!priv->propagated_one_redraw)
8085     _clutter_actor_propagate_queue_redraw (self, self);
8086 }
8087 
8088 /**
8089  * clutter_actor_queue_redraw:
8090  * @self: A #ClutterActor
8091  *
8092  * Queues up a redraw of an actor and any children. The redraw occurs
8093  * once the main loop becomes idle (after the current batch of events
8094  * has been processed, roughly).
8095  *
8096  * Applications rarely need to call this, as redraws are handled
8097  * automatically by modification functions.
8098  *
8099  * This function will not do anything if @self is not visible, or
8100  * if the actor is inside an invisible part of the scenegraph.
8101  *
8102  * Also be aware that painting is a NOP for actors with an opacity of
8103  * 0
8104  *
8105  * When you are implementing a custom actor you must queue a redraw
8106  * whenever some private state changes that will affect painting or
8107  * picking of your actor.
8108  */
8109 void
clutter_actor_queue_redraw(ClutterActor * self)8110 clutter_actor_queue_redraw (ClutterActor *self)
8111 {
8112   g_return_if_fail (CLUTTER_IS_ACTOR (self));
8113 
8114   _clutter_actor_queue_redraw_full (self,
8115                                     NULL, /* clip volume */
8116                                     NULL /* effect */);
8117 }
8118 
8119 static void
_clutter_actor_queue_relayout_on_clones(ClutterActor * self)8120 _clutter_actor_queue_relayout_on_clones (ClutterActor *self)
8121 {
8122   ClutterActorPrivate *priv = self->priv;
8123   GHashTableIter iter;
8124   gpointer key;
8125 
8126   if (priv->clones == NULL)
8127     return;
8128 
8129   g_hash_table_iter_init (&iter, priv->clones);
8130   while (g_hash_table_iter_next (&iter, &key, NULL))
8131     clutter_actor_queue_relayout (key);
8132 }
8133 
8134 void
_clutter_actor_queue_only_relayout(ClutterActor * self)8135 _clutter_actor_queue_only_relayout (ClutterActor *self)
8136 {
8137   ClutterActorPrivate *priv = self->priv;
8138 
8139   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
8140     return;
8141 
8142   if (priv->needs_width_request &&
8143       priv->needs_height_request &&
8144       priv->needs_allocation)
8145     return; /* save some cpu cycles */
8146 
8147 #ifdef CLUTTER_ENABLE_DEBUG
8148   if (!CLUTTER_ACTOR_IS_TOPLEVEL (self) && CLUTTER_ACTOR_IN_RELAYOUT (self))
8149     {
8150       g_warning ("The actor '%s' is currently inside an allocation "
8151                  "cycle; calling clutter_actor_queue_relayout() is "
8152                  "not recommended",
8153                  _clutter_actor_get_debug_name (self));
8154     }
8155 #endif /* CLUTTER_ENABLE_DEBUG */
8156 
8157   _clutter_actor_queue_relayout_on_clones (self);
8158 
8159   g_signal_emit (self, actor_signals[QUEUE_RELAYOUT], 0);
8160 }
8161 
8162 /**
8163  * clutter_actor_queue_redraw_with_clip:
8164  * @self: a #ClutterActor
8165  * @clip: (allow-none): a rectangular clip region, or %NULL
8166  *
8167  * Queues a redraw on @self limited to a specific, actor-relative
8168  * rectangular area.
8169  *
8170  * If @clip is %NULL this function is equivalent to
8171  * clutter_actor_queue_redraw().
8172  *
8173  * Since: 1.10
8174  */
8175 void
clutter_actor_queue_redraw_with_clip(ClutterActor * self,const cairo_rectangle_int_t * clip)8176 clutter_actor_queue_redraw_with_clip (ClutterActor                *self,
8177                                       const cairo_rectangle_int_t *clip)
8178 {
8179   ClutterPaintVolume volume;
8180   graphene_point3d_t origin;
8181 
8182   g_return_if_fail (CLUTTER_IS_ACTOR (self));
8183 
8184   if (clip == NULL)
8185     {
8186       clutter_actor_queue_redraw (self);
8187       return;
8188     }
8189 
8190   _clutter_paint_volume_init_static (&volume, self);
8191 
8192   origin.x = clip->x;
8193   origin.y = clip->y;
8194   origin.z = 0.0f;
8195 
8196   clutter_paint_volume_set_origin (&volume, &origin);
8197   clutter_paint_volume_set_width (&volume, clip->width);
8198   clutter_paint_volume_set_height (&volume, clip->height);
8199 
8200   _clutter_actor_queue_redraw_full (self, &volume, NULL);
8201 
8202   clutter_paint_volume_free (&volume);
8203 }
8204 
8205 /**
8206  * clutter_actor_queue_relayout:
8207  * @self: A #ClutterActor
8208  *
8209  * Indicates that the actor's size request or other layout-affecting
8210  * properties may have changed. This function is used inside #ClutterActor
8211  * subclass implementations, not by applications directly.
8212  *
8213  * Queueing a new layout automatically queues a redraw as well.
8214  *
8215  * Since: 0.8
8216  */
8217 void
clutter_actor_queue_relayout(ClutterActor * self)8218 clutter_actor_queue_relayout (ClutterActor *self)
8219 {
8220   g_return_if_fail (CLUTTER_IS_ACTOR (self));
8221 
8222   _clutter_actor_queue_only_relayout (self);
8223   clutter_actor_queue_redraw (self);
8224 }
8225 
8226 /**
8227  * clutter_actor_get_preferred_size:
8228  * @self: a #ClutterActor
8229  * @min_width_p: (out) (allow-none): return location for the minimum
8230  *   width, or %NULL
8231  * @min_height_p: (out) (allow-none): return location for the minimum
8232  *   height, or %NULL
8233  * @natural_width_p: (out) (allow-none): return location for the natural
8234  *   width, or %NULL
8235  * @natural_height_p: (out) (allow-none): return location for the natural
8236  *   height, or %NULL
8237  *
8238  * Computes the preferred minimum and natural size of an actor, taking into
8239  * account the actor's geometry management (either height-for-width
8240  * or width-for-height).
8241  *
8242  * The width and height used to compute the preferred height and preferred
8243  * width are the actor's natural ones.
8244  *
8245  * If you need to control the height for the preferred width, or the width for
8246  * the preferred height, you should use clutter_actor_get_preferred_width()
8247  * and clutter_actor_get_preferred_height(), and check the actor's preferred
8248  * geometry management using the #ClutterActor:request-mode property.
8249  *
8250  * Since: 0.8
8251  */
8252 void
clutter_actor_get_preferred_size(ClutterActor * self,gfloat * min_width_p,gfloat * min_height_p,gfloat * natural_width_p,gfloat * natural_height_p)8253 clutter_actor_get_preferred_size (ClutterActor *self,
8254                                   gfloat       *min_width_p,
8255                                   gfloat       *min_height_p,
8256                                   gfloat       *natural_width_p,
8257                                   gfloat       *natural_height_p)
8258 {
8259   ClutterActorPrivate *priv;
8260   gfloat min_width, min_height;
8261   gfloat natural_width, natural_height;
8262 
8263   g_return_if_fail (CLUTTER_IS_ACTOR (self));
8264 
8265   priv = self->priv;
8266 
8267   min_width = min_height = 0;
8268   natural_width = natural_height = 0;
8269 
8270   if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
8271     {
8272       CLUTTER_NOTE (LAYOUT, "Preferred size (height-for-width)");
8273       clutter_actor_get_preferred_width (self, -1,
8274                                          &min_width,
8275                                          &natural_width);
8276       clutter_actor_get_preferred_height (self, natural_width,
8277                                           &min_height,
8278                                           &natural_height);
8279     }
8280   else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
8281     {
8282       CLUTTER_NOTE (LAYOUT, "Preferred size (width-for-height)");
8283       clutter_actor_get_preferred_height (self, -1,
8284                                           &min_height,
8285                                           &natural_height);
8286       clutter_actor_get_preferred_width (self, natural_height,
8287                                          &min_width,
8288                                          &natural_width);
8289     }
8290   else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
8291     {
8292       CLUTTER_NOTE (LAYOUT, "Preferred size (content-size)");
8293 
8294       if (priv->content != NULL)
8295         clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height);
8296     }
8297   else
8298     {
8299       CLUTTER_NOTE (LAYOUT, "Unknown request mode");
8300     }
8301 
8302   if (min_width_p)
8303     *min_width_p = min_width;
8304 
8305   if (min_height_p)
8306     *min_height_p = min_height;
8307 
8308   if (natural_width_p)
8309     *natural_width_p = natural_width;
8310 
8311   if (natural_height_p)
8312     *natural_height_p = natural_height;
8313 }
8314 
8315 /*< private >
8316  * effective_align:
8317  * @align: a #ClutterActorAlign
8318  * @direction: a #ClutterTextDirection
8319  *
8320  * Retrieves the correct alignment depending on the text direction
8321  *
8322  * Return value: the effective alignment
8323  */
8324 static ClutterActorAlign
effective_align(ClutterActorAlign align,ClutterTextDirection direction)8325 effective_align (ClutterActorAlign    align,
8326                  ClutterTextDirection direction)
8327 {
8328   ClutterActorAlign res;
8329 
8330   switch (align)
8331     {
8332     case CLUTTER_ACTOR_ALIGN_START:
8333       res = (direction == CLUTTER_TEXT_DIRECTION_RTL)
8334           ? CLUTTER_ACTOR_ALIGN_END
8335           : CLUTTER_ACTOR_ALIGN_START;
8336       break;
8337 
8338     case CLUTTER_ACTOR_ALIGN_END:
8339       res = (direction == CLUTTER_TEXT_DIRECTION_RTL)
8340           ? CLUTTER_ACTOR_ALIGN_START
8341           : CLUTTER_ACTOR_ALIGN_END;
8342       break;
8343 
8344     default:
8345       res = align;
8346       break;
8347     }
8348 
8349   return res;
8350 }
8351 
8352 /*< private >
8353  * _clutter_actor_get_effective_x_align:
8354  * @self: a #ClutterActor
8355  *
8356  * Retrieves the effective horizontal alignment, taking into
8357  * consideration the text direction of @self.
8358  *
8359  * Return value: the effective horizontal alignment
8360  */
8361 ClutterActorAlign
_clutter_actor_get_effective_x_align(ClutterActor * self)8362 _clutter_actor_get_effective_x_align (ClutterActor *self)
8363 {
8364   return effective_align (clutter_actor_get_x_align (self),
8365                           clutter_actor_get_text_direction (self));
8366 }
8367 
8368 static inline void
adjust_for_margin(float margin_start,float margin_end,float * minimum_size,float * natural_size,float * allocated_start,float * allocated_end)8369 adjust_for_margin (float  margin_start,
8370                    float  margin_end,
8371                    float *minimum_size,
8372                    float *natural_size,
8373                    float *allocated_start,
8374                    float *allocated_end)
8375 {
8376   float min_size = *minimum_size;
8377   float nat_size = *natural_size;
8378   float start = *allocated_start;
8379   float end = *allocated_end;
8380 
8381   min_size = MAX (min_size - (margin_start + margin_end), 0);
8382   nat_size = MAX (nat_size - (margin_start + margin_end), 0);
8383 
8384   *minimum_size = min_size;
8385   *natural_size = nat_size;
8386 
8387   start += margin_start;
8388   end -= margin_end;
8389 
8390   if (end - start >= 0)
8391     {
8392       *allocated_start = start;
8393       *allocated_end = end;
8394     }
8395 }
8396 
8397 static inline void
adjust_for_alignment(ClutterActorAlign alignment,float natural_size,float * allocated_start,float * allocated_end)8398 adjust_for_alignment (ClutterActorAlign  alignment,
8399                       float              natural_size,
8400                       float             *allocated_start,
8401                       float             *allocated_end)
8402 {
8403   float allocated_size = *allocated_end - *allocated_start;
8404 
8405   if (allocated_size <= 0.f)
8406     return;
8407 
8408   switch (alignment)
8409     {
8410     case CLUTTER_ACTOR_ALIGN_FILL:
8411       /* do nothing */
8412       break;
8413 
8414     case CLUTTER_ACTOR_ALIGN_START:
8415       /* keep start */
8416       *allocated_end = *allocated_start + MIN (natural_size, allocated_size);
8417       break;
8418 
8419     case CLUTTER_ACTOR_ALIGN_END:
8420       if (allocated_size > natural_size)
8421         {
8422           *allocated_start += (allocated_size - natural_size);
8423           *allocated_end = *allocated_start + natural_size;
8424         }
8425       break;
8426 
8427     case CLUTTER_ACTOR_ALIGN_CENTER:
8428       if (allocated_size > natural_size)
8429         {
8430           *allocated_start += floorf ((allocated_size - natural_size) / 2);
8431           *allocated_end = *allocated_start + MIN (allocated_size, natural_size);
8432         }
8433       break;
8434     }
8435 }
8436 
8437 /*< private >
8438  * clutter_actor_adjust_width:
8439  * @self: a #ClutterActor
8440  * @minimum_width: (inout): the actor's preferred minimum width, which
8441  *   will be adjusted depending on the margin
8442  * @natural_width: (inout): the actor's preferred natural width, which
8443  *   will be adjusted depending on the margin
8444  * @adjusted_x1: (out): the adjusted x1 for the actor's bounding box
8445  * @adjusted_x2: (out): the adjusted x2 for the actor's bounding box
8446  *
8447  * Adjusts the preferred and allocated position and size of an actor,
8448  * depending on the margin and alignment properties.
8449  */
8450 static void
clutter_actor_adjust_width(ClutterActor * self,gfloat * minimum_width,gfloat * natural_width,gfloat * adjusted_x1,gfloat * adjusted_x2)8451 clutter_actor_adjust_width (ClutterActor *self,
8452                             gfloat       *minimum_width,
8453                             gfloat       *natural_width,
8454                             gfloat       *adjusted_x1,
8455                             gfloat       *adjusted_x2)
8456 {
8457   ClutterTextDirection text_dir;
8458   const ClutterLayoutInfo *info;
8459 
8460   info = _clutter_actor_get_layout_info_or_defaults (self);
8461   text_dir = clutter_actor_get_text_direction (self);
8462 
8463   CLUTTER_NOTE (LAYOUT, "Adjusting allocated X and width");
8464 
8465   /* this will tweak natural_width to remove the margin, so that
8466    * adjust_for_alignment() will use the correct size
8467    */
8468   adjust_for_margin (info->margin.left, info->margin.right,
8469                      minimum_width, natural_width,
8470                      adjusted_x1, adjusted_x2);
8471 
8472   adjust_for_alignment (effective_align (info->x_align, text_dir),
8473                         *natural_width,
8474                         adjusted_x1, adjusted_x2);
8475 }
8476 
8477 /*< private >
8478  * clutter_actor_adjust_height:
8479  * @self: a #ClutterActor
8480  * @minimum_height: (inout): the actor's preferred minimum height, which
8481  *   will be adjusted depending on the margin
8482  * @natural_height: (inout): the actor's preferred natural height, which
8483  *   will be adjusted depending on the margin
8484  * @adjusted_y1: (out): the adjusted y1 for the actor's bounding box
8485  * @adjusted_y2: (out): the adjusted y2 for the actor's bounding box
8486  *
8487  * Adjusts the preferred and allocated position and size of an actor,
8488  * depending on the margin and alignment properties.
8489  */
8490 static void
clutter_actor_adjust_height(ClutterActor * self,gfloat * minimum_height,gfloat * natural_height,gfloat * adjusted_y1,gfloat * adjusted_y2)8491 clutter_actor_adjust_height (ClutterActor *self,
8492                              gfloat       *minimum_height,
8493                              gfloat       *natural_height,
8494                              gfloat       *adjusted_y1,
8495                              gfloat       *adjusted_y2)
8496 {
8497   const ClutterLayoutInfo *info;
8498 
8499   info = _clutter_actor_get_layout_info_or_defaults (self);
8500 
8501   CLUTTER_NOTE (LAYOUT, "Adjusting allocated Y and height");
8502 
8503   /* this will tweak natural_height to remove the margin, so that
8504    * adjust_for_alignment() will use the correct size
8505    */
8506   adjust_for_margin (info->margin.top, info->margin.bottom,
8507                      minimum_height, natural_height,
8508                      adjusted_y1,
8509                      adjusted_y2);
8510 
8511   /* we don't use effective_align() here, because text direction
8512    * only affects the horizontal axis
8513    */
8514   adjust_for_alignment (info->y_align,
8515                         *natural_height,
8516                         adjusted_y1,
8517                         adjusted_y2);
8518 
8519 }
8520 
8521 /* looks for a cached size request for this for_size. If not
8522  * found, returns the oldest entry so it can be overwritten */
8523 static gboolean
_clutter_actor_get_cached_size_request(gfloat for_size,SizeRequest * cached_size_requests,SizeRequest ** result)8524 _clutter_actor_get_cached_size_request (gfloat         for_size,
8525                                         SizeRequest   *cached_size_requests,
8526                                         SizeRequest  **result)
8527 {
8528   guint i;
8529 
8530   *result = &cached_size_requests[0];
8531 
8532   for (i = 0; i < N_CACHED_SIZE_REQUESTS; i++)
8533     {
8534       SizeRequest *sr;
8535 
8536       sr = &cached_size_requests[i];
8537 
8538       if (sr->age > 0 &&
8539           sr->for_size == for_size)
8540         {
8541           CLUTTER_NOTE (LAYOUT, "Size cache hit for size: %.2f", for_size);
8542           *result = sr;
8543           return TRUE;
8544         }
8545       else if (sr->age < (*result)->age)
8546         {
8547           *result = sr;
8548         }
8549     }
8550 
8551   CLUTTER_NOTE (LAYOUT, "Size cache miss for size: %.2f", for_size);
8552 
8553   return FALSE;
8554 }
8555 
8556 static void
clutter_actor_update_preferred_size_for_constraints(ClutterActor * self,ClutterOrientation direction,float for_size,float * minimum_size,float * natural_size)8557 clutter_actor_update_preferred_size_for_constraints (ClutterActor *self,
8558                                                      ClutterOrientation direction,
8559                                                      float for_size,
8560                                                      float *minimum_size,
8561                                                      float *natural_size)
8562 {
8563   ClutterActorPrivate *priv = self->priv;
8564   const GList *constraints, *l;
8565 
8566   if (priv->constraints == NULL)
8567     return;
8568 
8569   constraints = _clutter_meta_group_peek_metas (priv->constraints);
8570   for (l = constraints; l != NULL; l = l->next)
8571     {
8572       ClutterConstraint *constraint = l->data;
8573       ClutterActorMeta *meta = l->data;
8574 
8575       if (!clutter_actor_meta_get_enabled (meta))
8576         continue;
8577 
8578       clutter_constraint_update_preferred_size (constraint, self,
8579                                                 direction,
8580                                                 for_size,
8581                                                 minimum_size,
8582                                                 natural_size);
8583 
8584       CLUTTER_NOTE (LAYOUT,
8585                     "Preferred %s of '%s' after constraint '%s': "
8586                     "{ min:%.2f, nat:%.2f }",
8587                     direction == CLUTTER_ORIENTATION_HORIZONTAL
8588                       ? "width"
8589                       : "height",
8590                     _clutter_actor_get_debug_name (self),
8591                     _clutter_actor_meta_get_debug_name (meta),
8592                     *minimum_size, *natural_size);
8593     }
8594 }
8595 
8596 /**
8597  * clutter_actor_get_preferred_width:
8598  * @self: A #ClutterActor
8599  * @for_height: available height when computing the preferred width,
8600  *   or a negative value to indicate that no height is defined
8601  * @min_width_p: (out) (allow-none): return location for minimum width,
8602  *   or %NULL
8603  * @natural_width_p: (out) (allow-none): return location for the natural
8604  *   width, or %NULL
8605  *
8606  * Computes the requested minimum and natural widths for an actor,
8607  * optionally depending on the specified height, or if they are
8608  * already computed, returns the cached values.
8609  *
8610  * An actor may not get its request - depending on the layout
8611  * manager that's in effect.
8612  *
8613  * A request should not incorporate the actor's scaleor translation;
8614  * those transformations do not affect layout, only rendering.
8615  *
8616  * Since: 0.8
8617  */
8618 void
clutter_actor_get_preferred_width(ClutterActor * self,gfloat for_height,gfloat * min_width_p,gfloat * natural_width_p)8619 clutter_actor_get_preferred_width (ClutterActor *self,
8620                                    gfloat        for_height,
8621                                    gfloat       *min_width_p,
8622                                    gfloat       *natural_width_p)
8623 {
8624   float request_min_width, request_natural_width;
8625   SizeRequest *cached_size_request;
8626   const ClutterLayoutInfo *info;
8627   ClutterActorPrivate *priv;
8628   gboolean found_in_cache;
8629 
8630   g_return_if_fail (CLUTTER_IS_ACTOR (self));
8631 
8632   priv = self->priv;
8633 
8634   info = _clutter_actor_get_layout_info_or_defaults (self);
8635 
8636   /* we shortcircuit the case of a fixed size set using set_width() */
8637   if (priv->min_width_set && priv->natural_width_set)
8638     {
8639       if (min_width_p != NULL)
8640         *min_width_p = info->minimum.width + (info->margin.left + info->margin.right);
8641 
8642       if (natural_width_p != NULL)
8643         *natural_width_p = info->natural.width + (info->margin.left + info->margin.right);
8644 
8645       return;
8646     }
8647 
8648   /* if the request mode is CONTENT_SIZE we simply return the content width */
8649   if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
8650     {
8651       float content_width = 0.f;
8652 
8653       if (priv->content != NULL)
8654         clutter_content_get_preferred_size (priv->content, &content_width, NULL);
8655 
8656       if (min_width_p != NULL)
8657         *min_width_p = content_width;
8658 
8659       if (natural_width_p != NULL)
8660         *natural_width_p = content_width;
8661 
8662       return;
8663     }
8664 
8665   CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_WIDTH);
8666 
8667   /* the remaining cases are:
8668    *
8669    *   - either min_width or natural_width have been set
8670    *   - neither min_width or natural_width have been set
8671    *
8672    * in both cases, we go through the cache (and through the actor in case
8673    * of cache misses) and determine the authoritative value depending on
8674    * the *_set flags.
8675    */
8676 
8677   if (!priv->needs_width_request)
8678     {
8679       found_in_cache =
8680         _clutter_actor_get_cached_size_request (for_height,
8681                                                 priv->width_requests,
8682                                                 &cached_size_request);
8683     }
8684   else
8685     {
8686       /* if the actor needs a width request we use the first slot */
8687       found_in_cache = FALSE;
8688       cached_size_request = &priv->width_requests[0];
8689     }
8690 
8691   if (!found_in_cache)
8692     {
8693       gfloat minimum_width, natural_width;
8694       ClutterActorClass *klass;
8695 
8696       minimum_width = natural_width = 0;
8697 
8698       /* adjust for the margin */
8699       if (for_height >= 0)
8700         {
8701           for_height -= (info->margin.top + info->margin.bottom);
8702           if (for_height < 0)
8703             for_height = 0;
8704         }
8705 
8706       CLUTTER_NOTE (LAYOUT, "Width request for %.2f px", for_height);
8707 
8708       klass = CLUTTER_ACTOR_GET_CLASS (self);
8709       klass->get_preferred_width (self, for_height,
8710                                   &minimum_width,
8711                                   &natural_width);
8712 
8713       /* adjust for constraints */
8714       clutter_actor_update_preferred_size_for_constraints (self,
8715                                                            CLUTTER_ORIENTATION_HORIZONTAL,
8716                                                            for_height,
8717                                                            &minimum_width,
8718                                                            &natural_width);
8719 
8720       /* adjust for the margin */
8721       minimum_width += (info->margin.left + info->margin.right);
8722       natural_width += (info->margin.left + info->margin.right);
8723 
8724       /* Due to accumulated float errors, it's better not to warn
8725        * on this, but just fix it.
8726        */
8727       if (natural_width < minimum_width)
8728 	natural_width = minimum_width;
8729 
8730       cached_size_request->min_size = minimum_width;
8731       cached_size_request->natural_size = natural_width;
8732       cached_size_request->for_size = for_height;
8733       cached_size_request->age = priv->cached_width_age;
8734 
8735       priv->cached_width_age += 1;
8736       priv->needs_width_request = FALSE;
8737     }
8738 
8739   if (!priv->min_width_set)
8740     request_min_width = cached_size_request->min_size;
8741   else
8742     request_min_width = info->margin.left
8743                       + info->minimum.width
8744                       + info->margin.right;
8745 
8746   if (!priv->natural_width_set)
8747     request_natural_width = cached_size_request->natural_size;
8748   else
8749     request_natural_width = info->margin.left
8750                           + info->natural.width
8751                           + info->margin.right;
8752 
8753   if (min_width_p)
8754     *min_width_p = request_min_width;
8755 
8756   if (natural_width_p)
8757     *natural_width_p = request_natural_width;
8758 
8759   CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_WIDTH);
8760 }
8761 
8762 /**
8763  * clutter_actor_get_preferred_height:
8764  * @self: A #ClutterActor
8765  * @for_width: available width to assume in computing desired height,
8766  *   or a negative value to indicate that no width is defined
8767  * @min_height_p: (out) (allow-none): return location for minimum height,
8768  *   or %NULL
8769  * @natural_height_p: (out) (allow-none): return location for natural
8770  *   height, or %NULL
8771  *
8772  * Computes the requested minimum and natural heights for an actor,
8773  * or if they are already computed, returns the cached values.
8774  *
8775  * An actor may not get its request - depending on the layout
8776  * manager that's in effect.
8777  *
8778  * A request should not incorporate the actor's scale or translation;
8779  * those transformations do not affect layout, only rendering.
8780  *
8781  * Since: 0.8
8782  */
8783 void
clutter_actor_get_preferred_height(ClutterActor * self,gfloat for_width,gfloat * min_height_p,gfloat * natural_height_p)8784 clutter_actor_get_preferred_height (ClutterActor *self,
8785                                     gfloat        for_width,
8786                                     gfloat       *min_height_p,
8787                                     gfloat       *natural_height_p)
8788 {
8789   float request_min_height, request_natural_height;
8790   SizeRequest *cached_size_request;
8791   const ClutterLayoutInfo *info;
8792   ClutterActorPrivate *priv;
8793   gboolean found_in_cache;
8794 
8795   g_return_if_fail (CLUTTER_IS_ACTOR (self));
8796 
8797   priv = self->priv;
8798 
8799   info = _clutter_actor_get_layout_info_or_defaults (self);
8800 
8801   /* we shortcircuit the case of a fixed size set using set_height() */
8802   if (priv->min_height_set && priv->natural_height_set)
8803     {
8804       if (min_height_p != NULL)
8805         *min_height_p = info->minimum.height + (info->margin.top + info->margin.bottom);
8806 
8807       if (natural_height_p != NULL)
8808         *natural_height_p = info->natural.height + (info->margin.top + info->margin.bottom);
8809 
8810       return;
8811     }
8812 
8813   /* if the request mode is CONTENT_SIZE we simply return the content height */
8814   if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
8815     {
8816       float content_height = 0.f;
8817 
8818       if (priv->content != NULL)
8819         clutter_content_get_preferred_size (priv->content, NULL, &content_height);
8820 
8821       if (min_height_p != NULL)
8822         *min_height_p = content_height;
8823 
8824       if (natural_height_p != NULL)
8825         *natural_height_p = content_height;
8826 
8827       return;
8828     }
8829 
8830   CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_HEIGHT);
8831 
8832   /* the remaining cases are:
8833    *
8834    *   - either min_height or natural_height have been set
8835    *   - neither min_height or natural_height have been set
8836    *
8837    * in both cases, we go through the cache (and through the actor in case
8838    * of cache misses) and determine the authoritative value depending on
8839    * the *_set flags.
8840    */
8841 
8842   if (!priv->needs_height_request)
8843     {
8844       found_in_cache =
8845         _clutter_actor_get_cached_size_request (for_width,
8846                                                 priv->height_requests,
8847                                                 &cached_size_request);
8848     }
8849   else
8850     {
8851       found_in_cache = FALSE;
8852       cached_size_request = &priv->height_requests[0];
8853     }
8854 
8855   if (!found_in_cache)
8856     {
8857       gfloat minimum_height, natural_height;
8858       ClutterActorClass *klass;
8859 
8860       minimum_height = natural_height = 0;
8861 
8862       CLUTTER_NOTE (LAYOUT, "Height request for %.2f px", for_width);
8863 
8864       /* adjust for margin */
8865       if (for_width >= 0)
8866         {
8867           for_width -= (info->margin.left + info->margin.right);
8868           if (for_width < 0)
8869             for_width = 0;
8870         }
8871 
8872       klass = CLUTTER_ACTOR_GET_CLASS (self);
8873       klass->get_preferred_height (self, for_width,
8874                                    &minimum_height,
8875                                    &natural_height);
8876 
8877       /* adjust for constraints */
8878       clutter_actor_update_preferred_size_for_constraints (self,
8879                                                            CLUTTER_ORIENTATION_VERTICAL,
8880                                                            for_width,
8881                                                            &minimum_height,
8882                                                            &natural_height);
8883 
8884       /* adjust for margin */
8885       minimum_height += (info->margin.top + info->margin.bottom);
8886       natural_height += (info->margin.top + info->margin.bottom);
8887 
8888       /* Due to accumulated float errors, it's better not to warn
8889        * on this, but just fix it.
8890        */
8891       if (natural_height < minimum_height)
8892 	natural_height = minimum_height;
8893 
8894       cached_size_request->min_size = minimum_height;
8895       cached_size_request->natural_size = natural_height;
8896       cached_size_request->for_size = for_width;
8897       cached_size_request->age = priv->cached_height_age;
8898 
8899       priv->cached_height_age += 1;
8900       priv->needs_height_request = FALSE;
8901     }
8902 
8903   if (!priv->min_height_set)
8904     request_min_height = cached_size_request->min_size;
8905   else
8906     request_min_height = info->margin.top
8907                        + info->minimum.height
8908                        + info->margin.bottom;
8909 
8910   if (!priv->natural_height_set)
8911     request_natural_height = cached_size_request->natural_size;
8912   else
8913     request_natural_height = info->margin.top
8914                            + info->natural.height
8915                            + info->margin.bottom;
8916 
8917   if (min_height_p)
8918     *min_height_p = request_min_height;
8919 
8920   if (natural_height_p)
8921     *natural_height_p = request_natural_height;
8922 
8923   CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_HEIGHT);
8924 }
8925 
8926 /**
8927  * clutter_actor_get_allocation_box:
8928  * @self: A #ClutterActor
8929  * @box: (out): the function fills this in with the actor's allocation
8930  *
8931  * Gets the layout box an actor has been assigned. The allocation can
8932  * only be assumed valid inside a paint() method; anywhere else, it
8933  * may be out-of-date.
8934  *
8935  * An allocation does not incorporate the actor's scale or translation;
8936  * those transformations do not affect layout, only rendering.
8937  *
8938  * Do not call any of the clutter_actor_get_allocation_*() family
8939  * of functions inside the implementation of the get_preferred_width()
8940  * or get_preferred_height() virtual functions.
8941  *
8942  * Since: 0.8
8943  */
8944 void
clutter_actor_get_allocation_box(ClutterActor * self,ClutterActorBox * box)8945 clutter_actor_get_allocation_box (ClutterActor    *self,
8946                                   ClutterActorBox *box)
8947 {
8948   g_return_if_fail (CLUTTER_IS_ACTOR (self));
8949 
8950   /* XXX - if needs_allocation=TRUE, we can either 1) g_return_if_fail,
8951    * which limits calling get_allocation to inside paint() basically; or
8952    * we can 2) force a layout, which could be expensive if someone calls
8953    * get_allocation somewhere silly; or we can 3) just return the latest
8954    * value, allowing it to be out-of-date, and assume people know what
8955    * they are doing.
8956    *
8957    * The least-surprises approach that keeps existing code working is
8958    * likely to be 2). People can end up doing some inefficient things,
8959    * though, and in general code that requires 2) is probably broken.
8960    */
8961 
8962   /* this implements 2) */
8963   if (G_UNLIKELY (self->priv->needs_allocation))
8964     {
8965       ClutterActor *stage = _clutter_actor_get_stage_internal (self);
8966 
8967       /* do not queue a relayout on an unparented actor */
8968       if (stage)
8969         clutter_stage_maybe_relayout (stage);
8970     }
8971 
8972   /* commenting out the code above and just keeping this assignment
8973    * implements 3)
8974    */
8975   *box = self->priv->allocation;
8976 }
8977 
8978 static void
clutter_actor_update_constraints(ClutterActor * self,ClutterActorBox * allocation)8979 clutter_actor_update_constraints (ClutterActor    *self,
8980                                   ClutterActorBox *allocation)
8981 {
8982   ClutterActorPrivate *priv = self->priv;
8983   const GList *constraints, *l;
8984 
8985   if (priv->constraints == NULL)
8986     return;
8987 
8988   constraints = _clutter_meta_group_peek_metas (priv->constraints);
8989   for (l = constraints; l != NULL; l = l->next)
8990     {
8991       ClutterConstraint *constraint = l->data;
8992       ClutterActorMeta *meta = l->data;
8993       gboolean changed = FALSE;
8994 
8995       if (clutter_actor_meta_get_enabled (meta))
8996         {
8997           changed |=
8998             clutter_constraint_update_allocation (constraint,
8999                                                   self,
9000                                                   allocation);
9001 
9002           CLUTTER_NOTE (LAYOUT,
9003                         "Allocation of '%s' after constraint '%s': "
9004                         "{ %.2f, %.2f, %.2f, %.2f } (changed:%s)",
9005                         _clutter_actor_get_debug_name (self),
9006                         _clutter_actor_meta_get_debug_name (meta),
9007                         allocation->x1,
9008                         allocation->y1,
9009                         allocation->x2,
9010                         allocation->y2,
9011                         changed ? "yes" : "no");
9012         }
9013     }
9014 }
9015 
9016 /*< private >
9017  * clutter_actor_adjust_allocation:
9018  * @self: a #ClutterActor
9019  * @allocation: (inout): the allocation to adjust
9020  *
9021  * Adjusts the passed allocation box taking into account the actor's
9022  * layout information, like alignment, expansion, and margin.
9023  */
9024 static void
clutter_actor_adjust_allocation(ClutterActor * self,ClutterActorBox * allocation)9025 clutter_actor_adjust_allocation (ClutterActor    *self,
9026                                  ClutterActorBox *allocation)
9027 {
9028   ClutterActorBox adj_allocation;
9029   float alloc_width, alloc_height;
9030   float min_width, min_height;
9031   float nat_width, nat_height;
9032   ClutterRequestMode req_mode;
9033 
9034   adj_allocation = *allocation;
9035 
9036   clutter_actor_box_get_size (allocation, &alloc_width, &alloc_height);
9037 
9038   /* There's no point in trying to adjust a zero-sized actor */
9039   if (alloc_width == 0.f && alloc_height == 0.f)
9040     return;
9041 
9042   /* we want to hit the cache, so we use the public API */
9043   req_mode = clutter_actor_get_request_mode (self);
9044 
9045   if (req_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
9046     {
9047       clutter_actor_get_preferred_width (self, -1,
9048                                          &min_width,
9049                                          &nat_width);
9050       clutter_actor_get_preferred_height (self, alloc_width,
9051                                           &min_height,
9052                                           &nat_height);
9053     }
9054   else if (req_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
9055     {
9056       clutter_actor_get_preferred_height (self, -1,
9057                                           &min_height,
9058                                           &nat_height);
9059       clutter_actor_get_preferred_width (self, alloc_height,
9060                                          &min_width,
9061                                          &nat_width);
9062     }
9063   else if (req_mode == CLUTTER_REQUEST_CONTENT_SIZE)
9064     {
9065       min_width = min_height = 0;
9066       nat_width = nat_height = 0;
9067 
9068       if (self->priv->content != NULL)
9069         clutter_content_get_preferred_size (self->priv->content, &nat_width, &nat_height);
9070     }
9071 
9072 #ifdef CLUTTER_ENABLE_DEBUG
9073   /* warn about underallocations */
9074   if (_clutter_diagnostic_enabled () &&
9075       (floorf (min_width - alloc_width) > 0 ||
9076        floorf (min_height - alloc_height) > 0))
9077     {
9078       ClutterActor *parent = clutter_actor_get_parent (self);
9079 
9080       /* the only actors that are allowed to be underallocated are the Stage,
9081        * as it doesn't have an implicit size, and Actors that specifically
9082        * told us that they want to opt-out from layout control mechanisms
9083        * through the NO_LAYOUT escape hatch.
9084        */
9085       if (parent != NULL &&
9086           !(self->flags & CLUTTER_ACTOR_NO_LAYOUT) != 0)
9087         {
9088           g_warning (G_STRLOC ": The actor '%s' is getting an allocation "
9089                      "of %.2f x %.2f from its parent actor '%s', but its "
9090                      "requested minimum size is of %.2f x %.2f",
9091                      _clutter_actor_get_debug_name (self),
9092                      alloc_width, alloc_height,
9093                      _clutter_actor_get_debug_name (parent),
9094                      min_width, min_height);
9095         }
9096     }
9097 #endif
9098 
9099   clutter_actor_adjust_width (self,
9100                               &min_width,
9101                               &nat_width,
9102                               &adj_allocation.x1,
9103                               &adj_allocation.x2);
9104 
9105   clutter_actor_adjust_height (self,
9106                                &min_height,
9107                                &nat_height,
9108                                &adj_allocation.y1,
9109                                &adj_allocation.y2);
9110 
9111   /* we maintain the invariant that an allocation cannot be adjusted
9112    * to be outside the parent-given box
9113    */
9114   if (adj_allocation.x1 < allocation->x1 ||
9115       adj_allocation.y1 < allocation->y1 ||
9116       adj_allocation.x2 > allocation->x2 ||
9117       adj_allocation.y2 > allocation->y2)
9118     {
9119       g_warning (G_STRLOC ": The actor '%s' tried to adjust its allocation "
9120                  "to { %.2f, %.2f, %.2f, %.2f }, which is outside of its "
9121                  "original allocation of { %.2f, %.2f, %.2f, %.2f }",
9122                  _clutter_actor_get_debug_name (self),
9123                  adj_allocation.x1, adj_allocation.y1,
9124                  adj_allocation.x2 - adj_allocation.x1,
9125                  adj_allocation.y2 - adj_allocation.y1,
9126                  allocation->x1, allocation->y1,
9127                  allocation->x2 - allocation->x1,
9128                  allocation->y2 - allocation->y1);
9129       return;
9130     }
9131 
9132   *allocation = adj_allocation;
9133 }
9134 
9135 static void
clutter_actor_allocate_internal(ClutterActor * self,const ClutterActorBox * allocation)9136 clutter_actor_allocate_internal (ClutterActor           *self,
9137                                  const ClutterActorBox  *allocation)
9138 {
9139   ClutterActorClass *klass;
9140 
9141   CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
9142 
9143   CLUTTER_NOTE (LAYOUT, "Calling %s::allocate()",
9144                 _clutter_actor_get_debug_name (self));
9145 
9146   klass = CLUTTER_ACTOR_GET_CLASS (self);
9147   klass->allocate (self, allocation);
9148 
9149   CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
9150 
9151   /* Caller should call clutter_actor_queue_redraw() if needed
9152    * for that particular case.
9153    */
9154 }
9155 
9156 /**
9157  * clutter_actor_allocate:
9158  * @self: A #ClutterActor
9159  * @box: new allocation of the actor, in parent-relative coordinates
9160  *
9161  * Assigns the size of a #ClutterActor from the given @box.
9162  *
9163  * This function should only be called on the children of an actor when
9164  * overriding the #ClutterActorClass.allocate() virtual function.
9165  *
9166  * This function will adjust the stored allocation to take into account
9167  * the alignment flags set in the #ClutterActor:x-align and
9168  * #ClutterActor:y-align properties, as well as the margin values set in
9169  * the #ClutterActor:margin-top, #ClutterActor:margin-right,
9170  * #ClutterActor:margin-bottom, and #ClutterActor:margin-left properties.
9171  *
9172  * This function will respect the easing state of the #ClutterActor and
9173  * interpolate between the current allocation and the new one if the
9174  * easing state duration is a positive value.
9175  *
9176  * Actors can know from their allocation box whether they have moved
9177  * with respect to their parent actor. The @flags parameter describes
9178  * additional information about the allocation, for instance whether
9179  * the parent has moved with respect to the stage, for example because
9180  * a grandparent's origin has moved.
9181  *
9182  * Since: 0.8
9183  */
9184 void
clutter_actor_allocate(ClutterActor * self,const ClutterActorBox * box)9185 clutter_actor_allocate (ClutterActor          *self,
9186                         const ClutterActorBox *box)
9187 {
9188   ClutterActorBox old_allocation, real_allocation;
9189   gboolean origin_changed, size_changed;
9190   ClutterActorPrivate *priv;
9191 
9192   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9193   if (G_UNLIKELY (_clutter_actor_get_stage_internal (self) == NULL))
9194     {
9195       g_warning ("Spurious clutter_actor_allocate called for actor %p/%s "
9196                  "which isn't a descendent of the stage!\n",
9197                  self, _clutter_actor_get_debug_name (self));
9198       return;
9199     }
9200 
9201   priv = self->priv;
9202 
9203   if (!CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
9204       !CLUTTER_ACTOR_IS_MAPPED (self) &&
9205       !clutter_actor_has_mapped_clones (self))
9206     return;
9207 
9208 #ifdef COGL_HAS_TRACING
9209   COGL_TRACE_SCOPED_ANCHOR (ClutterActorAllocate);
9210 
9211   if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_DETAILED_TRACE))
9212     {
9213       COGL_TRACE_BEGIN_ANCHORED (ClutterActorAllocate,
9214                                  "ClutterActor (allocate)");
9215       COGL_TRACE_DESCRIBE (ClutterActorAllocate,
9216                            _clutter_actor_get_debug_name (self));
9217     }
9218 #endif
9219 
9220   old_allocation = priv->allocation;
9221   real_allocation = *box;
9222 
9223   g_return_if_fail (!isnan (real_allocation.x1) &&
9224                     !isnan (real_allocation.x2) &&
9225                     !isnan (real_allocation.y1) &&
9226                     !isnan (real_allocation.y2));
9227 
9228   /* constraints are allowed to modify the allocation only here; we do
9229    * this prior to all the other checks so that we can bail out if the
9230    * allocation did not change
9231    */
9232   clutter_actor_update_constraints (self, &real_allocation);
9233 
9234   /* adjust the allocation depending on the align/margin properties */
9235   clutter_actor_adjust_allocation (self, &real_allocation);
9236 
9237   if (real_allocation.x2 < real_allocation.x1 ||
9238       real_allocation.y2 < real_allocation.y1)
9239     {
9240       g_warning (G_STRLOC ": Actor '%s' tried to allocate a size of %.2f x %.2f",
9241                  _clutter_actor_get_debug_name (self),
9242                  real_allocation.x2 - real_allocation.x1,
9243                  real_allocation.y2 - real_allocation.y1);
9244     }
9245 
9246   /* we allow 0-sized actors, but not negative-sized ones */
9247   real_allocation.x2 = MAX (real_allocation.x2, real_allocation.x1);
9248   real_allocation.y2 = MAX (real_allocation.y2, real_allocation.y1);
9249 
9250   origin_changed = (real_allocation.x1 != old_allocation.x1 ||
9251                     real_allocation.y1 != old_allocation.y1);
9252 
9253   size_changed = (real_allocation.x2 != old_allocation.x2 ||
9254                   real_allocation.y2 != old_allocation.y2);
9255 
9256   /* When needs_allocation is set but we didn't move nor resize, we still
9257    * want to call the allocate() vfunc because a child probably called
9258    * queue_relayout() and needs a new allocation.
9259    *
9260    * In case needs_allocation isn't set and we didn't move nor resize, we
9261    * can safely stop allocating.
9262    */
9263   if (!priv->needs_allocation && !origin_changed && !size_changed)
9264     {
9265       CLUTTER_NOTE (LAYOUT, "No allocation needed");
9266       return;
9267     }
9268 
9269   if (!origin_changed && !size_changed)
9270     {
9271       /* If the actor didn't move but needs_allocation is set, we just
9272        * need to allocate the children (see comment above) */
9273       clutter_actor_allocate_internal (self, &real_allocation);
9274       return;
9275     }
9276 
9277   if (_clutter_actor_create_transition (self, obj_props[PROP_ALLOCATION],
9278                                         &priv->allocation,
9279                                         &real_allocation))
9280     clutter_actor_allocate_internal (self, &priv->allocation);
9281 }
9282 
9283 /**
9284  * clutter_actor_set_allocation:
9285  * @self: a #ClutterActor
9286  * @box: a #ClutterActorBox
9287  *
9288  * Stores the allocation of @self as defined by @box.
9289  *
9290  * This function can only be called from within the implementation of
9291  * the #ClutterActorClass.allocate() virtual function.
9292  *
9293  * The allocation @box should have been adjusted to take into account
9294  * constraints, alignment, and margin properties.
9295  *
9296  * This function should only be used by subclasses of #ClutterActor
9297  * that wish to store their allocation but cannot chain up to the
9298  * parent's implementation; the default implementation of the
9299  * #ClutterActorClass.allocate() virtual function will call this
9300  * function.
9301  *
9302  * Since: 1.10
9303  */
9304 void
clutter_actor_set_allocation(ClutterActor * self,const ClutterActorBox * box)9305 clutter_actor_set_allocation (ClutterActor           *self,
9306                               const ClutterActorBox  *box)
9307 {
9308   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9309   g_return_if_fail (box != NULL);
9310 
9311   if (G_UNLIKELY (!CLUTTER_ACTOR_IN_RELAYOUT (self)))
9312     {
9313       g_critical (G_STRLOC ": The clutter_actor_set_allocation() function "
9314                   "can only be called from within the implementation of "
9315                   "the ClutterActor::allocate() virtual function.");
9316       return;
9317     }
9318 
9319   g_object_freeze_notify (G_OBJECT (self));
9320 
9321   clutter_actor_set_allocation_internal (self, box);
9322 
9323   g_object_thaw_notify (G_OBJECT (self));
9324 }
9325 
9326 /**
9327  * clutter_actor_set_position:
9328  * @self: A #ClutterActor
9329  * @x: New left position of actor in pixels.
9330  * @y: New top position of actor in pixels.
9331  *
9332  * Sets the actor's fixed position in pixels relative to any parent
9333  * actor.
9334  *
9335  * If a layout manager is in use, this position will override the
9336  * layout manager and force a fixed position.
9337  */
9338 void
clutter_actor_set_position(ClutterActor * self,gfloat x,gfloat y)9339 clutter_actor_set_position (ClutterActor *self,
9340 			    gfloat        x,
9341 			    gfloat        y)
9342 {
9343   graphene_point_t new_position;
9344   graphene_point_t cur_position;
9345 
9346   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9347 
9348   graphene_point_init (&new_position, x, y);
9349 
9350   cur_position.x = clutter_actor_get_x (self);
9351   cur_position.y = clutter_actor_get_y (self);
9352 
9353   if (!graphene_point_equal (&cur_position, &new_position))
9354     _clutter_actor_create_transition (self, obj_props[PROP_POSITION],
9355                                       &cur_position,
9356                                       &new_position);
9357 }
9358 
9359 /**
9360  * clutter_actor_get_fixed_position_set:
9361  * @self: A #ClutterActor
9362  *
9363  * Checks whether an actor has a fixed position set (and will thus be
9364  * unaffected by any layout manager).
9365  *
9366  * Return value: %TRUE if the fixed position is set on the actor
9367  *
9368  * Since: 0.8
9369  */
9370 gboolean
clutter_actor_get_fixed_position_set(ClutterActor * self)9371 clutter_actor_get_fixed_position_set (ClutterActor *self)
9372 {
9373   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
9374 
9375   return self->priv->position_set;
9376 }
9377 
9378 /**
9379  * clutter_actor_set_fixed_position_set:
9380  * @self: A #ClutterActor
9381  * @is_set: whether to use fixed position
9382  *
9383  * Sets whether an actor has a fixed position set (and will thus be
9384  * unaffected by any layout manager).
9385  *
9386  * Since: 0.8
9387  */
9388 void
clutter_actor_set_fixed_position_set(ClutterActor * self,gboolean is_set)9389 clutter_actor_set_fixed_position_set (ClutterActor *self,
9390                                       gboolean      is_set)
9391 {
9392   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9393 
9394   if (self->priv->position_set == (is_set != FALSE))
9395     return;
9396 
9397   if (!is_set)
9398     {
9399       ClutterLayoutInfo *info;
9400 
9401       /* Ensure we set back the default fixed position of 0,0 so that setting
9402 	 just one of x/y always atomically gets 0 for the other */
9403       info = _clutter_actor_peek_layout_info (self);
9404       if (info != NULL)
9405 	{
9406 	  info->fixed_pos.x = 0;
9407 	  info->fixed_pos.y = 0;
9408 	}
9409     }
9410 
9411   self->priv->position_set = is_set != FALSE;
9412   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIXED_POSITION_SET]);
9413 
9414   clutter_actor_queue_relayout (self);
9415 }
9416 
9417 /**
9418  * clutter_actor_move_by:
9419  * @self: A #ClutterActor
9420  * @dx: Distance to move Actor on X axis.
9421  * @dy: Distance to move Actor on Y axis.
9422  *
9423  * Moves an actor by the specified distance relative to its current
9424  * position in pixels.
9425  *
9426  * This function modifies the fixed position of an actor and thus removes
9427  * it from any layout management. Another way to move an actor is with an
9428  * additional translation, using clutter_actor_set_translation().
9429  *
9430  * Since: 0.2
9431  */
9432 void
clutter_actor_move_by(ClutterActor * self,gfloat dx,gfloat dy)9433 clutter_actor_move_by (ClutterActor *self,
9434 		       gfloat        dx,
9435 		       gfloat        dy)
9436 {
9437   const ClutterLayoutInfo *info;
9438   gfloat x, y;
9439 
9440   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9441 
9442   info = _clutter_actor_get_layout_info_or_defaults (self);
9443   x = info->fixed_pos.x;
9444   y = info->fixed_pos.y;
9445 
9446   clutter_actor_set_position (self, x + dx, y + dy);
9447 }
9448 
9449 static void
clutter_actor_set_min_width(ClutterActor * self,gfloat min_width)9450 clutter_actor_set_min_width (ClutterActor *self,
9451                              gfloat        min_width)
9452 {
9453   ClutterActorPrivate *priv = self->priv;
9454   ClutterActorBox old = { 0, };
9455   ClutterLayoutInfo *info;
9456 
9457   /* if we are setting the size on a top-level actor and the
9458    * backend only supports static top-levels (e.g. framebuffers)
9459    * then we ignore the passed value and we override it with
9460    * the stage implementation's preferred size.
9461    */
9462   if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
9463       clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
9464     return;
9465 
9466   info = _clutter_actor_get_layout_info (self);
9467 
9468   if (priv->min_width_set && min_width == info->minimum.width)
9469     return;
9470 
9471   g_object_freeze_notify (G_OBJECT (self));
9472 
9473   clutter_actor_store_old_geometry (self, &old);
9474 
9475   info->minimum.width = min_width;
9476   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH]);
9477   clutter_actor_set_min_width_set (self, TRUE);
9478 
9479   clutter_actor_notify_if_geometry_changed (self, &old);
9480 
9481   g_object_thaw_notify (G_OBJECT (self));
9482 
9483   clutter_actor_queue_relayout (self);
9484 }
9485 
9486 static void
clutter_actor_set_min_height(ClutterActor * self,gfloat min_height)9487 clutter_actor_set_min_height (ClutterActor *self,
9488                               gfloat        min_height)
9489 
9490 {
9491   ClutterActorPrivate *priv = self->priv;
9492   ClutterActorBox old = { 0, };
9493   ClutterLayoutInfo *info;
9494 
9495   /* if we are setting the size on a top-level actor and the
9496    * backend only supports static top-levels (e.g. framebuffers)
9497    * then we ignore the passed value and we override it with
9498    * the stage implementation's preferred size.
9499    */
9500   if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
9501       clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
9502     return;
9503 
9504   info = _clutter_actor_get_layout_info (self);
9505 
9506   if (priv->min_height_set && min_height == info->minimum.height)
9507     return;
9508 
9509   g_object_freeze_notify (G_OBJECT (self));
9510 
9511   clutter_actor_store_old_geometry (self, &old);
9512 
9513   info->minimum.height = min_height;
9514   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT]);
9515   clutter_actor_set_min_height_set (self, TRUE);
9516 
9517   clutter_actor_notify_if_geometry_changed (self, &old);
9518 
9519   g_object_thaw_notify (G_OBJECT (self));
9520 
9521   clutter_actor_queue_relayout (self);
9522 }
9523 
9524 static void
clutter_actor_set_natural_width(ClutterActor * self,gfloat natural_width)9525 clutter_actor_set_natural_width (ClutterActor *self,
9526                                  gfloat        natural_width)
9527 {
9528   ClutterActorPrivate *priv = self->priv;
9529   ClutterActorBox old = { 0, };
9530   ClutterLayoutInfo *info;
9531 
9532   /* if we are setting the size on a top-level actor and the
9533    * backend only supports static top-levels (e.g. framebuffers)
9534    * then we ignore the passed value and we override it with
9535    * the stage implementation's preferred size.
9536    */
9537   if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
9538       clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
9539     return;
9540 
9541   info = _clutter_actor_get_layout_info (self);
9542 
9543   if (priv->natural_width_set && natural_width == info->natural.width)
9544     return;
9545 
9546   g_object_freeze_notify (G_OBJECT (self));
9547 
9548   clutter_actor_store_old_geometry (self, &old);
9549 
9550   info->natural.width = natural_width;
9551   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH]);
9552   clutter_actor_set_natural_width_set (self, TRUE);
9553 
9554   clutter_actor_notify_if_geometry_changed (self, &old);
9555 
9556   g_object_thaw_notify (G_OBJECT (self));
9557 
9558   clutter_actor_queue_relayout (self);
9559 }
9560 
9561 static void
clutter_actor_set_natural_height(ClutterActor * self,gfloat natural_height)9562 clutter_actor_set_natural_height (ClutterActor *self,
9563                                   gfloat        natural_height)
9564 {
9565   ClutterActorPrivate *priv = self->priv;
9566   ClutterActorBox old = { 0, };
9567   ClutterLayoutInfo *info;
9568 
9569   /* if we are setting the size on a top-level actor and the
9570    * backend only supports static top-levels (e.g. framebuffers)
9571    * then we ignore the passed value and we override it with
9572    * the stage implementation's preferred size.
9573    */
9574   if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
9575       clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
9576     return;
9577 
9578   info = _clutter_actor_get_layout_info (self);
9579 
9580   if (priv->natural_height_set && natural_height == info->natural.height)
9581     return;
9582 
9583   g_object_freeze_notify (G_OBJECT (self));
9584 
9585   clutter_actor_store_old_geometry (self, &old);
9586 
9587   info->natural.height = natural_height;
9588   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT]);
9589   clutter_actor_set_natural_height_set (self, TRUE);
9590 
9591   clutter_actor_notify_if_geometry_changed (self, &old);
9592 
9593   g_object_thaw_notify (G_OBJECT (self));
9594 
9595   clutter_actor_queue_relayout (self);
9596 }
9597 
9598 static void
clutter_actor_set_min_width_set(ClutterActor * self,gboolean use_min_width)9599 clutter_actor_set_min_width_set (ClutterActor *self,
9600                                  gboolean      use_min_width)
9601 {
9602   ClutterActorPrivate *priv = self->priv;
9603   ClutterActorBox old = { 0, };
9604 
9605   if (priv->min_width_set == (use_min_width != FALSE))
9606     return;
9607 
9608   clutter_actor_store_old_geometry (self, &old);
9609 
9610   priv->min_width_set = use_min_width != FALSE;
9611   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH_SET]);
9612 
9613   clutter_actor_notify_if_geometry_changed (self, &old);
9614 
9615   clutter_actor_queue_relayout (self);
9616 }
9617 
9618 static void
clutter_actor_set_min_height_set(ClutterActor * self,gboolean use_min_height)9619 clutter_actor_set_min_height_set (ClutterActor *self,
9620                                   gboolean      use_min_height)
9621 {
9622   ClutterActorPrivate *priv = self->priv;
9623   ClutterActorBox old = { 0, };
9624 
9625   if (priv->min_height_set == (use_min_height != FALSE))
9626     return;
9627 
9628   clutter_actor_store_old_geometry (self, &old);
9629 
9630   priv->min_height_set = use_min_height != FALSE;
9631   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT_SET]);
9632 
9633   clutter_actor_notify_if_geometry_changed (self, &old);
9634 
9635   clutter_actor_queue_relayout (self);
9636 }
9637 
9638 static void
clutter_actor_set_natural_width_set(ClutterActor * self,gboolean use_natural_width)9639 clutter_actor_set_natural_width_set (ClutterActor *self,
9640                                      gboolean      use_natural_width)
9641 {
9642   ClutterActorPrivate *priv = self->priv;
9643   ClutterActorBox old = { 0, };
9644 
9645   if (priv->natural_width_set == (use_natural_width != FALSE))
9646     return;
9647 
9648   clutter_actor_store_old_geometry (self, &old);
9649 
9650   priv->natural_width_set = use_natural_width != FALSE;
9651   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH_SET]);
9652 
9653   clutter_actor_notify_if_geometry_changed (self, &old);
9654 
9655   clutter_actor_queue_relayout (self);
9656 }
9657 
9658 static void
clutter_actor_set_natural_height_set(ClutterActor * self,gboolean use_natural_height)9659 clutter_actor_set_natural_height_set (ClutterActor *self,
9660                                       gboolean      use_natural_height)
9661 {
9662   ClutterActorPrivate *priv = self->priv;
9663   ClutterActorBox old = { 0, };
9664 
9665   if (priv->natural_height_set == (use_natural_height != FALSE))
9666     return;
9667 
9668   clutter_actor_store_old_geometry (self, &old);
9669 
9670   priv->natural_height_set = use_natural_height != FALSE;
9671   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT_SET]);
9672 
9673   clutter_actor_notify_if_geometry_changed (self, &old);
9674 
9675   clutter_actor_queue_relayout (self);
9676 }
9677 
9678 /**
9679  * clutter_actor_set_request_mode:
9680  * @self: a #ClutterActor
9681  * @mode: the request mode
9682  *
9683  * Sets the geometry request mode of @self.
9684  *
9685  * The @mode determines the order for invoking
9686  * clutter_actor_get_preferred_width() and
9687  * clutter_actor_get_preferred_height()
9688  *
9689  * Since: 1.2
9690  */
9691 void
clutter_actor_set_request_mode(ClutterActor * self,ClutterRequestMode mode)9692 clutter_actor_set_request_mode (ClutterActor       *self,
9693                                 ClutterRequestMode  mode)
9694 {
9695   ClutterActorPrivate *priv;
9696 
9697   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9698 
9699   priv = self->priv;
9700 
9701   if (priv->request_mode == mode)
9702     return;
9703 
9704   priv->request_mode = mode;
9705 
9706   priv->needs_width_request = TRUE;
9707   priv->needs_height_request = TRUE;
9708 
9709   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REQUEST_MODE]);
9710 
9711   clutter_actor_queue_relayout (self);
9712 }
9713 
9714 /**
9715  * clutter_actor_get_request_mode:
9716  * @self: a #ClutterActor
9717  *
9718  * Retrieves the geometry request mode of @self
9719  *
9720  * Return value: the request mode for the actor
9721  *
9722  * Since: 1.2
9723  */
9724 ClutterRequestMode
clutter_actor_get_request_mode(ClutterActor * self)9725 clutter_actor_get_request_mode (ClutterActor *self)
9726 {
9727   g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
9728                         CLUTTER_REQUEST_HEIGHT_FOR_WIDTH);
9729 
9730   return self->priv->request_mode;
9731 }
9732 
9733 /* variant of set_width() without checks and without notification
9734  * freeze+thaw, for internal usage only
9735  */
9736 static inline void
clutter_actor_set_width_internal(ClutterActor * self,gfloat width)9737 clutter_actor_set_width_internal (ClutterActor *self,
9738                                   gfloat        width)
9739 {
9740   if (width >= 0)
9741     {
9742       /* the Stage will use the :min-width to control the minimum
9743        * width to be resized to, so we should not be setting it
9744        * along with the :natural-width
9745        */
9746       if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
9747         clutter_actor_set_min_width (self, width);
9748 
9749       clutter_actor_set_natural_width (self, width);
9750     }
9751   else
9752     {
9753       /* we only unset the :natural-width for the Stage */
9754       if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
9755         clutter_actor_set_min_width_set (self, FALSE);
9756 
9757       clutter_actor_set_natural_width_set (self, FALSE);
9758     }
9759 }
9760 
9761 /* variant of set_height() without checks and without notification
9762  * freeze+thaw, for internal usage only
9763  */
9764 static inline void
clutter_actor_set_height_internal(ClutterActor * self,gfloat height)9765 clutter_actor_set_height_internal (ClutterActor *self,
9766                                    gfloat        height)
9767 {
9768   if (height >= 0)
9769     {
9770       /* see the comment above in set_width_internal() */
9771       if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
9772         clutter_actor_set_min_height (self, height);
9773 
9774       clutter_actor_set_natural_height (self, height);
9775     }
9776   else
9777     {
9778       /* see the comment above in set_width_internal() */
9779       if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
9780         clutter_actor_set_min_height_set (self, FALSE);
9781 
9782       clutter_actor_set_natural_height_set (self, FALSE);
9783     }
9784 }
9785 
9786 static void
clutter_actor_set_size_internal(ClutterActor * self,const graphene_size_t * size)9787 clutter_actor_set_size_internal (ClutterActor          *self,
9788                                  const graphene_size_t *size)
9789 {
9790   if (size != NULL)
9791     {
9792       clutter_actor_set_width_internal (self, size->width);
9793       clutter_actor_set_height_internal (self, size->height);
9794     }
9795   else
9796     {
9797       clutter_actor_set_width_internal (self, -1);
9798       clutter_actor_set_height_internal (self, -1);
9799     }
9800 }
9801 
9802 /**
9803  * clutter_actor_set_size:
9804  * @self: A #ClutterActor
9805  * @width: New width of actor in pixels, or -1
9806  * @height: New height of actor in pixels, or -1
9807  *
9808  * Sets the actor's size request in pixels. This overrides any
9809  * "normal" size request the actor would have. For example
9810  * a text actor might normally request the size of the text;
9811  * this function would force a specific size instead.
9812  *
9813  * If @width and/or @height are -1 the actor will use its
9814  * "normal" size request instead of overriding it, i.e.
9815  * you can "unset" the size with -1.
9816  *
9817  * This function sets or unsets both the minimum and natural size.
9818  */
9819 void
clutter_actor_set_size(ClutterActor * self,gfloat width,gfloat height)9820 clutter_actor_set_size (ClutterActor *self,
9821 			gfloat        width,
9822 			gfloat        height)
9823 {
9824   graphene_size_t new_size;
9825 
9826   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9827 
9828   graphene_size_init (&new_size, width, height);
9829 
9830   /* minor optimization: if we don't have a duration then we can
9831    * skip the get_size() below, to avoid the chance of going through
9832    * get_preferred_width() and get_preferred_height() just to jump to
9833    * a new desired size
9834    */
9835   if (clutter_actor_get_easing_duration (self) == 0)
9836     {
9837       g_object_freeze_notify (G_OBJECT (self));
9838 
9839       clutter_actor_set_size_internal (self, &new_size);
9840 
9841       g_object_thaw_notify (G_OBJECT (self));
9842 
9843       return;
9844     }
9845   else
9846     {
9847       graphene_size_t cur_size;
9848 
9849       graphene_size_init (&cur_size,
9850                           clutter_actor_get_width (self),
9851                           clutter_actor_get_height (self));
9852 
9853       _clutter_actor_create_transition (self,
9854                                         obj_props[PROP_SIZE],
9855                                         &cur_size,
9856                                         &new_size);
9857     }
9858 }
9859 
9860 /**
9861  * clutter_actor_get_size:
9862  * @self: A #ClutterActor
9863  * @width: (out) (allow-none): return location for the width, or %NULL.
9864  * @height: (out) (allow-none): return location for the height, or %NULL.
9865  *
9866  * This function tries to "do what you mean" and return
9867  * the size an actor will have. If the actor has a valid
9868  * allocation, the allocation will be returned; otherwise,
9869  * the actors natural size request will be returned.
9870  *
9871  * If you care whether you get the request vs. the allocation, you
9872  * should probably call a different function like
9873  * clutter_actor_get_allocation_box() or
9874  * clutter_actor_get_preferred_width().
9875  *
9876  * Since: 0.2
9877  */
9878 void
clutter_actor_get_size(ClutterActor * self,gfloat * width,gfloat * height)9879 clutter_actor_get_size (ClutterActor *self,
9880 			gfloat       *width,
9881 			gfloat       *height)
9882 {
9883   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9884 
9885   if (width)
9886     *width = clutter_actor_get_width (self);
9887 
9888   if (height)
9889     *height = clutter_actor_get_height (self);
9890 }
9891 
9892 /**
9893  * clutter_actor_get_position:
9894  * @self: a #ClutterActor
9895  * @x: (out) (allow-none): return location for the X coordinate, or %NULL
9896  * @y: (out) (allow-none): return location for the Y coordinate, or %NULL
9897  *
9898  * This function tries to "do what you mean" and tell you where the
9899  * actor is, prior to any transformations. Retrieves the fixed
9900  * position of an actor in pixels, if one has been set; otherwise, if
9901  * the allocation is valid, returns the actor's allocated position;
9902  * otherwise, returns 0,0.
9903  *
9904  * The returned position is in pixels.
9905  *
9906  * Since: 0.6
9907  */
9908 void
clutter_actor_get_position(ClutterActor * self,gfloat * x,gfloat * y)9909 clutter_actor_get_position (ClutterActor *self,
9910                             gfloat       *x,
9911                             gfloat       *y)
9912 {
9913   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9914 
9915   if (x)
9916     *x = clutter_actor_get_x (self);
9917 
9918   if (y)
9919     *y = clutter_actor_get_y (self);
9920 }
9921 
9922 /**
9923  * clutter_actor_get_fixed_position:
9924  * @self: a #ClutterActor
9925  * @x: (out) (allow-none): return location for the X coordinate, or %NULL
9926  * @y: (out) (allow-none): return location for the Y coordinate, or %NULL
9927  *
9928  * This function gets the fixed position of the actor, if set. If there
9929  * is no fixed position set, this function returns %FALSE and doesn't set
9930  * the x and y coordinates.
9931  *
9932  * Returns: %TRUE if the fixed position is set, %FALSE if it isn't
9933  */
9934 gboolean
clutter_actor_get_fixed_position(ClutterActor * self,float * x,float * y)9935 clutter_actor_get_fixed_position (ClutterActor *self,
9936                                   float        *x,
9937                                   float        *y)
9938 {
9939   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
9940 
9941   if (self->priv->position_set)
9942     {
9943       const ClutterLayoutInfo *info;
9944 
9945       info = _clutter_actor_get_layout_info_or_defaults (self);
9946 
9947       if (x)
9948         *x = info->fixed_pos.x;
9949 
9950       if (y)
9951         *y = info->fixed_pos.y;
9952 
9953       return TRUE;
9954     }
9955 
9956   return FALSE;
9957 }
9958 
9959 /**
9960  * clutter_actor_get_transformed_extents:
9961  * @self: A #ClutterActor
9962  * @rect: (out): return location for the transformed bounding rect
9963  *
9964  * Gets the transformed bounding rect of an actor, in pixels relative to the stage.
9965  */
9966 void
clutter_actor_get_transformed_extents(ClutterActor * self,graphene_rect_t * rect)9967 clutter_actor_get_transformed_extents (ClutterActor    *self,
9968                                        graphene_rect_t *rect)
9969 {
9970   graphene_quad_t quad;
9971   graphene_point3d_t v[4];
9972   ClutterActorBox box;
9973 
9974   box.x1 = 0;
9975   box.y1 = 0;
9976   box.x2 = clutter_actor_box_get_width (&self->priv->allocation);
9977   box.y2 = clutter_actor_box_get_height (&self->priv->allocation);
9978   if (_clutter_actor_transform_and_project_box (self, &box, v))
9979     {
9980       graphene_quad_init (&quad,
9981                           (graphene_point_t *) &v[0],
9982                           (graphene_point_t *) &v[1],
9983                           (graphene_point_t *) &v[2],
9984                           (graphene_point_t *) &v[3]);
9985 
9986       if (rect)
9987         graphene_quad_bounds (&quad, rect);
9988     }
9989 }
9990 
9991 /**
9992  * clutter_actor_get_transformed_position:
9993  * @self: A #ClutterActor
9994  * @x: (out) (allow-none): return location for the X coordinate, or %NULL
9995  * @y: (out) (allow-none): return location for the Y coordinate, or %NULL
9996  *
9997  * Gets the absolute position of an actor, in pixels relative to the stage.
9998  *
9999  * Since: 0.8
10000  */
10001 void
clutter_actor_get_transformed_position(ClutterActor * self,gfloat * x,gfloat * y)10002 clutter_actor_get_transformed_position (ClutterActor *self,
10003                                         gfloat       *x,
10004                                         gfloat       *y)
10005 {
10006   graphene_point3d_t v1;
10007   graphene_point3d_t v2;
10008 
10009   v1.x = v1.y = v1.z = 0;
10010   clutter_actor_apply_transform_to_point (self, &v1, &v2);
10011 
10012   if (x)
10013     *x = v2.x;
10014 
10015   if (y)
10016     *y = v2.y;
10017 }
10018 
10019 /**
10020  * clutter_actor_get_transformed_size:
10021  * @self: A #ClutterActor
10022  * @width: (out) (allow-none): return location for the width, or %NULL
10023  * @height: (out) (allow-none): return location for the height, or %NULL
10024  *
10025  * Gets the absolute size of an actor in pixels, taking into account the
10026  * scaling factors.
10027  *
10028  * If the actor has a valid allocation, the allocated size will be used.
10029  * If the actor has not a valid allocation then the preferred size will
10030  * be transformed and returned.
10031  *
10032  * If you want the transformed allocation, see
10033  * clutter_actor_get_abs_allocation_vertices() instead.
10034  *
10035  * When the actor (or one of its ancestors) is rotated around the
10036  * X or Y axis, it no longer appears as on the stage as a rectangle, but
10037  * as a generic quadrangle; in that case this function returns the size
10038  * of the smallest rectangle that encapsulates the entire quad. Please
10039  * note that in this case no assumptions can be made about the relative
10040  * position of this envelope to the absolute position of the actor, as
10041  * returned by clutter_actor_get_transformed_position(); if you need this
10042  * information, you need to use clutter_actor_get_abs_allocation_vertices()
10043  * to get the coords of the actual quadrangle.
10044  *
10045  * Since: 0.8
10046  */
10047 void
clutter_actor_get_transformed_size(ClutterActor * self,gfloat * width,gfloat * height)10048 clutter_actor_get_transformed_size (ClutterActor *self,
10049                                     gfloat       *width,
10050                                     gfloat       *height)
10051 {
10052   ClutterActorPrivate *priv;
10053   graphene_point3d_t v[4];
10054   gfloat x_min, x_max, y_min, y_max;
10055   gint i;
10056 
10057   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10058 
10059   priv = self->priv;
10060 
10061   /* if the actor hasn't been allocated yet, get the preferred
10062    * size and transform that
10063    */
10064   if (priv->needs_allocation)
10065     {
10066       gfloat natural_width, natural_height;
10067       ClutterActorBox box;
10068 
10069       /* Make a fake allocation to transform.
10070        *
10071        * NB: _clutter_actor_transform_and_project_box expects a box in
10072        * the actor's coordinate space... */
10073 
10074       box.x1 = 0;
10075       box.y1 = 0;
10076 
10077       natural_width = natural_height = 0;
10078       clutter_actor_get_preferred_size (self, NULL, NULL,
10079                                         &natural_width,
10080                                         &natural_height);
10081 
10082       box.x2 = natural_width;
10083       box.y2 = natural_height;
10084 
10085       _clutter_actor_transform_and_project_box (self, &box, v);
10086     }
10087   else
10088     clutter_actor_get_abs_allocation_vertices (self, v);
10089 
10090   x_min = x_max = v[0].x;
10091   y_min = y_max = v[0].y;
10092 
10093   for (i = 1; i < G_N_ELEMENTS (v); ++i)
10094     {
10095       if (v[i].x < x_min)
10096 	x_min = v[i].x;
10097 
10098       if (v[i].x > x_max)
10099 	x_max = v[i].x;
10100 
10101       if (v[i].y < y_min)
10102 	y_min = v[i].y;
10103 
10104       if (v[i].y > y_max)
10105 	y_max = v[i].y;
10106     }
10107 
10108   if (width)
10109     *width  = x_max - x_min;
10110 
10111   if (height)
10112     *height = y_max - y_min;
10113 }
10114 
10115 /**
10116  * clutter_actor_get_width:
10117  * @self: A #ClutterActor
10118  *
10119  * Retrieves the width of a #ClutterActor.
10120  *
10121  * If the actor has a valid allocation, this function will return the
10122  * width of the allocated area given to the actor.
10123  *
10124  * If the actor does not have a valid allocation, this function will
10125  * return the actor's natural width, that is the preferred width of
10126  * the actor.
10127  *
10128  * If you care whether you get the preferred width or the width that
10129  * has been assigned to the actor, you should probably call a different
10130  * function like clutter_actor_get_allocation_box() to retrieve the
10131  * allocated size or clutter_actor_get_preferred_width() to retrieve the
10132  * preferred width.
10133  *
10134  * If an actor has a fixed width, for instance a width that has been
10135  * assigned using clutter_actor_set_width(), the width returned will
10136  * be the same value.
10137  *
10138  * Return value: the width of the actor, in pixels
10139  */
10140 gfloat
clutter_actor_get_width(ClutterActor * self)10141 clutter_actor_get_width (ClutterActor *self)
10142 {
10143   ClutterActorPrivate *priv;
10144 
10145   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10146 
10147   priv = self->priv;
10148 
10149   if (priv->needs_allocation)
10150     {
10151       gfloat natural_width = 0;
10152 
10153       if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
10154         {
10155           clutter_actor_get_preferred_width (self, -1, NULL, &natural_width);
10156         }
10157       else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
10158         {
10159           gfloat natural_height = 0;
10160 
10161           clutter_actor_get_preferred_height (self, -1, NULL, &natural_height);
10162           clutter_actor_get_preferred_width (self, natural_height,
10163                                              NULL,
10164                                              &natural_width);
10165         }
10166       else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL)
10167         {
10168           clutter_content_get_preferred_size (priv->content, &natural_width, NULL);
10169         }
10170 
10171       return natural_width;
10172     }
10173   else
10174     return priv->allocation.x2 - priv->allocation.x1;
10175 }
10176 
10177 /**
10178  * clutter_actor_get_height:
10179  * @self: A #ClutterActor
10180  *
10181  * Retrieves the height of a #ClutterActor.
10182  *
10183  * If the actor has a valid allocation, this function will return the
10184  * height of the allocated area given to the actor.
10185  *
10186  * If the actor does not have a valid allocation, this function will
10187  * return the actor's natural height, that is the preferred height of
10188  * the actor.
10189  *
10190  * If you care whether you get the preferred height or the height that
10191  * has been assigned to the actor, you should probably call a different
10192  * function like clutter_actor_get_allocation_box() to retrieve the
10193  * allocated size or clutter_actor_get_preferred_height() to retrieve the
10194  * preferred height.
10195  *
10196  * If an actor has a fixed height, for instance a height that has been
10197  * assigned using clutter_actor_set_height(), the height returned will
10198  * be the same value.
10199  *
10200  * Return value: the height of the actor, in pixels
10201  */
10202 gfloat
clutter_actor_get_height(ClutterActor * self)10203 clutter_actor_get_height (ClutterActor *self)
10204 {
10205   ClutterActorPrivate *priv;
10206 
10207   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10208 
10209   priv = self->priv;
10210 
10211   if (priv->needs_allocation)
10212     {
10213       gfloat natural_height = 0;
10214 
10215       if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
10216         {
10217           gfloat natural_width = 0;
10218 
10219           clutter_actor_get_preferred_width (self, -1, NULL, &natural_width);
10220           clutter_actor_get_preferred_height (self, natural_width,
10221                                               NULL, &natural_height);
10222         }
10223       else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
10224         {
10225           clutter_actor_get_preferred_height (self, -1, NULL, &natural_height);
10226         }
10227       else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL)
10228         {
10229           clutter_content_get_preferred_size (priv->content, NULL, &natural_height);
10230         }
10231 
10232       return natural_height;
10233     }
10234   else
10235     return priv->allocation.y2 - priv->allocation.y1;
10236 }
10237 
10238 /**
10239  * clutter_actor_set_width:
10240  * @self: A #ClutterActor
10241  * @width: Requested new width for the actor, in pixels, or -1
10242  *
10243  * Forces a width on an actor, causing the actor's preferred width
10244  * and height (if any) to be ignored.
10245  *
10246  * If @width is -1 the actor will use its preferred width request
10247  * instead of overriding it, i.e. you can "unset" the width with -1.
10248  *
10249  * This function sets both the minimum and natural size of the actor.
10250  *
10251  * since: 0.2
10252  */
10253 void
clutter_actor_set_width(ClutterActor * self,gfloat width)10254 clutter_actor_set_width (ClutterActor *self,
10255                          gfloat        width)
10256 {
10257   float cur_size;
10258 
10259   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10260 
10261   /* minor optimization: if we don't have a duration
10262    * then we can skip the get_width() below, to avoid
10263    * the chance of going through get_preferred_width()
10264    * just to jump to a new desired width.
10265    */
10266   if (clutter_actor_get_easing_duration (self) == 0)
10267     {
10268       g_object_freeze_notify (G_OBJECT (self));
10269 
10270       clutter_actor_set_width_internal (self, width);
10271 
10272       g_object_thaw_notify (G_OBJECT (self));
10273 
10274       return;
10275     }
10276   else
10277     cur_size = clutter_actor_get_width (self);
10278 
10279   _clutter_actor_create_transition (self,
10280                                     obj_props[PROP_WIDTH],
10281                                     cur_size,
10282                                     width);
10283 }
10284 
10285 /**
10286  * clutter_actor_set_height:
10287  * @self: A #ClutterActor
10288  * @height: Requested new height for the actor, in pixels, or -1
10289  *
10290  * Forces a height on an actor, causing the actor's preferred width
10291  * and height (if any) to be ignored.
10292  *
10293  * If @height is -1 the actor will use its preferred height instead of
10294  * overriding it, i.e. you can "unset" the height with -1.
10295  *
10296  * This function sets both the minimum and natural size of the actor.
10297  *
10298  * since: 0.2
10299  */
10300 void
clutter_actor_set_height(ClutterActor * self,gfloat height)10301 clutter_actor_set_height (ClutterActor *self,
10302                           gfloat        height)
10303 {
10304   float cur_size;
10305 
10306   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10307 
10308   /* see the comment in clutter_actor_set_width() above */
10309   if (clutter_actor_get_easing_duration (self) == 0)
10310     {
10311       g_object_freeze_notify (G_OBJECT (self));
10312 
10313       clutter_actor_set_height_internal (self, height);
10314 
10315       g_object_thaw_notify (G_OBJECT (self));
10316 
10317       return;
10318     }
10319   else
10320     cur_size = clutter_actor_get_height (self);
10321 
10322   _clutter_actor_create_transition (self,
10323                                     obj_props[PROP_HEIGHT],
10324                                     cur_size,
10325                                     height);
10326 }
10327 
10328 static inline void
clutter_actor_set_x_internal(ClutterActor * self,float x)10329 clutter_actor_set_x_internal (ClutterActor *self,
10330                               float         x)
10331 {
10332   ClutterActorPrivate *priv = self->priv;
10333   ClutterLayoutInfo *linfo;
10334   ClutterActorBox old = { 0, };
10335 
10336   linfo = _clutter_actor_get_layout_info (self);
10337 
10338   if (priv->position_set && linfo->fixed_pos.x == x)
10339     return;
10340 
10341   clutter_actor_store_old_geometry (self, &old);
10342 
10343   linfo->fixed_pos.x = x;
10344   clutter_actor_set_fixed_position_set (self, TRUE);
10345 
10346   clutter_actor_notify_if_geometry_changed (self, &old);
10347 
10348   clutter_actor_queue_relayout (self);
10349 }
10350 
10351 static inline void
clutter_actor_set_y_internal(ClutterActor * self,float y)10352 clutter_actor_set_y_internal (ClutterActor *self,
10353                               float         y)
10354 {
10355   ClutterActorPrivate *priv = self->priv;
10356   ClutterLayoutInfo *linfo;
10357   ClutterActorBox old = { 0, };
10358 
10359   linfo = _clutter_actor_get_layout_info (self);
10360 
10361   if (priv->position_set && linfo->fixed_pos.y == y)
10362     return;
10363 
10364   clutter_actor_store_old_geometry (self, &old);
10365 
10366   linfo->fixed_pos.y = y;
10367   clutter_actor_set_fixed_position_set (self, TRUE);
10368 
10369   clutter_actor_notify_if_geometry_changed (self, &old);
10370 
10371   clutter_actor_queue_relayout (self);
10372 }
10373 
10374 static void
clutter_actor_set_position_internal(ClutterActor * self,const graphene_point_t * position)10375 clutter_actor_set_position_internal (ClutterActor           *self,
10376                                      const graphene_point_t *position)
10377 {
10378   ClutterActorPrivate *priv = self->priv;
10379   ClutterLayoutInfo *linfo;
10380   ClutterActorBox old = { 0, };
10381 
10382   linfo = _clutter_actor_get_layout_info (self);
10383 
10384   if (priv->position_set &&
10385       graphene_point_equal (position, &linfo->fixed_pos))
10386     return;
10387 
10388   clutter_actor_store_old_geometry (self, &old);
10389 
10390   if (position != NULL)
10391     {
10392       linfo->fixed_pos = *position;
10393       clutter_actor_set_fixed_position_set (self, TRUE);
10394     }
10395   else
10396     clutter_actor_set_fixed_position_set (self, FALSE);
10397 
10398   clutter_actor_notify_if_geometry_changed (self, &old);
10399 
10400   clutter_actor_queue_relayout (self);
10401 }
10402 
10403 /**
10404  * clutter_actor_set_x:
10405  * @self: a #ClutterActor
10406  * @x: the actor's position on the X axis
10407  *
10408  * Sets the actor's X coordinate, relative to its parent, in pixels.
10409  *
10410  * Overrides any layout manager and forces a fixed position for
10411  * the actor.
10412  *
10413  * The #ClutterActor:x property is animatable.
10414  *
10415  * Since: 0.6
10416  */
10417 void
clutter_actor_set_x(ClutterActor * self,gfloat x)10418 clutter_actor_set_x (ClutterActor *self,
10419                      gfloat        x)
10420 {
10421   float cur_position = clutter_actor_get_x (self);
10422 
10423   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10424 
10425   _clutter_actor_create_transition (self, obj_props[PROP_X],
10426                                     cur_position,
10427                                     x);
10428 }
10429 
10430 /**
10431  * clutter_actor_set_y:
10432  * @self: a #ClutterActor
10433  * @y: the actor's position on the Y axis
10434  *
10435  * Sets the actor's Y coordinate, relative to its parent, in pixels.#
10436  *
10437  * Overrides any layout manager and forces a fixed position for
10438  * the actor.
10439  *
10440  * The #ClutterActor:y property is animatable.
10441  *
10442  * Since: 0.6
10443  */
10444 void
clutter_actor_set_y(ClutterActor * self,gfloat y)10445 clutter_actor_set_y (ClutterActor *self,
10446                      gfloat        y)
10447 {
10448   float cur_position = clutter_actor_get_y (self);
10449 
10450   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10451 
10452   _clutter_actor_create_transition (self, obj_props[PROP_Y],
10453                                     cur_position,
10454                                     y);
10455 }
10456 
10457 /**
10458  * clutter_actor_get_x:
10459  * @self: A #ClutterActor
10460  *
10461  * Retrieves the X coordinate of a #ClutterActor.
10462  *
10463  * This function tries to "do what you mean", by returning the
10464  * correct value depending on the actor's state.
10465  *
10466  * If the actor has a valid allocation, this function will return
10467  * the X coordinate of the origin of the allocation box.
10468  *
10469  * If the actor has any fixed coordinate set using clutter_actor_set_x(),
10470  * clutter_actor_set_position(), this function will return that coordinate.
10471  *
10472  * If both the allocation and a fixed position are missing, this function
10473  * will return 0.
10474  *
10475  * Return value: the X coordinate, in pixels, ignoring any
10476  *   transformation (i.e. scaling, rotation)
10477  */
10478 gfloat
clutter_actor_get_x(ClutterActor * self)10479 clutter_actor_get_x (ClutterActor *self)
10480 {
10481   ClutterActorPrivate *priv;
10482 
10483   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10484 
10485   priv = self->priv;
10486 
10487   if (priv->needs_allocation)
10488     {
10489       if (priv->position_set)
10490         {
10491           const ClutterLayoutInfo *info;
10492 
10493           info = _clutter_actor_get_layout_info_or_defaults (self);
10494 
10495           return info->fixed_pos.x;
10496         }
10497       else
10498         return 0;
10499     }
10500   else
10501     return priv->allocation.x1;
10502 }
10503 
10504 /**
10505  * clutter_actor_get_y:
10506  * @self: A #ClutterActor
10507  *
10508  * Retrieves the Y coordinate of a #ClutterActor.
10509  *
10510  * This function tries to "do what you mean", by returning the
10511  * correct value depending on the actor's state.
10512  *
10513  * If the actor has a valid allocation, this function will return
10514  * the Y coordinate of the origin of the allocation box.
10515  *
10516  * If the actor has any fixed coordinate set using clutter_actor_set_y(),
10517  * clutter_actor_set_position(), this function will return that coordinate.
10518  *
10519  * If both the allocation and a fixed position are missing, this function
10520  * will return 0.
10521  *
10522  * Return value: the Y coordinate, in pixels, ignoring any
10523  *   transformation (i.e. scaling, rotation)
10524  */
10525 gfloat
clutter_actor_get_y(ClutterActor * self)10526 clutter_actor_get_y (ClutterActor *self)
10527 {
10528   ClutterActorPrivate *priv;
10529 
10530   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10531 
10532   priv = self->priv;
10533 
10534   if (priv->needs_allocation)
10535     {
10536       if (priv->position_set)
10537         {
10538           const ClutterLayoutInfo *info;
10539 
10540           info = _clutter_actor_get_layout_info_or_defaults (self);
10541 
10542           return info->fixed_pos.y;
10543         }
10544       else
10545         return 0;
10546     }
10547   else
10548     return priv->allocation.y1;
10549 }
10550 
10551 /**
10552  * clutter_actor_set_scale:
10553  * @self: A #ClutterActor
10554  * @scale_x: double factor to scale actor by horizontally.
10555  * @scale_y: double factor to scale actor by vertically.
10556  *
10557  * Scales an actor with the given factors.
10558  *
10559  * The scale transformation is relative the the #ClutterActor:pivot-point.
10560  *
10561  * The #ClutterActor:scale-x and #ClutterActor:scale-y properties are
10562  * animatable.
10563  *
10564  * Since: 0.2
10565  */
10566 void
clutter_actor_set_scale(ClutterActor * self,gdouble scale_x,gdouble scale_y)10567 clutter_actor_set_scale (ClutterActor *self,
10568                          gdouble       scale_x,
10569                          gdouble       scale_y)
10570 {
10571   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10572 
10573   g_object_freeze_notify (G_OBJECT (self));
10574 
10575   clutter_actor_set_scale_factor (self, CLUTTER_X_AXIS, scale_x);
10576   clutter_actor_set_scale_factor (self, CLUTTER_Y_AXIS, scale_y);
10577 
10578   g_object_thaw_notify (G_OBJECT (self));
10579 }
10580 
10581 /**
10582  * clutter_actor_set_scale_z:
10583  * @self: a #ClutterActor
10584  * @scale_z: the scaling factor along the Z axis
10585  *
10586  * Scales an actor on the Z axis by the given @scale_z factor.
10587  *
10588  * The scale transformation is relative the the #ClutterActor:pivot-point.
10589  *
10590  * The #ClutterActor:scale-z property is animatable.
10591  *
10592  * Since: 1.12
10593  */
10594 void
clutter_actor_set_scale_z(ClutterActor * self,gdouble scale_z)10595 clutter_actor_set_scale_z (ClutterActor *self,
10596                            gdouble       scale_z)
10597 {
10598   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10599 
10600   clutter_actor_set_scale_factor (self, CLUTTER_Z_AXIS, scale_z);
10601 }
10602 
10603 /**
10604  * clutter_actor_get_scale:
10605  * @self: A #ClutterActor
10606  * @scale_x: (out) (allow-none): Location to store horizontal
10607  *   scale factor, or %NULL.
10608  * @scale_y: (out) (allow-none): Location to store vertical
10609  *   scale factor, or %NULL.
10610  *
10611  * Retrieves an actors scale factors.
10612  *
10613  * Since: 0.2
10614  */
10615 void
clutter_actor_get_scale(ClutterActor * self,gdouble * scale_x,gdouble * scale_y)10616 clutter_actor_get_scale (ClutterActor *self,
10617 			 gdouble      *scale_x,
10618 			 gdouble      *scale_y)
10619 {
10620   const ClutterTransformInfo *info;
10621 
10622   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10623 
10624   info = _clutter_actor_get_transform_info_or_defaults (self);
10625 
10626   if (scale_x)
10627     *scale_x = info->scale_x;
10628 
10629   if (scale_y)
10630     *scale_y = info->scale_y;
10631 }
10632 
10633 /**
10634  * clutter_actor_get_scale_z:
10635  * @self: A #ClutterActor
10636  *
10637  * Retrieves the scaling factor along the Z axis, as set using
10638  * clutter_actor_set_scale_z().
10639  *
10640  * Return value: the scaling factor along the Z axis
10641  *
10642  * Since: 1.12
10643  */
10644 gdouble
clutter_actor_get_scale_z(ClutterActor * self)10645 clutter_actor_get_scale_z (ClutterActor *self)
10646 {
10647   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 1.0);
10648 
10649   return _clutter_actor_get_transform_info_or_defaults (self)->scale_z;
10650 }
10651 
10652 static inline void
clutter_actor_set_opacity_internal(ClutterActor * self,guint8 opacity)10653 clutter_actor_set_opacity_internal (ClutterActor *self,
10654                                     guint8        opacity)
10655 {
10656   ClutterActorPrivate *priv = self->priv;
10657 
10658   if (priv->opacity != opacity)
10659     {
10660       priv->opacity = opacity;
10661 
10662       /* Queue a redraw from the flatten effect so that it can use
10663          its cached image if available instead of having to redraw the
10664          actual actor. If it doesn't end up using the FBO then the
10665          effect is still able to continue the paint anyway. If there
10666          is no flatten effect yet then this is equivalent to queueing
10667          a full redraw */
10668       _clutter_actor_queue_redraw_full (self,
10669                                         NULL, /* clip */
10670                                         priv->flatten_effect);
10671 
10672       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_OPACITY]);
10673     }
10674 }
10675 
10676 /**
10677  * clutter_actor_set_opacity:
10678  * @self: A #ClutterActor
10679  * @opacity: New opacity value for the actor.
10680  *
10681  * Sets the actor's opacity, with zero being completely transparent and
10682  * 255 (0xff) being fully opaque.
10683  *
10684  * The #ClutterActor:opacity property is animatable.
10685  */
10686 void
clutter_actor_set_opacity(ClutterActor * self,guint8 opacity)10687 clutter_actor_set_opacity (ClutterActor *self,
10688 			   guint8        opacity)
10689 {
10690   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10691 
10692   _clutter_actor_create_transition (self, obj_props[PROP_OPACITY],
10693                                     self->priv->opacity,
10694                                     opacity);
10695 }
10696 
10697 /*
10698  * clutter_actor_get_paint_opacity_internal:
10699  * @self: a #ClutterActor
10700  *
10701  * Retrieves the absolute opacity of the actor, as it appears on the stage
10702  *
10703  * This function does not do type checks
10704  *
10705  * Return value: the absolute opacity of the actor
10706  */
10707 static guint8
clutter_actor_get_paint_opacity_internal(ClutterActor * self)10708 clutter_actor_get_paint_opacity_internal (ClutterActor *self)
10709 {
10710   ClutterActorPrivate *priv = self->priv;
10711   ClutterActor *parent;
10712 
10713   /* override the top-level opacity to always be 255; even in
10714    * case of ClutterStage:use-alpha being TRUE we want the rest
10715    * of the scene to be painted
10716    */
10717   if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
10718     return 255;
10719 
10720   if (priv->opacity_override >= 0)
10721     return priv->opacity_override;
10722 
10723   parent = priv->parent;
10724 
10725   /* Factor in the actual actors opacity with parents */
10726   if (parent != NULL)
10727     {
10728       guint8 opacity = clutter_actor_get_paint_opacity_internal (parent);
10729 
10730       if (opacity != 0xff)
10731         return (opacity * priv->opacity) / 0xff;
10732     }
10733 
10734   return priv->opacity;
10735 
10736 }
10737 
10738 /**
10739  * clutter_actor_get_paint_opacity:
10740  * @self: A #ClutterActor
10741  *
10742  * Retrieves the absolute opacity of the actor, as it appears on the stage.
10743  *
10744  * This function traverses the hierarchy chain and composites the opacity of
10745  * the actor with that of its parents.
10746  *
10747  * This function is intended for subclasses to use in the paint virtual
10748  * function, to paint themselves with the correct opacity.
10749  *
10750  * Return value: The actor opacity value.
10751  *
10752  * Since: 0.8
10753  */
10754 guint8
clutter_actor_get_paint_opacity(ClutterActor * self)10755 clutter_actor_get_paint_opacity (ClutterActor *self)
10756 {
10757   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10758 
10759   return clutter_actor_get_paint_opacity_internal (self);
10760 }
10761 
10762 /**
10763  * clutter_actor_get_opacity:
10764  * @self: a #ClutterActor
10765  *
10766  * Retrieves the opacity value of an actor, as set by
10767  * clutter_actor_set_opacity().
10768  *
10769  * For retrieving the absolute opacity of the actor inside a paint
10770  * virtual function, see clutter_actor_get_paint_opacity().
10771  *
10772  * Return value: the opacity of the actor
10773  */
10774 guint8
clutter_actor_get_opacity(ClutterActor * self)10775 clutter_actor_get_opacity (ClutterActor *self)
10776 {
10777   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10778 
10779   return self->priv->opacity;
10780 }
10781 
10782 /**
10783  * clutter_actor_set_offscreen_redirect:
10784  * @self: A #ClutterActor
10785  * @redirect: New offscreen redirect flags for the actor.
10786  *
10787  * Defines the circumstances where the actor should be redirected into
10788  * an offscreen image. The offscreen image is used to flatten the
10789  * actor into a single image while painting for two main reasons.
10790  * Firstly, when the actor is painted a second time without any of its
10791  * contents changing it can simply repaint the cached image without
10792  * descending further down the actor hierarchy. Secondly, it will make
10793  * the opacity look correct even if there are overlapping primitives
10794  * in the actor.
10795  *
10796  * Caching the actor could in some cases be a performance win and in
10797  * some cases be a performance lose so it is important to determine
10798  * which value is right for an actor before modifying this value. For
10799  * example, there is never any reason to flatten an actor that is just
10800  * a single texture (such as a #ClutterTexture) because it is
10801  * effectively already cached in an image so the offscreen would be
10802  * redundant. Also if the actor contains primitives that are far apart
10803  * with a large transparent area in the middle (such as a large
10804  * CluterGroup with a small actor in the top left and a small actor in
10805  * the bottom right) then the cached image will contain the entire
10806  * image of the large area and the paint will waste time blending all
10807  * of the transparent pixels in the middle.
10808  *
10809  * The default method of implementing opacity on a container simply
10810  * forwards on the opacity to all of the children. If the children are
10811  * overlapping then it will appear as if they are two separate glassy
10812  * objects and there will be a break in the color where they
10813  * overlap. By redirecting to an offscreen buffer it will be as if the
10814  * two opaque objects are combined into one and then made transparent
10815  * which is usually what is expected.
10816  *
10817  * The image below demonstrates the difference between redirecting and
10818  * not. The image shows two Clutter groups, each containing a red and
10819  * a green rectangle which overlap. The opacity on the group is set to
10820  * 128 (which is 50%). When the offscreen redirect is not used, the
10821  * red rectangle can be seen through the blue rectangle as if the two
10822  * rectangles were separately transparent. When the redirect is used
10823  * the group as a whole is transparent instead so the red rectangle is
10824  * not visible where they overlap.
10825  *
10826  * <figure id="offscreen-redirect">
10827  *   <title>Sample of using an offscreen redirect for transparency</title>
10828  *   <graphic fileref="offscreen-redirect.png" format="PNG"/>
10829  * </figure>
10830  *
10831  * The default value for this property is 0, so we effectively will
10832  * never redirect an actor offscreen by default. This means that there
10833  * are times that transparent actors may look glassy as described
10834  * above. The reason this is the default is because there is a
10835  * performance trade off between quality and performance here. In many
10836  * cases the default form of glassy opacity looks good enough, but if
10837  * it's not you will need to set the
10838  * %CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY flag to enable
10839  * redirection for opacity.
10840  *
10841  * Custom actors that don't contain any overlapping primitives are
10842  * recommended to override the has_overlaps() virtual to return %FALSE
10843  * for maximum efficiency.
10844  *
10845  * Since: 1.8
10846  */
10847 void
clutter_actor_set_offscreen_redirect(ClutterActor * self,ClutterOffscreenRedirect redirect)10848 clutter_actor_set_offscreen_redirect (ClutterActor *self,
10849                                       ClutterOffscreenRedirect redirect)
10850 {
10851   ClutterActorPrivate *priv;
10852 
10853   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10854 
10855   priv = self->priv;
10856 
10857   if (priv->offscreen_redirect != redirect)
10858     {
10859       priv->offscreen_redirect = redirect;
10860 
10861       /* Queue a redraw from the effect so that it can use its cached
10862          image if available instead of having to redraw the actual
10863          actor. If it doesn't end up using the FBO then the effect is
10864          still able to continue the paint anyway. If there is no
10865          effect then this is equivalent to queuing a full redraw */
10866       _clutter_actor_queue_redraw_full (self,
10867                                         NULL, /* clip */
10868                                         priv->flatten_effect);
10869 
10870       g_object_notify_by_pspec (G_OBJECT (self),
10871                                 obj_props[PROP_OFFSCREEN_REDIRECT]);
10872     }
10873 }
10874 
10875 /**
10876  * clutter_actor_get_offscreen_redirect:
10877  * @self: a #ClutterActor
10878  *
10879  * Retrieves whether to redirect the actor to an offscreen buffer, as
10880  * set by clutter_actor_set_offscreen_redirect().
10881  *
10882  * Return value: the value of the offscreen-redirect property of the actor
10883  *
10884  * Since: 1.8
10885  */
10886 ClutterOffscreenRedirect
clutter_actor_get_offscreen_redirect(ClutterActor * self)10887 clutter_actor_get_offscreen_redirect (ClutterActor *self)
10888 {
10889   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10890 
10891   return self->priv->offscreen_redirect;
10892 }
10893 
10894 /**
10895  * clutter_actor_set_name:
10896  * @self: A #ClutterActor
10897  * @name: Textual tag to apply to actor
10898  *
10899  * Sets the given name to @self. The name can be used to identify
10900  * a #ClutterActor.
10901  */
10902 void
clutter_actor_set_name(ClutterActor * self,const gchar * name)10903 clutter_actor_set_name (ClutterActor *self,
10904 			const gchar  *name)
10905 {
10906   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10907 
10908   g_free (self->priv->name);
10909   self->priv->name = g_strdup (name);
10910 
10911   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NAME]);
10912 }
10913 
10914 /**
10915  * clutter_actor_get_name:
10916  * @self: A #ClutterActor
10917  *
10918  * Retrieves the name of @self.
10919  *
10920  * Return value: the name of the actor, or %NULL. The returned string is
10921  *   owned by the actor and should not be modified or freed.
10922  */
10923 const gchar *
clutter_actor_get_name(ClutterActor * self)10924 clutter_actor_get_name (ClutterActor *self)
10925 {
10926   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
10927 
10928   return self->priv->name;
10929 }
10930 
10931 static inline void
clutter_actor_set_z_position_internal(ClutterActor * self,float z_position)10932 clutter_actor_set_z_position_internal (ClutterActor *self,
10933                                        float         z_position)
10934 {
10935   ClutterTransformInfo *info;
10936 
10937   info = _clutter_actor_get_transform_info (self);
10938 
10939   if (memcmp (&info->z_position, &z_position, sizeof (float)) != 0)
10940     {
10941       info->z_position = z_position;
10942 
10943       transform_changed (self);
10944 
10945       clutter_actor_queue_redraw (self);
10946 
10947       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_Z_POSITION]);
10948     }
10949 }
10950 
10951 /**
10952  * clutter_actor_set_z_position:
10953  * @self: a #ClutterActor
10954  * @z_position: the position on the Z axis
10955  *
10956  * Sets the actor's position on the Z axis.
10957  *
10958  * See #ClutterActor:z-position.
10959  *
10960  * Since: 1.12
10961  */
10962 void
clutter_actor_set_z_position(ClutterActor * self,gfloat z_position)10963 clutter_actor_set_z_position (ClutterActor *self,
10964                               gfloat        z_position)
10965 {
10966   const ClutterTransformInfo *info;
10967 
10968   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10969 
10970   info = _clutter_actor_get_transform_info_or_defaults (self);
10971 
10972   _clutter_actor_create_transition (self, obj_props[PROP_Z_POSITION],
10973                                     info->z_position,
10974                                     z_position);
10975 }
10976 
10977 /**
10978  * clutter_actor_get_z_position:
10979  * @self: a #ClutterActor
10980  *
10981  * Retrieves the actor's position on the Z axis.
10982  *
10983  * Return value: the position on the Z axis.
10984  *
10985  * Since: 1.12
10986  */
10987 gfloat
clutter_actor_get_z_position(ClutterActor * self)10988 clutter_actor_get_z_position (ClutterActor *self)
10989 {
10990   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
10991 
10992   return _clutter_actor_get_transform_info_or_defaults (self)->z_position;
10993 }
10994 
10995 /**
10996  * clutter_actor_set_pivot_point:
10997  * @self: a #ClutterActor
10998  * @pivot_x: the normalized X coordinate of the pivot point
10999  * @pivot_y: the normalized Y coordinate of the pivot point
11000  *
11001  * Sets the position of the #ClutterActor:pivot-point around which the
11002  * scaling and rotation transformations occur.
11003  *
11004  * The pivot point's coordinates are in normalized space, with the (0, 0)
11005  * point being the top left corner of the actor, and the (1, 1) point being
11006  * the bottom right corner.
11007  *
11008  * Since: 1.12
11009  */
11010 void
clutter_actor_set_pivot_point(ClutterActor * self,gfloat pivot_x,gfloat pivot_y)11011 clutter_actor_set_pivot_point (ClutterActor *self,
11012                                gfloat        pivot_x,
11013                                gfloat        pivot_y)
11014 {
11015   graphene_point_t pivot = GRAPHENE_POINT_INIT (pivot_x, pivot_y);
11016   const ClutterTransformInfo *info;
11017 
11018   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11019 
11020   info = _clutter_actor_get_transform_info_or_defaults (self);
11021   _clutter_actor_create_transition (self, obj_props[PROP_PIVOT_POINT],
11022                                     &info->pivot,
11023                                     &pivot);
11024 }
11025 
11026 /**
11027  * clutter_actor_get_pivot_point:
11028  * @self: a #ClutterActor
11029  * @pivot_x: (out) (allow-none): return location for the normalized X
11030  *   coordinate of the pivot point, or %NULL
11031  * @pivot_y: (out) (allow-none): return location for the normalized Y
11032  *   coordinate of the pivot point, or %NULL
11033  *
11034  * Retrieves the coordinates of the #ClutterActor:pivot-point.
11035  *
11036  * Since: 1.12
11037  */
11038 void
clutter_actor_get_pivot_point(ClutterActor * self,gfloat * pivot_x,gfloat * pivot_y)11039 clutter_actor_get_pivot_point (ClutterActor *self,
11040                                gfloat       *pivot_x,
11041                                gfloat       *pivot_y)
11042 {
11043   const ClutterTransformInfo *info;
11044 
11045   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11046 
11047   info = _clutter_actor_get_transform_info_or_defaults (self);
11048 
11049   if (pivot_x != NULL)
11050     *pivot_x = info->pivot.x;
11051 
11052   if (pivot_y != NULL)
11053     *pivot_y = info->pivot.y;
11054 }
11055 
11056 /**
11057  * clutter_actor_set_pivot_point_z:
11058  * @self: a #ClutterActor
11059  * @pivot_z: the Z coordinate of the actor's pivot point
11060  *
11061  * Sets the component on the Z axis of the #ClutterActor:pivot-point around
11062  * which the scaling and rotation transformations occur.
11063  *
11064  * The @pivot_z value is expressed as a distance along the Z axis.
11065  *
11066  * Since: 1.12
11067  */
11068 void
clutter_actor_set_pivot_point_z(ClutterActor * self,gfloat pivot_z)11069 clutter_actor_set_pivot_point_z (ClutterActor *self,
11070                                  gfloat        pivot_z)
11071 {
11072   const ClutterTransformInfo *info;
11073 
11074   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11075 
11076   info = _clutter_actor_get_transform_info_or_defaults (self);
11077   _clutter_actor_create_transition (self, obj_props[PROP_PIVOT_POINT_Z],
11078                                     info->pivot_z,
11079                                     pivot_z);
11080 }
11081 
11082 /**
11083  * clutter_actor_get_pivot_point_z:
11084  * @self: a #ClutterActor
11085  *
11086  * Retrieves the Z component of the #ClutterActor:pivot-point.
11087  *
11088  * Since: 1.12
11089  */
11090 gfloat
clutter_actor_get_pivot_point_z(ClutterActor * self)11091 clutter_actor_get_pivot_point_z (ClutterActor *self)
11092 {
11093   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
11094 
11095   return _clutter_actor_get_transform_info_or_defaults (self)->pivot_z;
11096 }
11097 
11098 /**
11099  * clutter_actor_set_clip:
11100  * @self: A #ClutterActor
11101  * @xoff: X offset of the clip rectangle
11102  * @yoff: Y offset of the clip rectangle
11103  * @width: Width of the clip rectangle
11104  * @height: Height of the clip rectangle
11105  *
11106  * Sets clip area for @self. The clip area is always computed from the
11107  * upper left corner of the actor.
11108  *
11109  * Since: 0.6
11110  */
11111 void
clutter_actor_set_clip(ClutterActor * self,gfloat xoff,gfloat yoff,gfloat width,gfloat height)11112 clutter_actor_set_clip (ClutterActor *self,
11113                         gfloat        xoff,
11114                         gfloat        yoff,
11115                         gfloat        width,
11116                         gfloat        height)
11117 {
11118   ClutterActorPrivate *priv;
11119   GObject *obj;
11120 
11121   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11122 
11123   priv = self->priv;
11124 
11125   if (priv->has_clip &&
11126       priv->clip.origin.x == xoff &&
11127       priv->clip.origin.y == yoff &&
11128       priv->clip.size.width == width &&
11129       priv->clip.size.height == height)
11130     return;
11131 
11132   obj = G_OBJECT (self);
11133 
11134   priv->clip.origin.x = xoff;
11135   priv->clip.origin.y = yoff;
11136   priv->clip.size.width = width;
11137   priv->clip.size.height = height;
11138 
11139   priv->has_clip = TRUE;
11140 
11141   queue_update_paint_volume (self);
11142   clutter_actor_queue_redraw (self);
11143 
11144   g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]);
11145   g_object_notify_by_pspec (obj, obj_props[PROP_HAS_CLIP]);
11146 }
11147 
11148 /**
11149  * clutter_actor_remove_clip:
11150  * @self: A #ClutterActor
11151  *
11152  * Removes clip area from @self.
11153  */
11154 void
clutter_actor_remove_clip(ClutterActor * self)11155 clutter_actor_remove_clip (ClutterActor *self)
11156 {
11157   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11158 
11159   if (!self->priv->has_clip)
11160     return;
11161 
11162   self->priv->has_clip = FALSE;
11163 
11164   queue_update_paint_volume (self);
11165   clutter_actor_queue_redraw (self);
11166 
11167   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]);
11168 }
11169 
11170 /**
11171  * clutter_actor_has_clip:
11172  * @self: a #ClutterActor
11173  *
11174  * Determines whether the actor has a clip area set or not.
11175  *
11176  * Return value: %TRUE if the actor has a clip area set.
11177  *
11178  * Since: 0.2
11179  */
11180 gboolean
clutter_actor_has_clip(ClutterActor * self)11181 clutter_actor_has_clip (ClutterActor *self)
11182 {
11183   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
11184 
11185   return self->priv->has_clip;
11186 }
11187 
11188 /**
11189  * clutter_actor_get_clip:
11190  * @self: a #ClutterActor
11191  * @xoff: (out) (allow-none): return location for the X offset of
11192  *   the clip rectangle, or %NULL
11193  * @yoff: (out) (allow-none): return location for the Y offset of
11194  *   the clip rectangle, or %NULL
11195  * @width: (out) (allow-none): return location for the width of
11196  *   the clip rectangle, or %NULL
11197  * @height: (out) (allow-none): return location for the height of
11198  *   the clip rectangle, or %NULL
11199  *
11200  * Gets the clip area for @self, if any is set.
11201  *
11202  * Since: 0.6
11203  */
11204 void
clutter_actor_get_clip(ClutterActor * self,gfloat * xoff,gfloat * yoff,gfloat * width,gfloat * height)11205 clutter_actor_get_clip (ClutterActor *self,
11206                         gfloat       *xoff,
11207                         gfloat       *yoff,
11208                         gfloat       *width,
11209                         gfloat       *height)
11210 {
11211   ClutterActorPrivate *priv;
11212 
11213   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11214 
11215   priv = self->priv;
11216 
11217   if (!priv->has_clip)
11218     return;
11219 
11220   if (xoff != NULL)
11221     *xoff = priv->clip.origin.x;
11222 
11223   if (yoff != NULL)
11224     *yoff = priv->clip.origin.y;
11225 
11226   if (width != NULL)
11227     *width = priv->clip.size.width;
11228 
11229   if (height != NULL)
11230     *height = priv->clip.size.height;
11231 }
11232 
11233 /**
11234  * clutter_actor_get_children:
11235  * @self: a #ClutterActor
11236  *
11237  * Retrieves the list of children of @self.
11238  *
11239  * Return value: (transfer container) (element-type ClutterActor): A newly
11240  *   allocated #GList of #ClutterActor<!-- -->s. Use g_list_free() when
11241  *   done.
11242  *
11243  * Since: 1.10
11244  */
11245 GList *
clutter_actor_get_children(ClutterActor * self)11246 clutter_actor_get_children (ClutterActor *self)
11247 {
11248   ClutterActor *iter;
11249   GList *res;
11250 
11251   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
11252 
11253   /* we walk the list backward so that we can use prepend(),
11254    * which is O(1)
11255    */
11256   for (iter = self->priv->last_child, res = NULL;
11257        iter != NULL;
11258        iter = iter->priv->prev_sibling)
11259     {
11260       res = g_list_prepend (res, iter);
11261     }
11262 
11263   return res;
11264 }
11265 
11266 /*< private >
11267  * insert_child_at_depth:
11268  * @self: a #ClutterActor
11269  * @child: a #ClutterActor
11270  *
11271  * Inserts @child inside the list of children held by @self, using
11272  * the depth as the insertion criteria.
11273  *
11274  * This sadly makes the insertion not O(1), but we can keep the
11275  * list sorted so that the painters algorithm we use for painting
11276  * the children will work correctly.
11277  */
11278 static void
insert_child_at_depth(ClutterActor * self,ClutterActor * child,gpointer dummy G_GNUC_UNUSED)11279 insert_child_at_depth (ClutterActor *self,
11280                        ClutterActor *child,
11281                        gpointer      dummy G_GNUC_UNUSED)
11282 {
11283   ClutterActor *iter;
11284   float child_depth;
11285 
11286   child->priv->parent = self;
11287 
11288   child_depth =
11289     _clutter_actor_get_transform_info_or_defaults (child)->z_position;
11290 
11291   /* special-case the first child */
11292   if (self->priv->n_children == 0)
11293     {
11294       self->priv->first_child = child;
11295       self->priv->last_child = child;
11296 
11297       child->priv->next_sibling = NULL;
11298       child->priv->prev_sibling = NULL;
11299 
11300       return;
11301     }
11302 
11303   /* Find the right place to insert the child so that it will still be
11304      sorted and the child will be after all of the actors at the same
11305      dept */
11306   for (iter = self->priv->first_child;
11307        iter != NULL;
11308        iter = iter->priv->next_sibling)
11309     {
11310       float iter_depth;
11311 
11312       iter_depth =
11313         _clutter_actor_get_transform_info_or_defaults (iter)->z_position;
11314 
11315       if (iter_depth > child_depth)
11316         break;
11317     }
11318 
11319   if (iter != NULL)
11320     {
11321       ClutterActor *tmp = iter->priv->prev_sibling;
11322 
11323       if (tmp != NULL)
11324         tmp->priv->next_sibling = child;
11325 
11326       /* Insert the node before the found one */
11327       child->priv->prev_sibling = iter->priv->prev_sibling;
11328       child->priv->next_sibling = iter;
11329       iter->priv->prev_sibling = child;
11330     }
11331   else
11332     {
11333       ClutterActor *tmp = self->priv->last_child;
11334 
11335       if (tmp != NULL)
11336         tmp->priv->next_sibling = child;
11337 
11338       /* insert the node at the end of the list */
11339       child->priv->prev_sibling = self->priv->last_child;
11340       child->priv->next_sibling = NULL;
11341     }
11342 
11343   if (child->priv->prev_sibling == NULL)
11344     self->priv->first_child = child;
11345 
11346   if (child->priv->next_sibling == NULL)
11347     self->priv->last_child = child;
11348 }
11349 
11350 static void
insert_child_at_index(ClutterActor * self,ClutterActor * child,gpointer data_)11351 insert_child_at_index (ClutterActor *self,
11352                        ClutterActor *child,
11353                        gpointer      data_)
11354 {
11355   gint index_ = GPOINTER_TO_INT (data_);
11356 
11357   child->priv->parent = self;
11358 
11359   if (index_ == 0)
11360     {
11361       ClutterActor *tmp = self->priv->first_child;
11362 
11363       if (tmp != NULL)
11364         tmp->priv->prev_sibling = child;
11365 
11366       child->priv->prev_sibling = NULL;
11367       child->priv->next_sibling = tmp;
11368     }
11369   else if (index_ < 0 || index_ >= self->priv->n_children)
11370     {
11371       ClutterActor *tmp = self->priv->last_child;
11372 
11373       if (tmp != NULL)
11374         tmp->priv->next_sibling = child;
11375 
11376       child->priv->prev_sibling = tmp;
11377       child->priv->next_sibling = NULL;
11378     }
11379   else
11380     {
11381       ClutterActor *iter;
11382       int i;
11383 
11384       for (iter = self->priv->first_child, i = 0;
11385            iter != NULL;
11386            iter = iter->priv->next_sibling, i += 1)
11387         {
11388           if (index_ == i)
11389             {
11390               ClutterActor *tmp = iter->priv->prev_sibling;
11391 
11392               child->priv->prev_sibling = tmp;
11393               child->priv->next_sibling = iter;
11394 
11395               iter->priv->prev_sibling = child;
11396 
11397               if (tmp != NULL)
11398                 tmp->priv->next_sibling = child;
11399 
11400               break;
11401             }
11402         }
11403     }
11404 
11405   if (child->priv->prev_sibling == NULL)
11406     self->priv->first_child = child;
11407 
11408   if (child->priv->next_sibling == NULL)
11409     self->priv->last_child = child;
11410 }
11411 
11412 static void
insert_child_above(ClutterActor * self,ClutterActor * child,gpointer data)11413 insert_child_above (ClutterActor *self,
11414                     ClutterActor *child,
11415                     gpointer      data)
11416 {
11417   ClutterActor *sibling = data;
11418 
11419   child->priv->parent = self;
11420 
11421   if (sibling == NULL)
11422     sibling = self->priv->last_child;
11423 
11424   child->priv->prev_sibling = sibling;
11425 
11426   if (sibling != NULL)
11427     {
11428       ClutterActor *tmp = sibling->priv->next_sibling;
11429 
11430       child->priv->next_sibling = tmp;
11431 
11432       if (tmp != NULL)
11433         tmp->priv->prev_sibling = child;
11434 
11435       sibling->priv->next_sibling = child;
11436     }
11437   else
11438     child->priv->next_sibling = NULL;
11439 
11440   if (child->priv->prev_sibling == NULL)
11441     self->priv->first_child = child;
11442 
11443   if (child->priv->next_sibling == NULL)
11444     self->priv->last_child = child;
11445 }
11446 
11447 static void
insert_child_below(ClutterActor * self,ClutterActor * child,gpointer data)11448 insert_child_below (ClutterActor *self,
11449                     ClutterActor *child,
11450                     gpointer      data)
11451 {
11452   ClutterActor *sibling = data;
11453 
11454   child->priv->parent = self;
11455 
11456   if (sibling == NULL)
11457     sibling = self->priv->first_child;
11458 
11459   child->priv->next_sibling = sibling;
11460 
11461   if (sibling != NULL)
11462     {
11463       ClutterActor *tmp = sibling->priv->prev_sibling;
11464 
11465       child->priv->prev_sibling = tmp;
11466 
11467       if (tmp != NULL)
11468         tmp->priv->next_sibling = child;
11469 
11470       sibling->priv->prev_sibling = child;
11471     }
11472   else
11473     child->priv->prev_sibling = NULL;
11474 
11475   if (child->priv->prev_sibling == NULL)
11476     self->priv->first_child = child;
11477 
11478   if (child->priv->next_sibling == NULL)
11479     self->priv->last_child = child;
11480 }
11481 
11482 typedef void (* ClutterActorAddChildFunc) (ClutterActor *parent,
11483                                            ClutterActor *child,
11484                                            gpointer      data);
11485 
11486 typedef enum
11487 {
11488   ADD_CHILD_CREATE_META        = 1 << 0,
11489   ADD_CHILD_EMIT_PARENT_SET    = 1 << 1,
11490   ADD_CHILD_EMIT_ACTOR_ADDED   = 1 << 2,
11491   ADD_CHILD_CHECK_STATE        = 1 << 3,
11492   ADD_CHILD_NOTIFY_FIRST_LAST  = 1 << 4,
11493   ADD_CHILD_SHOW_ON_SET_PARENT = 1 << 5,
11494 
11495   /* default flags for public API */
11496   ADD_CHILD_DEFAULT_FLAGS    = ADD_CHILD_CREATE_META |
11497                                ADD_CHILD_EMIT_PARENT_SET |
11498                                ADD_CHILD_EMIT_ACTOR_ADDED |
11499                                ADD_CHILD_CHECK_STATE |
11500                                ADD_CHILD_NOTIFY_FIRST_LAST |
11501                                ADD_CHILD_SHOW_ON_SET_PARENT,
11502 } ClutterActorAddChildFlags;
11503 
11504 /*< private >
11505  * clutter_actor_add_child_internal:
11506  * @self: a #ClutterActor
11507  * @child: a #ClutterActor
11508  * @flags: control flags for actions
11509  * @add_func: delegate function
11510  * @data: (closure): data to pass to @add_func
11511  *
11512  * Adds @child to the list of children of @self.
11513  *
11514  * The actual insertion inside the list is delegated to @add_func: this
11515  * function will just set up the state, perform basic checks, and emit
11516  * signals.
11517  *
11518  * The @flags argument is used to perform additional operations.
11519  */
11520 static inline void
clutter_actor_add_child_internal(ClutterActor * self,ClutterActor * child,ClutterActorAddChildFlags flags,ClutterActorAddChildFunc add_func,gpointer data)11521 clutter_actor_add_child_internal (ClutterActor              *self,
11522                                   ClutterActor              *child,
11523                                   ClutterActorAddChildFlags  flags,
11524                                   ClutterActorAddChildFunc   add_func,
11525                                   gpointer                   data)
11526 {
11527   ClutterTextDirection text_dir;
11528   gboolean create_meta;
11529   gboolean emit_parent_set, emit_actor_added;
11530   gboolean check_state;
11531   gboolean notify_first_last;
11532   gboolean show_on_set_parent;
11533   ClutterActor *old_first_child, *old_last_child;
11534   GObject *obj;
11535 
11536   if (self == child)
11537     {
11538       g_warning ("Cannot add the actor '%s' to itself.",
11539                   _clutter_actor_get_debug_name (self));
11540       return;
11541     }
11542 
11543   if (child->priv->parent != NULL)
11544     {
11545       g_warning ("The actor '%s' already has a parent, '%s'. You must "
11546                  "use clutter_actor_remove_child() first.",
11547                  _clutter_actor_get_debug_name (child),
11548                  _clutter_actor_get_debug_name (child->priv->parent));
11549       return;
11550     }
11551 
11552   if (CLUTTER_ACTOR_IS_TOPLEVEL (child))
11553     {
11554       g_warning ("The actor '%s' is a top-level actor, and cannot be "
11555                  "a child of another actor.",
11556                  _clutter_actor_get_debug_name (child));
11557       return;
11558     }
11559 
11560   /* the following check disallows calling methods that change the stacking
11561    * order within the destruction sequence, by triggering a critical
11562    * warning first, and leaving the actor in an undefined state, which
11563    * then ends up being caught by an assertion.
11564    *
11565    * the reproducible sequence is:
11566    *
11567    *   - actor gets destroyed;
11568    *   - another actor, linked to the first, will try to change the
11569    *     stacking order of the first actor;
11570    *   - changing the stacking order is a composite operation composed
11571    *     by the following steps:
11572    *     1. ref() the child;
11573    *     2. remove_child_internal(), which removes the reference;
11574    *     3. add_child_internal(), which adds a reference;
11575    *   - the state of the actor is not changed between (2) and (3), as
11576    *     it could be an expensive recomputation;
11577    *   - if (3) bails out, then the actor is in an undefined state, but
11578    *     still alive;
11579    *   - the destruction sequence terminates, but the actor is unparented
11580    *     while its state indicates being parented instead.
11581    *   - assertion failure.
11582    *
11583    * the obvious fix would be to decompose each set_child_*_sibling()
11584    * method into proper remove_child()/add_child(), with state validation;
11585    * this may cause excessive work, though, and trigger a cascade of other
11586    * bugs in code that assumes that a change in the stacking order is an
11587    * atomic operation.
11588    *
11589    * another potential fix is to just remove this check here, and let
11590    * code doing stacking order changes inside the destruction sequence
11591    * of an actor continue doing the stacking changes as before; this
11592    * option still performs more work than necessary.
11593    *
11594    * the third fix is to silently bail out early from every
11595    * set_child_*_sibling() and set_child_at_index() method, and avoid
11596    * doing stack changes altogether; Clutter implements this last option.
11597    *
11598    * see bug: https://bugzilla.gnome.org/show_bug.cgi?id=670647
11599    */
11600   if (CLUTTER_ACTOR_IN_DESTRUCTION (child))
11601     {
11602       g_warning ("The actor '%s' is currently being destroyed, and "
11603                  "cannot be added as a child of another actor.",
11604                  _clutter_actor_get_debug_name (child));
11605       return;
11606     }
11607 
11608   create_meta = (flags & ADD_CHILD_CREATE_META) != 0;
11609   emit_parent_set = (flags & ADD_CHILD_EMIT_PARENT_SET) != 0;
11610   emit_actor_added = (flags & ADD_CHILD_EMIT_ACTOR_ADDED) != 0;
11611   check_state = (flags & ADD_CHILD_CHECK_STATE) != 0;
11612   notify_first_last = (flags & ADD_CHILD_NOTIFY_FIRST_LAST) != 0;
11613   show_on_set_parent = (flags & ADD_CHILD_SHOW_ON_SET_PARENT) != 0;
11614 
11615   old_first_child = self->priv->first_child;
11616   old_last_child = self->priv->last_child;
11617 
11618   obj = G_OBJECT (self);
11619   g_object_freeze_notify (obj);
11620 
11621   if (create_meta)
11622     clutter_container_create_child_meta (CLUTTER_CONTAINER (self), child);
11623 
11624   g_object_ref_sink (child);
11625   child->priv->parent = NULL;
11626   child->priv->next_sibling = NULL;
11627   child->priv->prev_sibling = NULL;
11628 
11629   /* delegate the actual insertion */
11630   add_func (self, child, data);
11631 
11632   g_assert (child->priv->parent == self);
11633 
11634   self->priv->n_children += 1;
11635 
11636   self->priv->age += 1;
11637 
11638   if (self->priv->in_cloned_branch)
11639     clutter_actor_push_in_cloned_branch (child, self->priv->in_cloned_branch);
11640 
11641   if (self->priv->unmapped_paint_branch_counter)
11642     push_in_paint_unmapped_branch (child, self->priv->unmapped_paint_branch_counter);
11643 
11644   /* children may cause their parent to expand, if they are set
11645    * to expand; if a child is not expanded then it cannot change
11646    * its parent's state. any further change later on will queue
11647    * an expand state check.
11648    *
11649    * this check, with the initial state of the needs_compute_expand
11650    * flag set to FALSE, should avoid recomputing the expand flags
11651    * state while building the actor tree.
11652    */
11653   if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
11654       (child->priv->needs_compute_expand ||
11655        child->priv->needs_x_expand ||
11656        child->priv->needs_y_expand))
11657     {
11658       clutter_actor_queue_compute_expand (self);
11659     }
11660 
11661   if (emit_parent_set)
11662     g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL);
11663 
11664   if (check_state)
11665     {
11666       /* If parent is mapped or realized, we need to also be mapped or
11667        * realized once we're inside the parent.
11668        */
11669       clutter_actor_update_map_state (child, MAP_STATE_CHECK);
11670 
11671       /* propagate the parent's text direction to the child */
11672       text_dir = clutter_actor_get_text_direction (self);
11673       clutter_actor_set_text_direction (child, text_dir);
11674     }
11675 
11676   /* this may end up queueing a redraw, in case the actor is
11677    * not visible but the show-on-set-parent property is still
11678    * set.
11679    *
11680    * XXX:2.0 - remove this check and unconditionally show() the
11681    * actor once we remove the show-on-set-parent property
11682    */
11683   if (show_on_set_parent && child->priv->show_on_set_parent)
11684     clutter_actor_show (child);
11685 
11686   /* on the other hand, this will catch any other case where
11687    * the actor is supposed to be visible when it's added
11688    */
11689   if (CLUTTER_ACTOR_IS_MAPPED (child))
11690     clutter_actor_queue_redraw (child);
11691 
11692   if (emit_actor_added)
11693     _clutter_container_emit_actor_added (CLUTTER_CONTAINER (self), child);
11694 
11695   if (notify_first_last)
11696     {
11697       if (old_first_child != self->priv->first_child)
11698         g_object_notify_by_pspec (obj, obj_props[PROP_FIRST_CHILD]);
11699 
11700       if (old_last_child != self->priv->last_child)
11701         g_object_notify_by_pspec (obj, obj_props[PROP_LAST_CHILD]);
11702     }
11703 
11704   g_object_thaw_notify (obj);
11705 }
11706 
11707 /**
11708  * clutter_actor_add_child:
11709  * @self: a #ClutterActor
11710  * @child: a #ClutterActor
11711  *
11712  * Adds @child to the children of @self.
11713  *
11714  * This function will acquire a reference on @child that will only
11715  * be released when calling clutter_actor_remove_child().
11716  *
11717  * This function will take into consideration the #ClutterActor:depth
11718  * of @child, and will keep the list of children sorted.
11719  *
11720  * This function will emit the #ClutterContainer::actor-added signal
11721  * on @self.
11722  *
11723  * Since: 1.10
11724  */
11725 void
clutter_actor_add_child(ClutterActor * self,ClutterActor * child)11726 clutter_actor_add_child (ClutterActor *self,
11727                          ClutterActor *child)
11728 {
11729   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11730   g_return_if_fail (CLUTTER_IS_ACTOR (child));
11731   g_return_if_fail (self != child);
11732   g_return_if_fail (child->priv->parent == NULL);
11733 
11734   clutter_actor_add_child_internal (self, child,
11735                                     ADD_CHILD_DEFAULT_FLAGS,
11736                                     insert_child_at_depth,
11737                                     NULL);
11738 }
11739 
11740 /**
11741  * clutter_actor_insert_child_at_index:
11742  * @self: a #ClutterActor
11743  * @child: a #ClutterActor
11744  * @index_: the index
11745  *
11746  * Inserts @child into the list of children of @self, using the
11747  * given @index_. If @index_ is greater than the number of children
11748  * in @self, or is less than 0, then the new child is added at the end.
11749  *
11750  * This function will acquire a reference on @child that will only
11751  * be released when calling clutter_actor_remove_child().
11752  *
11753  * This function will not take into consideration the #ClutterActor:depth
11754  * of @child.
11755  *
11756  * This function will emit the #ClutterContainer::actor-added signal
11757  * on @self.
11758  *
11759  * Since: 1.10
11760  */
11761 void
clutter_actor_insert_child_at_index(ClutterActor * self,ClutterActor * child,gint index_)11762 clutter_actor_insert_child_at_index (ClutterActor *self,
11763                                      ClutterActor *child,
11764                                      gint          index_)
11765 {
11766   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11767   g_return_if_fail (CLUTTER_IS_ACTOR (child));
11768   g_return_if_fail (self != child);
11769   g_return_if_fail (child->priv->parent == NULL);
11770 
11771   clutter_actor_add_child_internal (self, child,
11772                                     ADD_CHILD_DEFAULT_FLAGS,
11773                                     insert_child_at_index,
11774                                     GINT_TO_POINTER (index_));
11775 }
11776 
11777 /**
11778  * clutter_actor_insert_child_above:
11779  * @self: a #ClutterActor
11780  * @child: a #ClutterActor
11781  * @sibling: (allow-none): a child of @self, or %NULL
11782  *
11783  * Inserts @child into the list of children of @self, above another
11784  * child of @self or, if @sibling is %NULL, above all the children
11785  * of @self.
11786  *
11787  * This function will acquire a reference on @child that will only
11788  * be released when calling clutter_actor_remove_child().
11789  *
11790  * This function will not take into consideration the #ClutterActor:depth
11791  * of @child.
11792  *
11793  * This function will emit the #ClutterContainer::actor-added signal
11794  * on @self.
11795  *
11796  * Since: 1.10
11797  */
11798 void
clutter_actor_insert_child_above(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)11799 clutter_actor_insert_child_above (ClutterActor *self,
11800                                   ClutterActor *child,
11801                                   ClutterActor *sibling)
11802 {
11803   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11804   g_return_if_fail (CLUTTER_IS_ACTOR (child));
11805   g_return_if_fail (self != child);
11806   g_return_if_fail (child != sibling);
11807   g_return_if_fail (child->priv->parent == NULL);
11808   g_return_if_fail (sibling == NULL ||
11809                     (CLUTTER_IS_ACTOR (sibling) &&
11810                      sibling->priv->parent == self));
11811 
11812   clutter_actor_add_child_internal (self, child,
11813                                     ADD_CHILD_DEFAULT_FLAGS,
11814                                     insert_child_above,
11815                                     sibling);
11816 }
11817 
11818 /**
11819  * clutter_actor_insert_child_below:
11820  * @self: a #ClutterActor
11821  * @child: a #ClutterActor
11822  * @sibling: (allow-none): a child of @self, or %NULL
11823  *
11824  * Inserts @child into the list of children of @self, below another
11825  * child of @self or, if @sibling is %NULL, below all the children
11826  * of @self.
11827  *
11828  * This function will acquire a reference on @child that will only
11829  * be released when calling clutter_actor_remove_child().
11830  *
11831  * This function will not take into consideration the #ClutterActor:depth
11832  * of @child.
11833  *
11834  * This function will emit the #ClutterContainer::actor-added signal
11835  * on @self.
11836  *
11837  * Since: 1.10
11838  */
11839 void
clutter_actor_insert_child_below(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)11840 clutter_actor_insert_child_below (ClutterActor *self,
11841                                   ClutterActor *child,
11842                                   ClutterActor *sibling)
11843 {
11844   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11845   g_return_if_fail (CLUTTER_IS_ACTOR (child));
11846   g_return_if_fail (self != child);
11847   g_return_if_fail (child != sibling);
11848   g_return_if_fail (child->priv->parent == NULL);
11849   g_return_if_fail (sibling == NULL ||
11850                     (CLUTTER_IS_ACTOR (sibling) &&
11851                      sibling->priv->parent == self));
11852 
11853   clutter_actor_add_child_internal (self, child,
11854                                     ADD_CHILD_DEFAULT_FLAGS,
11855                                     insert_child_below,
11856                                     sibling);
11857 }
11858 
11859 /**
11860  * clutter_actor_get_parent:
11861  * @self: A #ClutterActor
11862  *
11863  * Retrieves the parent of @self.
11864  *
11865  * Return Value: (transfer none): The #ClutterActor parent, or %NULL
11866  *  if no parent is set
11867  */
11868 ClutterActor *
clutter_actor_get_parent(ClutterActor * self)11869 clutter_actor_get_parent (ClutterActor *self)
11870 {
11871   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
11872 
11873   return self->priv->parent;
11874 }
11875 
11876 /**
11877  * clutter_actor_get_paint_visibility:
11878  * @self: A #ClutterActor
11879  *
11880  * Retrieves the 'paint' visibility of an actor recursively checking for non
11881  * visible parents.
11882  *
11883  * This is by definition the same as %CLUTTER_ACTOR_IS_MAPPED.
11884  *
11885  * Return Value: %TRUE if the actor is visible and will be painted.
11886  *
11887  * Since: 0.8
11888  */
11889 gboolean
clutter_actor_get_paint_visibility(ClutterActor * actor)11890 clutter_actor_get_paint_visibility (ClutterActor *actor)
11891 {
11892   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
11893 
11894   return CLUTTER_ACTOR_IS_MAPPED (actor);
11895 }
11896 
11897 /**
11898  * clutter_actor_remove_child:
11899  * @self: a #ClutterActor
11900  * @child: a #ClutterActor
11901  *
11902  * Removes @child from the children of @self.
11903  *
11904  * This function will release the reference added by
11905  * clutter_actor_add_child(), so if you want to keep using @child
11906  * you will have to acquire a referenced on it before calling this
11907  * function.
11908  *
11909  * This function will emit the #ClutterContainer::actor-removed
11910  * signal on @self.
11911  *
11912  * Since: 1.10
11913  */
11914 void
clutter_actor_remove_child(ClutterActor * self,ClutterActor * child)11915 clutter_actor_remove_child (ClutterActor *self,
11916                             ClutterActor *child)
11917 {
11918   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11919   g_return_if_fail (CLUTTER_IS_ACTOR (child));
11920   g_return_if_fail (self != child);
11921   g_return_if_fail (child->priv->parent != NULL);
11922   g_return_if_fail (child->priv->parent == self);
11923 
11924   clutter_actor_remove_child_internal (self, child,
11925                                        REMOVE_CHILD_DEFAULT_FLAGS);
11926 }
11927 
11928 /**
11929  * clutter_actor_remove_all_children:
11930  * @self: a #ClutterActor
11931  *
11932  * Removes all children of @self.
11933  *
11934  * This function releases the reference added by inserting a child actor
11935  * in the list of children of @self.
11936  *
11937  * If the reference count of a child drops to zero, the child will be
11938  * destroyed. If you want to ensure the destruction of all the children
11939  * of @self, use clutter_actor_destroy_all_children().
11940  *
11941  * Since: 1.10
11942  */
11943 void
clutter_actor_remove_all_children(ClutterActor * self)11944 clutter_actor_remove_all_children (ClutterActor *self)
11945 {
11946   ClutterActorIter iter;
11947 
11948   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11949 
11950   if (self->priv->n_children == 0)
11951     return;
11952 
11953   g_object_freeze_notify (G_OBJECT (self));
11954 
11955   clutter_actor_iter_init (&iter, self);
11956   while (clutter_actor_iter_next (&iter, NULL))
11957     clutter_actor_iter_remove (&iter);
11958 
11959   g_object_thaw_notify (G_OBJECT (self));
11960 
11961   /* sanity check */
11962   g_assert (self->priv->first_child == NULL);
11963   g_assert (self->priv->last_child == NULL);
11964   g_assert (self->priv->n_children == 0);
11965 }
11966 
11967 /**
11968  * clutter_actor_destroy_all_children:
11969  * @self: a #ClutterActor
11970  *
11971  * Destroys all children of @self.
11972  *
11973  * This function releases the reference added by inserting a child
11974  * actor in the list of children of @self, and ensures that the
11975  * #ClutterActor::destroy signal is emitted on each child of the
11976  * actor.
11977  *
11978  * By default, #ClutterActor will emit the #ClutterActor::destroy signal
11979  * when its reference count drops to 0; the default handler of the
11980  * #ClutterActor::destroy signal will destroy all the children of an
11981  * actor. This function ensures that all children are destroyed, instead
11982  * of just removed from @self, unlike clutter_actor_remove_all_children()
11983  * which will merely release the reference and remove each child.
11984  *
11985  * Unless you acquired an additional reference on each child of @self
11986  * prior to calling clutter_actor_remove_all_children() and want to reuse
11987  * the actors, you should use clutter_actor_destroy_all_children() in
11988  * order to make sure that children are destroyed and signal handlers
11989  * are disconnected even in cases where circular references prevent this
11990  * from automatically happening through reference counting alone.
11991  *
11992  * Since: 1.10
11993  */
11994 void
clutter_actor_destroy_all_children(ClutterActor * self)11995 clutter_actor_destroy_all_children (ClutterActor *self)
11996 {
11997   ClutterActorIter iter;
11998 
11999   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12000 
12001   if (self->priv->n_children == 0)
12002     return;
12003 
12004   g_object_freeze_notify (G_OBJECT (self));
12005 
12006   clutter_actor_iter_init (&iter, self);
12007   while (clutter_actor_iter_next (&iter, NULL))
12008     clutter_actor_iter_destroy (&iter);
12009 
12010   g_object_thaw_notify (G_OBJECT (self));
12011 
12012   /* sanity check */
12013   g_assert (self->priv->first_child == NULL);
12014   g_assert (self->priv->last_child == NULL);
12015   g_assert (self->priv->n_children == 0);
12016 }
12017 
12018 typedef struct _InsertBetweenData {
12019   ClutterActor *prev_sibling;
12020   ClutterActor *next_sibling;
12021 } InsertBetweenData;
12022 
12023 static void
insert_child_between(ClutterActor * self,ClutterActor * child,gpointer data_)12024 insert_child_between (ClutterActor *self,
12025                       ClutterActor *child,
12026                       gpointer      data_)
12027 {
12028   InsertBetweenData *data = data_;
12029   ClutterActor *prev_sibling = data->prev_sibling;
12030   ClutterActor *next_sibling = data->next_sibling;
12031 
12032   child->priv->parent = self;
12033   child->priv->prev_sibling = prev_sibling;
12034   child->priv->next_sibling = next_sibling;
12035 
12036   if (prev_sibling != NULL)
12037     prev_sibling->priv->next_sibling = child;
12038 
12039   if (next_sibling != NULL)
12040     next_sibling->priv->prev_sibling = child;
12041 
12042   if (child->priv->prev_sibling == NULL)
12043     self->priv->first_child = child;
12044 
12045   if (child->priv->next_sibling == NULL)
12046     self->priv->last_child = child;
12047 }
12048 
12049 /**
12050  * clutter_actor_replace_child:
12051  * @self: a #ClutterActor
12052  * @old_child: the child of @self to replace
12053  * @new_child: the #ClutterActor to replace @old_child
12054  *
12055  * Replaces @old_child with @new_child in the list of children of @self.
12056  *
12057  * Since: 1.10
12058  */
12059 void
clutter_actor_replace_child(ClutterActor * self,ClutterActor * old_child,ClutterActor * new_child)12060 clutter_actor_replace_child (ClutterActor *self,
12061                              ClutterActor *old_child,
12062                              ClutterActor *new_child)
12063 {
12064   ClutterActor *prev_sibling, *next_sibling;
12065   InsertBetweenData clos;
12066 
12067   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12068   g_return_if_fail (CLUTTER_IS_ACTOR (old_child));
12069   g_return_if_fail (old_child->priv->parent == self);
12070   g_return_if_fail (CLUTTER_IS_ACTOR (new_child));
12071   g_return_if_fail (old_child != new_child);
12072   g_return_if_fail (new_child != self);
12073   g_return_if_fail (new_child->priv->parent == NULL);
12074 
12075   prev_sibling = old_child->priv->prev_sibling;
12076   next_sibling = old_child->priv->next_sibling;
12077   clutter_actor_remove_child_internal (self, old_child,
12078                                        REMOVE_CHILD_DEFAULT_FLAGS);
12079 
12080   clos.prev_sibling = prev_sibling;
12081   clos.next_sibling = next_sibling;
12082   clutter_actor_add_child_internal (self, new_child,
12083                                     ADD_CHILD_DEFAULT_FLAGS,
12084                                     insert_child_between,
12085                                     &clos);
12086 }
12087 
12088 /**
12089  * clutter_actor_contains:
12090  * @self: A #ClutterActor
12091  * @descendant: A #ClutterActor, possibly contained in @self
12092  *
12093  * Determines if @descendant is contained inside @self (either as an
12094  * immediate child, or as a deeper descendant). If @self and
12095  * @descendant point to the same actor then it will also return %TRUE.
12096  *
12097  * Return value: whether @descendent is contained within @self
12098  *
12099  * Since: 1.4
12100  */
12101 gboolean
clutter_actor_contains(ClutterActor * self,ClutterActor * descendant)12102 clutter_actor_contains (ClutterActor *self,
12103 			ClutterActor *descendant)
12104 {
12105   ClutterActor *actor;
12106 
12107   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
12108   g_return_val_if_fail (CLUTTER_IS_ACTOR (descendant), FALSE);
12109 
12110   for (actor = descendant; actor; actor = actor->priv->parent)
12111     if (actor == self)
12112       return TRUE;
12113 
12114   return FALSE;
12115 }
12116 
12117 /**
12118  * clutter_actor_set_child_above_sibling:
12119  * @self: a #ClutterActor
12120  * @child: a #ClutterActor child of @self
12121  * @sibling: (allow-none): a #ClutterActor child of @self, or %NULL
12122  *
12123  * Sets @child to be above @sibling in the list of children of @self.
12124  *
12125  * If @sibling is %NULL, @child will be the new last child of @self.
12126  *
12127  * This function is logically equivalent to removing @child and using
12128  * clutter_actor_insert_child_above(), but it will not emit signals
12129  * or change state on @child.
12130  *
12131  * Since: 1.10
12132  */
12133 void
clutter_actor_set_child_above_sibling(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)12134 clutter_actor_set_child_above_sibling (ClutterActor *self,
12135                                        ClutterActor *child,
12136                                        ClutterActor *sibling)
12137 {
12138   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12139   g_return_if_fail (CLUTTER_IS_ACTOR (child));
12140   g_return_if_fail (child->priv->parent == self);
12141   g_return_if_fail (child != sibling);
12142   g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
12143 
12144   if (sibling != NULL)
12145     g_return_if_fail (sibling->priv->parent == self);
12146 
12147   if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
12148       CLUTTER_ACTOR_IN_DESTRUCTION (child) ||
12149       (sibling != NULL && CLUTTER_ACTOR_IN_DESTRUCTION (sibling)))
12150     return;
12151 
12152   /* we don't want to change the state of child, or emit signals, or
12153    * regenerate ChildMeta instances here, but we still want to follow
12154    * the correct sequence of steps encoded in remove_child() and
12155    * add_child(), so that correctness is ensured, and we only go
12156    * through one known code path.
12157    */
12158   g_object_ref (child);
12159   clutter_actor_remove_child_internal (self, child, 0);
12160   clutter_actor_add_child_internal (self, child,
12161                                     ADD_CHILD_NOTIFY_FIRST_LAST,
12162                                     insert_child_above,
12163                                     sibling);
12164   g_object_unref(child);
12165 
12166   clutter_actor_queue_relayout (self);
12167 }
12168 
12169 /**
12170  * clutter_actor_set_child_below_sibling:
12171  * @self: a #ClutterActor
12172  * @child: a #ClutterActor child of @self
12173  * @sibling: (allow-none): a #ClutterActor child of @self, or %NULL
12174  *
12175  * Sets @child to be below @sibling in the list of children of @self.
12176  *
12177  * If @sibling is %NULL, @child will be the new first child of @self.
12178  *
12179  * This function is logically equivalent to removing @self and using
12180  * clutter_actor_insert_child_below(), but it will not emit signals
12181  * or change state on @child.
12182  *
12183  * Since: 1.10
12184  */
12185 void
clutter_actor_set_child_below_sibling(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)12186 clutter_actor_set_child_below_sibling (ClutterActor *self,
12187                                        ClutterActor *child,
12188                                        ClutterActor *sibling)
12189 {
12190   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12191   g_return_if_fail (CLUTTER_IS_ACTOR (child));
12192   g_return_if_fail (child->priv->parent == self);
12193   g_return_if_fail (child != sibling);
12194   g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
12195 
12196   if (sibling != NULL)
12197     g_return_if_fail (sibling->priv->parent == self);
12198 
12199   if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
12200       CLUTTER_ACTOR_IN_DESTRUCTION (child) ||
12201       (sibling != NULL && CLUTTER_ACTOR_IN_DESTRUCTION (sibling)))
12202     return;
12203 
12204   /* see the comment in set_child_above_sibling() */
12205   g_object_ref (child);
12206   clutter_actor_remove_child_internal (self, child, 0);
12207   clutter_actor_add_child_internal (self, child,
12208                                     ADD_CHILD_NOTIFY_FIRST_LAST,
12209                                     insert_child_below,
12210                                     sibling);
12211   g_object_unref(child);
12212 
12213   clutter_actor_queue_relayout (self);
12214 }
12215 
12216 /**
12217  * clutter_actor_set_child_at_index:
12218  * @self: a #ClutterActor
12219  * @child: a #ClutterActor child of @self
12220  * @index_: the new index for @child
12221  *
12222  * Changes the index of @child in the list of children of @self.
12223  *
12224  * This function is logically equivalent to removing @child and
12225  * calling clutter_actor_insert_child_at_index(), but it will not
12226  * emit signals or change state on @child.
12227  *
12228  * Since: 1.10
12229  */
12230 void
clutter_actor_set_child_at_index(ClutterActor * self,ClutterActor * child,gint index_)12231 clutter_actor_set_child_at_index (ClutterActor *self,
12232                                   ClutterActor *child,
12233                                   gint          index_)
12234 {
12235   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12236   g_return_if_fail (CLUTTER_IS_ACTOR (child));
12237   g_return_if_fail (child->priv->parent == self);
12238   g_return_if_fail (index_ <= self->priv->n_children);
12239 
12240   if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
12241       CLUTTER_ACTOR_IN_DESTRUCTION (child))
12242     return;
12243 
12244   g_object_ref (child);
12245   clutter_actor_remove_child_internal (self, child, 0);
12246   clutter_actor_add_child_internal (self, child,
12247                                     ADD_CHILD_NOTIFY_FIRST_LAST,
12248                                     insert_child_at_index,
12249                                     GINT_TO_POINTER (index_));
12250   g_object_unref (child);
12251 
12252   clutter_actor_queue_relayout (self);
12253 }
12254 
12255 /*
12256  * Event handling
12257  */
12258 
12259 /**
12260  * clutter_actor_event:
12261  * @actor: a #ClutterActor
12262  * @event: a #ClutterEvent
12263  * @capture: %TRUE if event in in capture phase, %FALSE otherwise.
12264  *
12265  * This function is used to emit an event on the main stage.
12266  * You should rarely need to use this function, except for
12267  * synthetising events.
12268  *
12269  * Return value: the return value from the signal emission: %TRUE
12270  *   if the actor handled the event, or %FALSE if the event was
12271  *   not handled
12272  *
12273  * Since: 0.6
12274  */
12275 gboolean
clutter_actor_event(ClutterActor * actor,const ClutterEvent * event,gboolean capture)12276 clutter_actor_event (ClutterActor       *actor,
12277                      const ClutterEvent *event,
12278                      gboolean            capture)
12279 {
12280   gboolean retval = FALSE;
12281   gint signal_num = -1;
12282   GQuark detail = 0;
12283 
12284   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
12285   g_return_val_if_fail (event != NULL, FALSE);
12286 
12287   g_object_ref (actor);
12288 
12289   switch (event->type)
12290     {
12291     case CLUTTER_NOTHING:
12292       break;
12293     case CLUTTER_BUTTON_PRESS:
12294       signal_num = BUTTON_PRESS_EVENT;
12295       detail = quark_button;
12296       break;
12297     case CLUTTER_BUTTON_RELEASE:
12298       signal_num = BUTTON_RELEASE_EVENT;
12299       detail = quark_button;
12300       break;
12301     case CLUTTER_SCROLL:
12302       signal_num = SCROLL_EVENT;
12303       detail = quark_scroll;
12304       break;
12305     case CLUTTER_KEY_PRESS:
12306       signal_num = KEY_PRESS_EVENT;
12307       detail = quark_key;
12308       break;
12309     case CLUTTER_KEY_RELEASE:
12310       signal_num = KEY_RELEASE_EVENT;
12311       detail = quark_key;
12312       break;
12313     case CLUTTER_MOTION:
12314       signal_num = MOTION_EVENT;
12315       detail = quark_motion;
12316       break;
12317     case CLUTTER_ENTER:
12318       signal_num = ENTER_EVENT;
12319       detail = quark_pointer_focus;
12320       break;
12321     case CLUTTER_LEAVE:
12322       signal_num = LEAVE_EVENT;
12323       detail = quark_pointer_focus;
12324       break;
12325     case CLUTTER_TOUCH_BEGIN:
12326     case CLUTTER_TOUCH_END:
12327     case CLUTTER_TOUCH_UPDATE:
12328     case CLUTTER_TOUCH_CANCEL:
12329       signal_num = TOUCH_EVENT;
12330       detail = quark_touch;
12331       break;
12332     case CLUTTER_TOUCHPAD_PINCH:
12333     case CLUTTER_TOUCHPAD_SWIPE:
12334       signal_num = -1;
12335       detail = quark_touchpad;
12336       break;
12337     case CLUTTER_PROXIMITY_IN:
12338     case CLUTTER_PROXIMITY_OUT:
12339       signal_num = -1;
12340       detail = quark_proximity;
12341       break;
12342     case CLUTTER_PAD_BUTTON_PRESS:
12343     case CLUTTER_PAD_BUTTON_RELEASE:
12344     case CLUTTER_PAD_STRIP:
12345     case CLUTTER_PAD_RING:
12346       signal_num = -1;
12347       detail = quark_pad;
12348       break;
12349     case CLUTTER_IM_COMMIT:
12350     case CLUTTER_IM_DELETE:
12351     case CLUTTER_IM_PREEDIT:
12352       signal_num = -1;
12353       detail = quark_im;
12354     case CLUTTER_DEVICE_ADDED:
12355     case CLUTTER_DEVICE_REMOVED:
12356       break;
12357     case CLUTTER_EVENT_LAST:  /* Just keep compiler warnings quiet */
12358       break;
12359     }
12360 
12361   if (capture)
12362     g_signal_emit (actor, actor_signals[CAPTURED_EVENT], detail, event, &retval);
12363   else
12364     {
12365       g_signal_emit (actor, actor_signals[EVENT], 0, event, &retval);
12366 
12367       if (!retval && signal_num != -1)
12368         g_signal_emit (actor, actor_signals[signal_num], 0, event, &retval);
12369     }
12370 
12371   g_object_unref (actor);
12372 
12373   return retval;
12374 }
12375 
12376 /**
12377  * clutter_actor_set_reactive:
12378  * @actor: a #ClutterActor
12379  * @reactive: whether the actor should be reactive to events
12380  *
12381  * Sets @actor as reactive. Reactive actors will receive events.
12382  *
12383  * Since: 0.6
12384  */
12385 void
clutter_actor_set_reactive(ClutterActor * actor,gboolean reactive)12386 clutter_actor_set_reactive (ClutterActor *actor,
12387                             gboolean      reactive)
12388 {
12389   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
12390 
12391   if (reactive == CLUTTER_ACTOR_IS_REACTIVE (actor))
12392     return;
12393 
12394   if (reactive)
12395     CLUTTER_ACTOR_SET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE);
12396   else
12397     CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE);
12398 
12399   g_object_notify_by_pspec (G_OBJECT (actor), obj_props[PROP_REACTIVE]);
12400 }
12401 
12402 /**
12403  * clutter_actor_get_reactive:
12404  * @actor: a #ClutterActor
12405  *
12406  * Checks whether @actor is marked as reactive.
12407  *
12408  * Return value: %TRUE if the actor is reactive
12409  *
12410  * Since: 0.6
12411  */
12412 gboolean
clutter_actor_get_reactive(ClutterActor * actor)12413 clutter_actor_get_reactive (ClutterActor *actor)
12414 {
12415   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
12416 
12417   return CLUTTER_ACTOR_IS_REACTIVE (actor) ? TRUE : FALSE;
12418 }
12419 
12420 static void
clutter_actor_store_content_box(ClutterActor * self,const ClutterActorBox * box)12421 clutter_actor_store_content_box (ClutterActor *self,
12422                                  const ClutterActorBox *box)
12423 {
12424   if (box != NULL)
12425     {
12426       self->priv->content_box = *box;
12427       self->priv->content_box_valid = TRUE;
12428     }
12429   else
12430     self->priv->content_box_valid = FALSE;
12431 
12432   clutter_actor_queue_redraw (self);
12433 
12434   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
12435 }
12436 
12437 static void
clutter_container_iface_init(ClutterContainerIface * iface)12438 clutter_container_iface_init (ClutterContainerIface *iface)
12439 {
12440   /* we don't override anything, as ClutterContainer already has a default
12441    * implementation that we can use, and which calls into our own API.
12442    */
12443 }
12444 
12445 typedef enum
12446 {
12447   PARSE_X,
12448   PARSE_Y,
12449   PARSE_WIDTH,
12450   PARSE_HEIGHT,
12451 } ParseDimension;
12452 
12453 static gfloat
parse_units(ClutterActor * self,ParseDimension dimension,JsonNode * node)12454 parse_units (ClutterActor   *self,
12455              ParseDimension  dimension,
12456              JsonNode       *node)
12457 {
12458   GValue value = G_VALUE_INIT;
12459   gfloat retval = 0;
12460 
12461   if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE)
12462     return 0;
12463 
12464   json_node_get_value (node, &value);
12465 
12466   if (G_VALUE_HOLDS (&value, G_TYPE_INT64))
12467     {
12468       retval = (gfloat) g_value_get_int64 (&value);
12469     }
12470   else if (G_VALUE_HOLDS (&value, G_TYPE_DOUBLE))
12471     {
12472       retval = g_value_get_double (&value);
12473     }
12474   else if (G_VALUE_HOLDS (&value, G_TYPE_STRING))
12475     {
12476       ClutterUnits units;
12477       gboolean res;
12478 
12479       res = clutter_units_from_string (&units, g_value_get_string (&value));
12480       if (res)
12481         retval = clutter_units_to_pixels (&units);
12482       else
12483         {
12484           g_warning ("Invalid value '%s': integers, strings or floating point "
12485                      "values can be used for the x, y, width and height "
12486                      "properties. Valid modifiers for strings are 'px', 'mm', "
12487                      "'pt' and 'em'.",
12488                      g_value_get_string (&value));
12489           retval = 0;
12490         }
12491     }
12492   else
12493     {
12494       g_warning ("Invalid value of type '%s': integers, strings of floating "
12495                  "point values can be used for the x, y, width, and height "
12496                  "properties.",
12497                  g_type_name (G_VALUE_TYPE (&value)));
12498     }
12499 
12500   g_value_unset (&value);
12501 
12502   return retval;
12503 }
12504 
12505 typedef struct {
12506   ClutterRotateAxis axis;
12507 
12508   gdouble angle;
12509 
12510   gfloat center_x;
12511   gfloat center_y;
12512   gfloat center_z;
12513 } RotationInfo;
12514 
12515 static inline gboolean
parse_rotation_array(ClutterActor * actor,JsonArray * array,RotationInfo * info)12516 parse_rotation_array (ClutterActor *actor,
12517                       JsonArray    *array,
12518                       RotationInfo *info)
12519 {
12520   JsonNode *element;
12521 
12522   if (json_array_get_length (array) != 2)
12523     return FALSE;
12524 
12525   /* angle */
12526   element = json_array_get_element (array, 0);
12527   if (JSON_NODE_TYPE (element) == JSON_NODE_VALUE)
12528     info->angle = json_node_get_double (element);
12529   else
12530     return FALSE;
12531 
12532   /* center */
12533   element = json_array_get_element (array, 1);
12534   if (JSON_NODE_TYPE (element) == JSON_NODE_ARRAY)
12535     {
12536       JsonArray *center = json_node_get_array (element);
12537 
12538       if (json_array_get_length (center) != 2)
12539         return FALSE;
12540 
12541       switch (info->axis)
12542         {
12543         case CLUTTER_X_AXIS:
12544           info->center_y = parse_units (actor, PARSE_Y,
12545                                         json_array_get_element (center, 0));
12546           info->center_z = parse_units (actor, PARSE_Y,
12547                                         json_array_get_element (center, 1));
12548           return TRUE;
12549 
12550         case CLUTTER_Y_AXIS:
12551           info->center_x = parse_units (actor, PARSE_X,
12552                                         json_array_get_element (center, 0));
12553           info->center_z = parse_units (actor, PARSE_X,
12554                                         json_array_get_element (center, 1));
12555           return TRUE;
12556 
12557         case CLUTTER_Z_AXIS:
12558           info->center_x = parse_units (actor, PARSE_X,
12559                                         json_array_get_element (center, 0));
12560           info->center_y = parse_units (actor, PARSE_Y,
12561                                         json_array_get_element (center, 1));
12562           return TRUE;
12563         }
12564     }
12565 
12566   return FALSE;
12567 }
12568 
12569 static gboolean
parse_rotation(ClutterActor * actor,JsonNode * node,RotationInfo * info)12570 parse_rotation (ClutterActor *actor,
12571                 JsonNode     *node,
12572                 RotationInfo *info)
12573 {
12574   JsonArray *array;
12575   guint len, i;
12576   gboolean retval = FALSE;
12577 
12578   if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
12579     {
12580       g_warning ("Invalid node of type '%s' found, expecting an array",
12581                  json_node_type_name (node));
12582       return FALSE;
12583     }
12584 
12585   array = json_node_get_array (node);
12586   len = json_array_get_length (array);
12587 
12588   for (i = 0; i < len; i++)
12589     {
12590       JsonNode *element = json_array_get_element (array, i);
12591       JsonObject *object;
12592       JsonNode *member;
12593 
12594       if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT)
12595         {
12596           g_warning ("Invalid node of type '%s' found, expecting an object",
12597                      json_node_type_name (element));
12598           return FALSE;
12599         }
12600 
12601       object = json_node_get_object (element);
12602 
12603       if (json_object_has_member (object, "x-axis"))
12604         {
12605           member = json_object_get_member (object, "x-axis");
12606 
12607           info->axis = CLUTTER_X_AXIS;
12608 
12609           if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
12610             {
12611               info->angle = json_node_get_double (member);
12612               retval = TRUE;
12613             }
12614           else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
12615             retval = parse_rotation_array (actor,
12616                                            json_node_get_array (member),
12617                                            info);
12618           else
12619             retval = FALSE;
12620         }
12621       else if (json_object_has_member (object, "y-axis"))
12622         {
12623           member = json_object_get_member (object, "y-axis");
12624 
12625           info->axis = CLUTTER_Y_AXIS;
12626 
12627           if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
12628             {
12629               info->angle = json_node_get_double (member);
12630               retval = TRUE;
12631             }
12632           else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
12633             retval = parse_rotation_array (actor,
12634                                            json_node_get_array (member),
12635                                            info);
12636           else
12637             retval = FALSE;
12638         }
12639       else if (json_object_has_member (object, "z-axis"))
12640         {
12641           member = json_object_get_member (object, "z-axis");
12642 
12643           info->axis = CLUTTER_Z_AXIS;
12644 
12645           if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
12646             {
12647               info->angle = json_node_get_double (member);
12648               retval = TRUE;
12649             }
12650           else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
12651             retval = parse_rotation_array (actor,
12652                                            json_node_get_array (member),
12653                                            info);
12654           else
12655             retval = FALSE;
12656         }
12657     }
12658 
12659   return retval;
12660 }
12661 
12662 static GSList *
parse_actor_metas(ClutterScript * script,ClutterActor * actor,JsonNode * node)12663 parse_actor_metas (ClutterScript *script,
12664                    ClutterActor  *actor,
12665                    JsonNode      *node)
12666 {
12667   GList *elements, *l;
12668   GSList *retval = NULL;
12669 
12670   if (!JSON_NODE_HOLDS_ARRAY (node))
12671     return NULL;
12672 
12673   elements = json_array_get_elements (json_node_get_array (node));
12674 
12675   for (l = elements; l != NULL; l = l->next)
12676     {
12677       JsonNode *element = l->data;
12678       const gchar *id_ = _clutter_script_get_id_from_node (element);
12679       GObject *meta;
12680 
12681       if (id_ == NULL || *id_ == '\0')
12682         continue;
12683 
12684       meta = clutter_script_get_object (script, id_);
12685       if (meta == NULL)
12686         continue;
12687 
12688       retval = g_slist_prepend (retval, meta);
12689     }
12690 
12691   g_list_free (elements);
12692 
12693   return g_slist_reverse (retval);
12694 }
12695 
12696 static ClutterMargin *
parse_margin(ClutterActor * self,JsonNode * node)12697 parse_margin (ClutterActor *self,
12698               JsonNode     *node)
12699 {
12700   ClutterMargin *margin;
12701   JsonArray *array;
12702 
12703   if (!JSON_NODE_HOLDS_ARRAY (node))
12704     {
12705       g_warning ("The margin property must be an array of 1 to 4 elements");
12706       return NULL;
12707     }
12708 
12709   margin = clutter_margin_new ();
12710   array = json_node_get_array (node);
12711   switch (json_array_get_length (array))
12712     {
12713     case 1:
12714       margin->top = margin->right = margin->bottom = margin->left =
12715         parse_units (self, 0, json_array_get_element (array, 0));
12716       break;
12717 
12718     case 2:
12719       margin->top = margin->bottom =
12720         parse_units (self, 0, json_array_get_element (array, 0));
12721       margin->right = margin->left =
12722         parse_units (self, 0, json_array_get_element (array, 1));
12723       break;
12724 
12725     case 3:
12726       margin->top =
12727         parse_units (self, 0, json_array_get_element (array, 0));
12728       margin->right = margin->left =
12729         parse_units (self, 0, json_array_get_element (array, 1));
12730       margin->bottom =
12731         parse_units (self, 0, json_array_get_element (array, 2));
12732       break;
12733 
12734     case 4:
12735       margin->top =
12736         parse_units (self, 0, json_array_get_element (array, 0));
12737       margin->right =
12738         parse_units (self, 0, json_array_get_element (array, 1));
12739       margin->bottom =
12740         parse_units (self, 0, json_array_get_element (array, 2));
12741       margin->left =
12742         parse_units (self, 0, json_array_get_element (array, 3));
12743       break;
12744 
12745     default:
12746       g_warning ("The margin property must be an array of 1 to 4 elements");
12747       clutter_margin_free (margin);
12748       return NULL;
12749     }
12750   return margin;
12751 }
12752 
12753 static gboolean
clutter_actor_parse_custom_node(ClutterScriptable * scriptable,ClutterScript * script,GValue * value,const gchar * name,JsonNode * node)12754 clutter_actor_parse_custom_node (ClutterScriptable *scriptable,
12755                                  ClutterScript     *script,
12756                                  GValue            *value,
12757                                  const gchar       *name,
12758                                  JsonNode          *node)
12759 {
12760   ClutterActor *actor = CLUTTER_ACTOR (scriptable);
12761   gboolean retval = FALSE;
12762 
12763   if ((name[0] == 'x' && name[1] == '\0') ||
12764       (name[0] == 'y' && name[1] == '\0') ||
12765       (strcmp (name, "width") == 0) ||
12766       (strcmp (name, "height") == 0))
12767     {
12768       ParseDimension dimension;
12769       gfloat units;
12770 
12771       if (name[0] == 'x')
12772         dimension = PARSE_X;
12773       else if (name[0] == 'y')
12774         dimension = PARSE_Y;
12775       else if (name[0] == 'w')
12776         dimension = PARSE_WIDTH;
12777       else if (name[0] == 'h')
12778         dimension = PARSE_HEIGHT;
12779       else
12780         return FALSE;
12781 
12782       units = parse_units (actor, dimension, node);
12783 
12784       /* convert back to pixels: all properties are pixel-based */
12785       g_value_init (value, G_TYPE_FLOAT);
12786       g_value_set_float (value, units);
12787 
12788       retval = TRUE;
12789     }
12790   else if (strcmp (name, "rotation") == 0)
12791     {
12792       RotationInfo *info;
12793 
12794       info = g_new0 (RotationInfo, 1);
12795       retval = parse_rotation (actor, node, info);
12796 
12797       if (retval)
12798         {
12799           g_value_init (value, G_TYPE_POINTER);
12800           g_value_set_pointer (value, info);
12801         }
12802       else
12803         g_free (info);
12804     }
12805   else if (strcmp (name, "actions") == 0 ||
12806            strcmp (name, "constraints") == 0 ||
12807            strcmp (name, "effects") == 0)
12808     {
12809       GSList *l;
12810 
12811       l = parse_actor_metas (script, actor, node);
12812 
12813       g_value_init (value, G_TYPE_POINTER);
12814       g_value_set_pointer (value, l);
12815 
12816       retval = TRUE;
12817     }
12818   else if (strcmp (name, "margin") == 0)
12819     {
12820       ClutterMargin *margin = parse_margin (actor, node);
12821 
12822       if (margin)
12823         {
12824           g_value_init (value, CLUTTER_TYPE_MARGIN);
12825           g_value_set_boxed (value, margin);
12826           retval = TRUE;
12827         }
12828     }
12829 
12830   return retval;
12831 }
12832 
12833 static void
clutter_actor_set_custom_property(ClutterScriptable * scriptable,ClutterScript * script,const gchar * name,const GValue * value)12834 clutter_actor_set_custom_property (ClutterScriptable *scriptable,
12835                                    ClutterScript     *script,
12836                                    const gchar       *name,
12837                                    const GValue      *value)
12838 {
12839   ClutterActor *actor = CLUTTER_ACTOR (scriptable);
12840 
12841 #ifdef CLUTTER_ENABLE_DEBUG
12842   if (G_UNLIKELY (CLUTTER_HAS_DEBUG (SCRIPT)))
12843     {
12844       gchar *tmp = g_strdup_value_contents (value);
12845 
12846       CLUTTER_NOTE (SCRIPT,
12847                     "in ClutterActor::set_custom_property('%s') = %s",
12848                     name,
12849                     tmp);
12850 
12851       g_free (tmp);
12852     }
12853 #endif /* CLUTTER_ENABLE_DEBUG */
12854 
12855   if (strcmp (name, "rotation") == 0)
12856     {
12857       RotationInfo *info;
12858 
12859       if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
12860         return;
12861 
12862       info = g_value_get_pointer (value);
12863 
12864       clutter_actor_set_rotation_angle (actor, info->axis, info->angle);
12865 
12866       g_free (info);
12867 
12868       return;
12869     }
12870 
12871   if (strcmp (name, "actions") == 0 ||
12872       strcmp (name, "constraints") == 0 ||
12873       strcmp (name, "effects") == 0)
12874     {
12875       GSList *metas, *l;
12876 
12877       if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
12878         return;
12879 
12880       metas = g_value_get_pointer (value);
12881       for (l = metas; l != NULL; l = l->next)
12882         {
12883           if (name[0] == 'a')
12884             clutter_actor_add_action (actor, l->data);
12885 
12886           if (name[0] == 'c')
12887             clutter_actor_add_constraint (actor, l->data);
12888 
12889           if (name[0] == 'e')
12890             clutter_actor_add_effect (actor, l->data);
12891         }
12892 
12893       g_slist_free (metas);
12894 
12895       return;
12896     }
12897   if (strcmp (name, "margin") == 0)
12898     {
12899       clutter_actor_set_margin (actor, g_value_get_boxed (value));
12900       return;
12901     }
12902 
12903   g_object_set_property (G_OBJECT (scriptable), name, value);
12904 }
12905 
12906 static void
clutter_scriptable_iface_init(ClutterScriptableIface * iface)12907 clutter_scriptable_iface_init (ClutterScriptableIface *iface)
12908 {
12909   iface->parse_custom_node = clutter_actor_parse_custom_node;
12910   iface->set_custom_property = clutter_actor_set_custom_property;
12911 }
12912 
12913 static gboolean
get_layout_from_animation_property(ClutterActor * actor,const gchar * name,gchar ** name_p)12914 get_layout_from_animation_property (ClutterActor  *actor,
12915                                     const gchar   *name,
12916                                     gchar        **name_p)
12917 {
12918   g_auto (GStrv) tokens = NULL;
12919 
12920   if (!g_str_has_prefix (name, "@layout"))
12921     return FALSE;
12922 
12923   tokens = g_strsplit (name, ".", -1);
12924   if (tokens == NULL || g_strv_length (tokens) != 2)
12925     {
12926       CLUTTER_NOTE (ANIMATION, "Invalid property name '%s'",
12927                     name + 1);
12928       return FALSE;
12929     }
12930 
12931   if (name_p != NULL)
12932     *name_p = g_strdup (tokens[1]);
12933 
12934   return TRUE;
12935 }
12936 
12937 static gboolean
get_content_from_animation_property(ClutterActor * actor,const gchar * name,gchar ** name_p)12938 get_content_from_animation_property (ClutterActor  *actor,
12939                                      const gchar   *name,
12940                                      gchar        **name_p)
12941 {
12942   g_auto (GStrv) tokens = NULL;
12943 
12944   if (!g_str_has_prefix (name, "@content"))
12945     return FALSE;
12946 
12947   if (!actor->priv->content)
12948     {
12949       CLUTTER_NOTE (ANIMATION, "No ClutterContent available for '%s'",
12950                     name + 1);
12951       return FALSE;
12952     }
12953 
12954   tokens = g_strsplit (name, ".", -1);
12955   if (tokens == NULL || g_strv_length (tokens) != 2)
12956     {
12957       CLUTTER_NOTE (ANIMATION, "Invalid property name '%s'",
12958                     name + 1);
12959       return FALSE;
12960     }
12961 
12962   if (name_p != NULL)
12963     *name_p = g_strdup (tokens[1]);
12964 
12965   return TRUE;
12966 }
12967 
12968 static ClutterActorMeta *
get_meta_from_animation_property(ClutterActor * actor,const gchar * name,gchar ** name_p)12969 get_meta_from_animation_property (ClutterActor  *actor,
12970                                   const gchar   *name,
12971                                   gchar        **name_p)
12972 {
12973   ClutterActorPrivate *priv = actor->priv;
12974   ClutterActorMeta *meta = NULL;
12975   gchar **tokens;
12976 
12977   /* if this is not a special property, fall through */
12978   if (name[0] != '@')
12979     return NULL;
12980 
12981   /* detect the properties named using the following spec:
12982    *
12983    *   @<section>.<meta-name>.<property-name>
12984    *
12985    * where <section> can be one of the following:
12986    *
12987    *   - actions
12988    *   - constraints
12989    *   - effects
12990    *
12991    * and <meta-name> is the name set on a specific ActorMeta
12992    */
12993 
12994   tokens = g_strsplit (name + 1, ".", -1);
12995   if (tokens == NULL || g_strv_length (tokens) != 3)
12996     {
12997       CLUTTER_NOTE (ANIMATION, "Invalid property name '%s'",
12998                     name + 1);
12999       g_strfreev (tokens);
13000       return NULL;
13001     }
13002 
13003   if (strcmp (tokens[0], "actions") == 0)
13004     meta = _clutter_meta_group_get_meta (priv->actions, tokens[1]);
13005 
13006   if (strcmp (tokens[0], "constraints") == 0)
13007     meta = _clutter_meta_group_get_meta (priv->constraints, tokens[1]);
13008 
13009   if (strcmp (tokens[0], "effects") == 0)
13010     meta = _clutter_meta_group_get_meta (priv->effects, tokens[1]);
13011 
13012   if (name_p != NULL)
13013     *name_p = g_strdup (tokens[2]);
13014 
13015   CLUTTER_NOTE (ANIMATION,
13016                 "Looking for property '%s' of object '%s' in section '%s'",
13017                 tokens[2],
13018                 tokens[1],
13019                 tokens[0]);
13020 
13021   g_strfreev (tokens);
13022 
13023   return meta;
13024 }
13025 
13026 static GParamSpec *
clutter_actor_find_property(ClutterAnimatable * animatable,const gchar * property_name)13027 clutter_actor_find_property (ClutterAnimatable *animatable,
13028                              const gchar       *property_name)
13029 {
13030   ClutterActor *actor = CLUTTER_ACTOR (animatable);
13031   ClutterActorMeta *meta = NULL;
13032   GObjectClass *klass = NULL;
13033   GParamSpec *pspec = NULL;
13034   gchar *p_name = NULL;
13035   gboolean use_content = FALSE;
13036   gboolean use_layout;
13037 
13038   use_layout = get_layout_from_animation_property (actor,
13039                                                    property_name,
13040                                                    &p_name);
13041 
13042   if (!use_layout)
13043     use_content = get_content_from_animation_property (actor,
13044                                                        property_name,
13045                                                        &p_name);
13046 
13047   if (!use_layout && !use_content)
13048     meta = get_meta_from_animation_property (actor,
13049                                              property_name,
13050                                              &p_name);
13051 
13052   if (meta != NULL)
13053     {
13054       klass = G_OBJECT_GET_CLASS (meta);
13055 
13056       pspec = g_object_class_find_property (klass, p_name);
13057     }
13058   else if (use_layout)
13059     {
13060       klass = G_OBJECT_GET_CLASS (actor->priv->layout_manager);
13061 
13062       pspec = g_object_class_find_property (klass, p_name);
13063     }
13064   else if (use_content)
13065     {
13066       klass = G_OBJECT_GET_CLASS (actor->priv->content);
13067 
13068       pspec = g_object_class_find_property (klass, p_name);
13069     }
13070   else
13071     {
13072       klass = G_OBJECT_GET_CLASS (animatable);
13073 
13074       pspec = g_object_class_find_property (klass, property_name);
13075     }
13076 
13077   g_free (p_name);
13078 
13079   return pspec;
13080 }
13081 
13082 static void
clutter_actor_get_initial_state(ClutterAnimatable * animatable,const gchar * property_name,GValue * initial)13083 clutter_actor_get_initial_state (ClutterAnimatable *animatable,
13084                                  const gchar       *property_name,
13085                                  GValue            *initial)
13086 {
13087   ClutterActor *actor = CLUTTER_ACTOR (animatable);
13088   ClutterActorMeta *meta = NULL;
13089   gchar *p_name = NULL;
13090   gboolean use_content = FALSE;
13091   gboolean use_layout;
13092 
13093   use_layout = get_layout_from_animation_property (actor,
13094                                                    property_name,
13095                                                    &p_name);
13096 
13097   if (!use_layout)
13098     use_content = get_content_from_animation_property (actor,
13099                                                        property_name,
13100                                                        &p_name);
13101 
13102   if (!use_layout && !use_content)
13103     meta = get_meta_from_animation_property (actor,
13104                                              property_name,
13105                                              &p_name);
13106 
13107   if (meta != NULL)
13108     g_object_get_property (G_OBJECT (meta), p_name, initial);
13109   else if (use_layout)
13110     g_object_get_property (G_OBJECT (actor->priv->layout_manager), p_name, initial);
13111   else if (use_content)
13112     g_object_get_property (G_OBJECT (actor->priv->content), p_name, initial);
13113   else
13114     g_object_get_property (G_OBJECT (animatable), property_name, initial);
13115 
13116   g_free (p_name);
13117 }
13118 
13119 /*
13120  * clutter_actor_set_animatable_property:
13121  * @actor: a #ClutterActor
13122  * @prop_id: the paramspec id
13123  * @value: the value to set
13124  * @pspec: the paramspec
13125  *
13126  * Sets values of animatable properties.
13127  *
13128  * This is a variant of clutter_actor_set_property() that gets called
13129  * by the #ClutterAnimatable implementation of #ClutterActor for the
13130  * properties with the %CLUTTER_PARAM_ANIMATABLE flag set on their
13131  * #GParamSpec.
13132  *
13133  * Unlike the implementation of #GObjectClass.set_property(), this
13134  * function will not update the interval if a transition involving an
13135  * animatable property is in progress - this avoids cycles with the
13136  * transition API calling the public API.
13137  */
13138 static void
clutter_actor_set_animatable_property(ClutterActor * actor,guint prop_id,const GValue * value,GParamSpec * pspec)13139 clutter_actor_set_animatable_property (ClutterActor *actor,
13140                                        guint         prop_id,
13141                                        const GValue *value,
13142                                        GParamSpec   *pspec)
13143 {
13144   GObject *obj = G_OBJECT (actor);
13145 
13146   g_object_freeze_notify (obj);
13147 
13148   switch (prop_id)
13149     {
13150     case PROP_X:
13151       clutter_actor_set_x_internal (actor, g_value_get_float (value));
13152       break;
13153 
13154     case PROP_Y:
13155       clutter_actor_set_y_internal (actor, g_value_get_float (value));
13156       break;
13157 
13158     case PROP_POSITION:
13159       clutter_actor_set_position_internal (actor, g_value_get_boxed (value));
13160       break;
13161 
13162     case PROP_WIDTH:
13163       clutter_actor_set_width_internal (actor, g_value_get_float (value));
13164       break;
13165 
13166     case PROP_HEIGHT:
13167       clutter_actor_set_height_internal (actor, g_value_get_float (value));
13168       break;
13169 
13170     case PROP_SIZE:
13171       clutter_actor_set_size_internal (actor, g_value_get_boxed (value));
13172       break;
13173 
13174     case PROP_ALLOCATION:
13175       clutter_actor_allocate_internal (actor, g_value_get_boxed (value));
13176       clutter_actor_queue_redraw (actor);
13177       break;
13178 
13179     case PROP_Z_POSITION:
13180       clutter_actor_set_z_position_internal (actor, g_value_get_float (value));
13181       break;
13182 
13183     case PROP_OPACITY:
13184       clutter_actor_set_opacity_internal (actor, g_value_get_uint (value));
13185       break;
13186 
13187     case PROP_BACKGROUND_COLOR:
13188       clutter_actor_set_background_color_internal (actor, clutter_value_get_color (value));
13189       break;
13190 
13191     case PROP_PIVOT_POINT:
13192       clutter_actor_set_pivot_point_internal (actor, g_value_get_boxed (value));
13193       break;
13194 
13195     case PROP_PIVOT_POINT_Z:
13196       clutter_actor_set_pivot_point_z_internal (actor, g_value_get_float (value));
13197       break;
13198 
13199     case PROP_TRANSLATION_X:
13200     case PROP_TRANSLATION_Y:
13201     case PROP_TRANSLATION_Z:
13202       clutter_actor_set_translation_internal (actor,
13203                                               g_value_get_float (value),
13204                                               pspec);
13205       break;
13206 
13207     case PROP_SCALE_X:
13208     case PROP_SCALE_Y:
13209     case PROP_SCALE_Z:
13210       clutter_actor_set_scale_factor_internal (actor,
13211                                                g_value_get_double (value),
13212                                                pspec);
13213       break;
13214 
13215     case PROP_ROTATION_ANGLE_X:
13216     case PROP_ROTATION_ANGLE_Y:
13217     case PROP_ROTATION_ANGLE_Z:
13218       clutter_actor_set_rotation_angle_internal (actor,
13219                                                  g_value_get_double (value),
13220                                                  pspec);
13221       break;
13222 
13223     case PROP_CONTENT_BOX:
13224       clutter_actor_store_content_box (actor, g_value_get_boxed (value));
13225       break;
13226 
13227     case PROP_MARGIN_TOP:
13228     case PROP_MARGIN_BOTTOM:
13229     case PROP_MARGIN_LEFT:
13230     case PROP_MARGIN_RIGHT:
13231       clutter_actor_set_margin_internal (actor, g_value_get_float (value),
13232                                          pspec);
13233       break;
13234 
13235     case PROP_TRANSFORM:
13236       clutter_actor_set_transform_internal (actor, g_value_get_boxed (value));
13237       break;
13238 
13239     case PROP_CHILD_TRANSFORM:
13240       clutter_actor_set_child_transform_internal (actor, g_value_get_boxed (value));
13241       break;
13242 
13243     default:
13244       g_object_set_property (obj, pspec->name, value);
13245       break;
13246     }
13247 
13248   g_object_thaw_notify (obj);
13249 }
13250 
13251 static void
clutter_actor_set_final_state(ClutterAnimatable * animatable,const gchar * property_name,const GValue * final)13252 clutter_actor_set_final_state (ClutterAnimatable *animatable,
13253                                const gchar       *property_name,
13254                                const GValue      *final)
13255 {
13256   ClutterActor *actor = CLUTTER_ACTOR (animatable);
13257   ClutterActorMeta *meta = NULL;
13258   gchar *p_name = NULL;
13259   gboolean use_content = FALSE;
13260   gboolean use_layout;
13261 
13262   use_layout = get_layout_from_animation_property (actor,
13263                                                    property_name,
13264                                                    &p_name);
13265 
13266   if (!use_layout)
13267     use_content = get_content_from_animation_property (actor,
13268                                                        property_name,
13269                                                        &p_name);
13270 
13271   if (!use_layout && !use_content)
13272     meta = get_meta_from_animation_property (actor,
13273                                              property_name,
13274                                              &p_name);
13275 
13276   if (meta != NULL)
13277     g_object_set_property (G_OBJECT (meta), p_name, final);
13278   else if (use_layout)
13279     g_object_set_property (G_OBJECT (actor->priv->layout_manager), p_name, final);
13280   else if (use_content)
13281     g_object_set_property (G_OBJECT (actor->priv->content), p_name, final);
13282   else
13283     {
13284       GObjectClass *obj_class = G_OBJECT_GET_CLASS (animatable);
13285       GParamSpec *pspec;
13286 
13287       pspec = g_object_class_find_property (obj_class, property_name);
13288 
13289       if (pspec != NULL)
13290         {
13291           if ((pspec->flags & CLUTTER_PARAM_ANIMATABLE) != 0)
13292             {
13293               /* XXX - I'm going to the special hell for this */
13294               clutter_actor_set_animatable_property (actor, pspec->param_id, final, pspec);
13295             }
13296           else
13297             g_object_set_property (G_OBJECT (animatable), pspec->name, final);
13298         }
13299     }
13300 
13301   g_free (p_name);
13302 }
13303 
13304 static ClutterActor *
clutter_actor_get_actor(ClutterAnimatable * animatable)13305 clutter_actor_get_actor (ClutterAnimatable *animatable)
13306 {
13307   return CLUTTER_ACTOR (animatable);
13308 }
13309 
13310 static void
clutter_animatable_iface_init(ClutterAnimatableInterface * iface)13311 clutter_animatable_iface_init (ClutterAnimatableInterface *iface)
13312 {
13313   iface->find_property = clutter_actor_find_property;
13314   iface->get_initial_state = clutter_actor_get_initial_state;
13315   iface->set_final_state = clutter_actor_set_final_state;
13316   iface->get_actor = clutter_actor_get_actor;
13317 }
13318 
13319 /**
13320  * clutter_actor_transform_stage_point:
13321  * @self: A #ClutterActor
13322  * @x: (in): x screen coordinate of the point to unproject
13323  * @y: (in): y screen coordinate of the point to unproject
13324  * @x_out: (out): return location for the unprojected x coordinance
13325  * @y_out: (out): return location for the unprojected y coordinance
13326  *
13327  * This function translates screen coordinates (@x, @y) to
13328  * coordinates relative to the actor. For example, it can be used to translate
13329  * screen events from global screen coordinates into actor-local coordinates.
13330  *
13331  * The conversion can fail, notably if the transform stack results in the
13332  * actor being projected on the screen as a mere line.
13333  *
13334  * The conversion should not be expected to be pixel-perfect due to the
13335  * nature of the operation. In general the error grows when the skewing
13336  * of the actor rectangle on screen increases.
13337  *
13338  * This function can be computationally intensive.
13339  *
13340  * This function only works when the allocation is up-to-date, i.e. inside of
13341  * the #ClutterActorClass.paint() implementation
13342  *
13343  * Return value: %TRUE if conversion was successful.
13344  *
13345  * Since: 0.6
13346  */
13347 gboolean
clutter_actor_transform_stage_point(ClutterActor * self,gfloat x,gfloat y,gfloat * x_out,gfloat * y_out)13348 clutter_actor_transform_stage_point (ClutterActor *self,
13349 				     gfloat        x,
13350 				     gfloat        y,
13351 				     gfloat       *x_out,
13352 				     gfloat       *y_out)
13353 {
13354   graphene_point3d_t v[4];
13355   double ST[3][3];
13356   double RQ[3][3];
13357   int du, dv;
13358   double px, py;
13359   double det;
13360   float xf, yf, wf;
13361   ClutterActorPrivate *priv;
13362 
13363   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
13364 
13365   priv = self->priv;
13366 
13367   /* This implementation is based on the quad -> quad projection algorithm
13368    * described by Paul Heckbert in:
13369    *
13370    *   http://www.cs.cmu.edu/~ph/texfund/texfund.pdf
13371    *
13372    * and the sample implementation at:
13373    *
13374    *   http://www.cs.cmu.edu/~ph/src/texfund/
13375    *
13376    * Our texture is a rectangle with origin [0, 0], so we are mapping from
13377    * quad to rectangle only, which significantly simplifies things.
13378    * Function calls have been unrolled.
13379    */
13380   clutter_actor_get_abs_allocation_vertices (self, v);
13381 
13382   /* Keeping these as ints simplifies the multiplication (no significant
13383    * loss of precision here).
13384    */
13385   du = ceilf (priv->allocation.x2 - priv->allocation.x1);
13386   dv = ceilf (priv->allocation.y2 - priv->allocation.y1);
13387 
13388   if (du == 0 || dv == 0)
13389     return FALSE;
13390 
13391 #define DET(a,b,c,d)    (((a) * (d)) - ((b) * (c)))
13392 
13393   /* First, find mapping from unit uv square to xy quadrilateral; this
13394    * equivalent to the pmap_square_quad() functions in the sample
13395    * implementation, which we can simplify, since our target is always
13396    * a rectangle.
13397    */
13398   px = v[0].x - v[1].x + v[3].x - v[2].x;
13399   py = v[0].y - v[1].y + v[3].y - v[2].y;
13400 
13401   if ((int) px == 0 && (int) py == 0)
13402     {
13403       /* affine transform */
13404       RQ[0][0] = v[1].x - v[0].x;
13405       RQ[1][0] = v[3].x - v[1].x;
13406       RQ[2][0] = v[0].x;
13407       RQ[0][1] = v[1].y - v[0].y;
13408       RQ[1][1] = v[3].y - v[1].y;
13409       RQ[2][1] = v[0].y;
13410       RQ[0][2] = 0.0;
13411       RQ[1][2] = 0.0;
13412       RQ[2][2] = 1.0;
13413     }
13414   else
13415     {
13416       /* projective transform */
13417       double dx1, dx2, dy1, dy2;
13418 
13419       dx1 = v[1].x - v[3].x;
13420       dx2 = v[2].x - v[3].x;
13421       dy1 = v[1].y - v[3].y;
13422       dy2 = v[2].y - v[3].y;
13423 
13424       det = DET (dx1, dx2, dy1, dy2);
13425       if (fabs (det) <= DBL_EPSILON)
13426         return FALSE;
13427 
13428       RQ[0][2] = DET (px, dx2, py, dy2) / det;
13429       RQ[1][2] = DET (dx1, px, dy1, py) / det;
13430       RQ[2][2] = 1.0;
13431       RQ[0][0] = v[1].x - v[0].x + (RQ[0][2] * v[1].x);
13432       RQ[1][0] = v[2].x - v[0].x + (RQ[1][2] * v[2].x);
13433       RQ[2][0] = v[0].x;
13434       RQ[0][1] = v[1].y - v[0].y + (RQ[0][2] * v[1].y);
13435       RQ[1][1] = v[2].y - v[0].y + (RQ[1][2] * v[2].y);
13436       RQ[2][1] = v[0].y;
13437     }
13438 
13439   /*
13440    * Now combine with transform from our rectangle (u0,v0,u1,v1) to unit
13441    * square. Since our rectangle is based at 0,0 we only need to scale.
13442    */
13443   RQ[0][0] /= du;
13444   RQ[1][0] /= dv;
13445   RQ[0][1] /= du;
13446   RQ[1][1] /= dv;
13447   RQ[0][2] /= du;
13448   RQ[1][2] /= dv;
13449 
13450   /*
13451    * Now RQ is transform from uv rectangle to xy quadrilateral; we need an
13452    * inverse of that.
13453    */
13454   ST[0][0] = DET (RQ[1][1], RQ[1][2], RQ[2][1], RQ[2][2]);
13455   ST[1][0] = DET (RQ[1][2], RQ[1][0], RQ[2][2], RQ[2][0]);
13456   ST[2][0] = DET (RQ[1][0], RQ[1][1], RQ[2][0], RQ[2][1]);
13457   ST[0][1] = DET (RQ[2][1], RQ[2][2], RQ[0][1], RQ[0][2]);
13458   ST[1][1] = DET (RQ[2][2], RQ[2][0], RQ[0][2], RQ[0][0]);
13459   ST[2][1] = DET (RQ[2][0], RQ[2][1], RQ[0][0], RQ[0][1]);
13460   ST[0][2] = DET (RQ[0][1], RQ[0][2], RQ[1][1], RQ[1][2]);
13461   ST[1][2] = DET (RQ[0][2], RQ[0][0], RQ[1][2], RQ[1][0]);
13462   ST[2][2] = DET (RQ[0][0], RQ[0][1], RQ[1][0], RQ[1][1]);
13463 
13464   /*
13465    * Check the resulting matrix is OK.
13466    */
13467   det = (RQ[0][0] * ST[0][0])
13468       + (RQ[0][1] * ST[0][1])
13469       + (RQ[0][2] * ST[0][2]);
13470   if (fabs (det) <= DBL_EPSILON)
13471     return FALSE;
13472 
13473   /*
13474    * Now transform our point with the ST matrix; the notional w
13475    * coordinate is 1, hence the last part is simply added.
13476    */
13477   xf = x * ST[0][0] + y * ST[1][0] + ST[2][0];
13478   yf = x * ST[0][1] + y * ST[1][1] + ST[2][1];
13479   wf = x * ST[0][2] + y * ST[1][2] + ST[2][2];
13480 
13481   if (x_out)
13482     *x_out = xf / wf;
13483 
13484   if (y_out)
13485     *y_out = yf / wf;
13486 
13487 #undef DET
13488 
13489   return TRUE;
13490 }
13491 
13492 /**
13493  * clutter_actor_is_rotated:
13494  * @self: a #ClutterActor
13495  *
13496  * Checks whether any rotation is applied to the actor.
13497  *
13498  * Return value: %TRUE if the actor is rotated.
13499  *
13500  * Since: 0.6
13501  */
13502 gboolean
clutter_actor_is_rotated(ClutterActor * self)13503 clutter_actor_is_rotated (ClutterActor *self)
13504 {
13505   const ClutterTransformInfo *info;
13506 
13507   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
13508 
13509   info = _clutter_actor_get_transform_info_or_defaults (self);
13510 
13511   if (info->rx_angle || info->ry_angle || info->rz_angle)
13512     return TRUE;
13513 
13514   return FALSE;
13515 }
13516 
13517 /**
13518  * clutter_actor_is_scaled:
13519  * @self: a #ClutterActor
13520  *
13521  * Checks whether the actor is scaled in either dimension.
13522  *
13523  * Return value: %TRUE if the actor is scaled.
13524  *
13525  * Since: 0.6
13526  */
13527 gboolean
clutter_actor_is_scaled(ClutterActor * self)13528 clutter_actor_is_scaled (ClutterActor *self)
13529 {
13530   const ClutterTransformInfo *info;
13531 
13532   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
13533 
13534   info = _clutter_actor_get_transform_info_or_defaults (self);
13535 
13536   if (info->scale_x != 1.0 || info->scale_y != 1.0)
13537     return TRUE;
13538 
13539   return FALSE;
13540 }
13541 
13542 ClutterActor *
_clutter_actor_get_stage_internal(ClutterActor * actor)13543 _clutter_actor_get_stage_internal (ClutterActor *actor)
13544 {
13545   while (actor && !CLUTTER_ACTOR_IS_TOPLEVEL (actor))
13546     actor = actor->priv->parent;
13547 
13548   return actor;
13549 }
13550 
13551 /**
13552  * clutter_actor_get_stage:
13553  * @actor: a #ClutterActor
13554  *
13555  * Retrieves the #ClutterStage where @actor is contained.
13556  *
13557  * Return value: (transfer none) (type Clutter.Stage): the stage
13558  *   containing the actor, or %NULL
13559  *
13560  * Since: 0.8
13561  */
13562 ClutterActor *
clutter_actor_get_stage(ClutterActor * actor)13563 clutter_actor_get_stage (ClutterActor *actor)
13564 {
13565   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
13566 
13567   return _clutter_actor_get_stage_internal (actor);
13568 }
13569 
13570 /**
13571  * clutter_actor_allocate_available_size:
13572  * @self: a #ClutterActor
13573  * @x: the actor's X coordinate
13574  * @y: the actor's Y coordinate
13575  * @available_width: the maximum available width, or -1 to use the
13576  *   actor's natural width
13577  * @available_height: the maximum available height, or -1 to use the
13578  *   actor's natural height
13579  *
13580  * Allocates @self taking into account the #ClutterActor's
13581  * preferred size, but limiting it to the maximum available width
13582  * and height provided.
13583  *
13584  * This function will do the right thing when dealing with the
13585  * actor's request mode.
13586  *
13587  * The implementation of this function is equivalent to:
13588  *
13589  * |[<!-- language="C" -->
13590  *   if (request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
13591  *     {
13592  *       clutter_actor_get_preferred_width (self, available_height,
13593  *                                          &min_width,
13594  *                                          &natural_width);
13595  *       width = CLAMP (natural_width, min_width, available_width);
13596  *
13597  *       clutter_actor_get_preferred_height (self, width,
13598  *                                           &min_height,
13599  *                                           &natural_height);
13600  *       height = CLAMP (natural_height, min_height, available_height);
13601  *     }
13602  *   else if (request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
13603  *     {
13604  *       clutter_actor_get_preferred_height (self, available_width,
13605  *                                           &min_height,
13606  *                                           &natural_height);
13607  *       height = CLAMP (natural_height, min_height, available_height);
13608  *
13609  *       clutter_actor_get_preferred_width (self, height,
13610  *                                          &min_width,
13611  *                                          &natural_width);
13612  *       width = CLAMP (natural_width, min_width, available_width);
13613  *     }
13614  *   else if (request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
13615  *     {
13616  *       clutter_content_get_preferred_size (content, &natural_width, &natural_height);
13617  *
13618  *       width = CLAMP (natural_width, 0, available_width);
13619  *       height = CLAMP (natural_height, 0, available_height);
13620  *     }
13621  *
13622  *   box.x1 = x; box.y1 = y;
13623  *   box.x2 = box.x1 + available_width;
13624  *   box.y2 = box.y1 + available_height;
13625  *   clutter_actor_allocate (self, &box);
13626  * ]|
13627  *
13628  * This function can be used by fluid layout managers to allocate
13629  * an actor's preferred size without making it bigger than the area
13630  * available for the container.
13631  *
13632  * Since: 1.0
13633  */
13634 void
clutter_actor_allocate_available_size(ClutterActor * self,gfloat x,gfloat y,gfloat available_width,gfloat available_height)13635 clutter_actor_allocate_available_size (ClutterActor           *self,
13636                                        gfloat                  x,
13637                                        gfloat                  y,
13638                                        gfloat                  available_width,
13639                                        gfloat                  available_height)
13640 {
13641   ClutterActorPrivate *priv;
13642   gfloat width, height;
13643   gfloat min_width, min_height;
13644   gfloat natural_width, natural_height;
13645   ClutterActorBox box;
13646 
13647   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13648 
13649   priv = self->priv;
13650 
13651   width = height = 0.0;
13652 
13653   switch (priv->request_mode)
13654     {
13655     case CLUTTER_REQUEST_HEIGHT_FOR_WIDTH:
13656       clutter_actor_get_preferred_width (self, available_height,
13657                                          &min_width,
13658                                          &natural_width);
13659       width  = CLAMP (natural_width, min_width, available_width);
13660 
13661       clutter_actor_get_preferred_height (self, width,
13662                                           &min_height,
13663                                           &natural_height);
13664       height = CLAMP (natural_height, min_height, available_height);
13665       break;
13666 
13667     case CLUTTER_REQUEST_WIDTH_FOR_HEIGHT:
13668       clutter_actor_get_preferred_height (self, available_width,
13669                                           &min_height,
13670                                           &natural_height);
13671       height = CLAMP (natural_height, min_height, available_height);
13672 
13673       clutter_actor_get_preferred_width (self, height,
13674                                          &min_width,
13675                                          &natural_width);
13676       width  = CLAMP (natural_width, min_width, available_width);
13677       break;
13678 
13679     case CLUTTER_REQUEST_CONTENT_SIZE:
13680       if (priv->content != NULL)
13681         {
13682           clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height);
13683 
13684           width = CLAMP (natural_width, 0, available_width);
13685           height = CLAMP (natural_height, 0, available_height);
13686         }
13687       break;
13688     }
13689 
13690 
13691   box.x1 = x;
13692   box.y1 = y;
13693   box.x2 = box.x1 + width;
13694   box.y2 = box.y1 + height;
13695   clutter_actor_allocate (self, &box);
13696 }
13697 
13698 /**
13699  * clutter_actor_allocate_preferred_size:
13700  * @self: a #ClutterActor
13701  * @x: the actor's X coordinate
13702  * @y: the actor's Y coordinate
13703  *
13704  * Allocates the natural size of @self.
13705  *
13706  * This function is a utility call for #ClutterActor implementations
13707  * that allocates the actor's preferred natural size. It can be used
13708  * by fixed layout managers (like #ClutterGroup or so called
13709  * 'composite actors') inside the ClutterActor::allocate
13710  * implementation to give each child exactly how much space it
13711  * requires, regardless of the size of the parent.
13712  *
13713  * This function is not meant to be used by applications. It is also
13714  * not meant to be used outside the implementation of the
13715  * #ClutterActorClass.allocate virtual function.
13716  *
13717  * Since: 0.8
13718  */
13719 void
clutter_actor_allocate_preferred_size(ClutterActor * self,float x,float y)13720 clutter_actor_allocate_preferred_size (ClutterActor *self,
13721                                        float         x,
13722                                        float         y)
13723 {
13724   gfloat natural_width, natural_height;
13725   ClutterActorBox actor_box;
13726 
13727   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13728 
13729   clutter_actor_get_preferred_size (self,
13730                                     NULL, NULL,
13731                                     &natural_width,
13732                                     &natural_height);
13733 
13734   actor_box.x1 = x;
13735   actor_box.y1 = y;
13736   actor_box.x2 = actor_box.x1 + natural_width;
13737   actor_box.y2 = actor_box.y1 + natural_height;
13738 
13739   clutter_actor_allocate (self, &actor_box);
13740 }
13741 
13742 /**
13743  * clutter_actor_allocate_align_fill:
13744  * @self: a #ClutterActor
13745  * @box: a #ClutterActorBox, containing the available width and height
13746  * @x_align: the horizontal alignment, between 0 and 1
13747  * @y_align: the vertical alignment, between 0 and 1
13748  * @x_fill: whether the actor should fill horizontally
13749  * @y_fill: whether the actor should fill vertically
13750  *
13751  * Allocates @self by taking into consideration the available allocation
13752  * area; an alignment factor on either axis; and whether the actor should
13753  * fill the allocation on either axis.
13754  *
13755  * The @box should contain the available allocation width and height;
13756  * if the x1 and y1 members of #ClutterActorBox are not set to 0, the
13757  * allocation will be offset by their value.
13758  *
13759  * This function takes into consideration the geometry request specified by
13760  * the #ClutterActor:request-mode property, and the text direction.
13761  *
13762  * This function is useful for fluid layout managers using legacy alignment
13763  * flags. Newly written layout managers should use the #ClutterActor:x-align
13764  * and #ClutterActor:y-align properties, instead, and just call
13765  * clutter_actor_allocate() inside their #ClutterActorClass.allocate()
13766  * implementation.
13767  *
13768  * Since: 1.4
13769  */
13770 void
clutter_actor_allocate_align_fill(ClutterActor * self,const ClutterActorBox * box,gdouble x_align,gdouble y_align,gboolean x_fill,gboolean y_fill)13771 clutter_actor_allocate_align_fill (ClutterActor           *self,
13772                                    const ClutterActorBox  *box,
13773                                    gdouble                 x_align,
13774                                    gdouble                 y_align,
13775                                    gboolean                x_fill,
13776                                    gboolean                y_fill)
13777 {
13778   ClutterActorPrivate *priv;
13779   ClutterActorBox allocation = CLUTTER_ACTOR_BOX_INIT_ZERO;
13780   gfloat x_offset, y_offset;
13781   gfloat available_width, available_height;
13782   gfloat child_width = 0.f, child_height = 0.f;
13783 
13784   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13785   g_return_if_fail (box != NULL);
13786   g_return_if_fail (x_align >= 0.0 && x_align <= 1.0);
13787   g_return_if_fail (y_align >= 0.0 && y_align <= 1.0);
13788 
13789   priv = self->priv;
13790 
13791   clutter_actor_box_get_origin (box, &x_offset, &y_offset);
13792   clutter_actor_box_get_size (box, &available_width, &available_height);
13793 
13794   if (available_width <= 0)
13795     available_width = 0.f;
13796 
13797   if (available_height <= 0)
13798     available_height = 0.f;
13799 
13800   allocation.x1 = x_offset;
13801   allocation.y1 = y_offset;
13802 
13803   if (available_width == 0.f && available_height == 0.f)
13804     goto out;
13805 
13806   if (x_fill)
13807     child_width = available_width;
13808 
13809   if (y_fill)
13810     child_height = available_height;
13811 
13812   /* if we are filling horizontally and vertically then we're done */
13813   if (x_fill && y_fill)
13814     goto out;
13815 
13816   if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
13817     {
13818       gfloat min_width, natural_width;
13819       gfloat min_height, natural_height;
13820 
13821       if (!x_fill)
13822         {
13823           clutter_actor_get_preferred_width (self, available_height,
13824                                              &min_width,
13825                                              &natural_width);
13826 
13827           child_width = CLAMP (natural_width, min_width, available_width);
13828         }
13829 
13830       if (!y_fill)
13831         {
13832           clutter_actor_get_preferred_height (self, child_width,
13833                                               &min_height,
13834                                               &natural_height);
13835 
13836           child_height = CLAMP (natural_height, min_height, available_height);
13837         }
13838     }
13839   else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
13840     {
13841       gfloat min_width, natural_width;
13842       gfloat min_height, natural_height;
13843 
13844       if (!y_fill)
13845         {
13846           clutter_actor_get_preferred_height (self, available_width,
13847                                               &min_height,
13848                                               &natural_height);
13849 
13850           child_height = CLAMP (natural_height, min_height, available_height);
13851         }
13852 
13853       if (!x_fill)
13854         {
13855           clutter_actor_get_preferred_width (self, child_height,
13856                                              &min_width,
13857                                              &natural_width);
13858 
13859           child_width = CLAMP (natural_width, min_width, available_width);
13860         }
13861     }
13862   else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL)
13863     {
13864       gfloat natural_width, natural_height;
13865 
13866       clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height);
13867 
13868       if (!x_fill)
13869         child_width = CLAMP (natural_width, 0, available_width);
13870 
13871       if (!y_fill)
13872         child_height = CLAMP (natural_height, 0, available_height);
13873     }
13874 
13875   /* invert the horizontal alignment for RTL languages */
13876   if (priv->text_direction == CLUTTER_TEXT_DIRECTION_RTL)
13877     x_align = 1.0 - x_align;
13878 
13879   if (!x_fill)
13880     allocation.x1 += ((available_width - child_width) * x_align);
13881 
13882   if (!y_fill)
13883     allocation.y1 += ((available_height - child_height) * y_align);
13884 
13885 out:
13886 
13887   allocation.x1 = floorf (allocation.x1);
13888   allocation.y1 = floorf (allocation.y1);
13889   allocation.x2 = ceilf (allocation.x1 + MAX (child_width, 0));
13890   allocation.y2 = ceilf (allocation.y1 + MAX (child_height, 0));
13891 
13892   clutter_actor_allocate (self, &allocation);
13893 }
13894 
13895 /**
13896  * clutter_actor_grab_key_focus:
13897  * @self: a #ClutterActor
13898  *
13899  * Sets the key focus of the #ClutterStage including @self
13900  * to this #ClutterActor.
13901  *
13902  * Since: 1.0
13903  */
13904 void
clutter_actor_grab_key_focus(ClutterActor * self)13905 clutter_actor_grab_key_focus (ClutterActor *self)
13906 {
13907   ClutterActor *stage;
13908 
13909   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13910 
13911   if (self->priv->has_key_focus)
13912     return;
13913 
13914   stage = _clutter_actor_get_stage_internal (self);
13915   if (stage != NULL)
13916     clutter_stage_set_key_focus (CLUTTER_STAGE (stage), self);
13917 }
13918 
13919 static void
update_pango_context(ClutterBackend * backend,PangoContext * context)13920 update_pango_context (ClutterBackend *backend,
13921                       PangoContext   *context)
13922 {
13923   ClutterSettings *settings;
13924   PangoFontDescription *font_desc;
13925   const cairo_font_options_t *font_options;
13926   gchar *font_name;
13927   PangoDirection pango_dir;
13928   gdouble resolution;
13929 
13930   settings = clutter_settings_get_default ();
13931 
13932   /* update the text direction */
13933   if (clutter_get_default_text_direction () == CLUTTER_TEXT_DIRECTION_RTL)
13934     pango_dir = PANGO_DIRECTION_RTL;
13935   else
13936     pango_dir = PANGO_DIRECTION_LTR;
13937 
13938   pango_context_set_base_dir (context, pango_dir);
13939 
13940   g_object_get (settings, "font-name", &font_name, NULL);
13941 
13942   /* get the configuration for the PangoContext from the backend */
13943   font_options = clutter_backend_get_font_options (backend);
13944   resolution = clutter_backend_get_resolution (backend);
13945 
13946   font_desc = pango_font_description_from_string (font_name);
13947 
13948   if (resolution < 0)
13949     resolution = 96.0; /* fall back */
13950 
13951   pango_context_set_font_description (context, font_desc);
13952   pango_cairo_context_set_font_options (context, font_options);
13953   pango_cairo_context_set_resolution (context, resolution);
13954 
13955   pango_font_description_free (font_desc);
13956   g_free (font_name);
13957 }
13958 
13959 /**
13960  * clutter_actor_get_pango_context:
13961  * @self: a #ClutterActor
13962  *
13963  * Retrieves the #PangoContext for @self. The actor's #PangoContext
13964  * is already configured using the appropriate font map, resolution
13965  * and font options.
13966  *
13967  * Unlike clutter_actor_create_pango_context(), this context is owend
13968  * by the #ClutterActor and it will be updated each time the options
13969  * stored by the #ClutterBackend change.
13970  *
13971  * You can use the returned #PangoContext to create a #PangoLayout
13972  * and render text using cogl_pango_show_layout() to reuse the
13973  * glyphs cache also used by Clutter.
13974  *
13975  * Return value: (transfer none): the #PangoContext for a #ClutterActor.
13976  *   The returned #PangoContext is owned by the actor and should not be
13977  *   unreferenced by the application code
13978  *
13979  * Since: 1.0
13980  */
13981 PangoContext *
clutter_actor_get_pango_context(ClutterActor * self)13982 clutter_actor_get_pango_context (ClutterActor *self)
13983 {
13984   ClutterActorPrivate *priv;
13985   ClutterBackend *backend = clutter_get_default_backend ();
13986 
13987   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
13988 
13989   priv = self->priv;
13990 
13991   if (G_UNLIKELY (priv->pango_context == NULL))
13992     {
13993       priv->pango_context = clutter_actor_create_pango_context (self);
13994 
13995       priv->resolution_changed_id =
13996         g_signal_connect_object (backend, "resolution-changed",
13997                                  G_CALLBACK (update_pango_context), priv->pango_context, 0);
13998       priv->font_changed_id =
13999         g_signal_connect_object (backend, "font-changed",
14000                                  G_CALLBACK (update_pango_context), priv->pango_context, 0);
14001     }
14002   else
14003     update_pango_context (backend, priv->pango_context);
14004 
14005   return priv->pango_context;
14006 }
14007 
14008 /**
14009  * clutter_actor_create_pango_context:
14010  * @self: a #ClutterActor
14011  *
14012  * Creates a #PangoContext for the given actor. The #PangoContext
14013  * is already configured using the appropriate font map, resolution
14014  * and font options.
14015  *
14016  * See also clutter_actor_get_pango_context().
14017  *
14018  * Return value: (transfer full): the newly created #PangoContext.
14019  *   Use g_object_unref() on the returned value to deallocate its
14020  *   resources
14021  *
14022  * Since: 1.0
14023  */
14024 PangoContext *
clutter_actor_create_pango_context(ClutterActor * self)14025 clutter_actor_create_pango_context (ClutterActor *self)
14026 {
14027   CoglPangoFontMap *font_map;
14028   PangoContext *context;
14029 
14030   font_map = COGL_PANGO_FONT_MAP (clutter_get_font_map ());
14031 
14032   context = cogl_pango_font_map_create_context (font_map);
14033   update_pango_context (clutter_get_default_backend (), context);
14034   pango_context_set_language (context, pango_language_get_default ());
14035 
14036   return context;
14037 }
14038 
14039 /**
14040  * clutter_actor_create_pango_layout:
14041  * @self: a #ClutterActor
14042  * @text: (allow-none): the text to set on the #PangoLayout, or %NULL
14043  *
14044  * Creates a new #PangoLayout from the same #PangoContext used
14045  * by the #ClutterActor. The #PangoLayout is already configured
14046  * with the font map, resolution and font options, and the
14047  * given @text.
14048  *
14049  * If you want to keep around a #PangoLayout created by this
14050  * function you will have to connect to the #ClutterBackend::font-changed
14051  * and #ClutterBackend::resolution-changed signals, and call
14052  * pango_layout_context_changed() in response to them.
14053  *
14054  * Return value: (transfer full): the newly created #PangoLayout.
14055  *   Use g_object_unref() when done
14056  *
14057  * Since: 1.0
14058  */
14059 PangoLayout *
clutter_actor_create_pango_layout(ClutterActor * self,const gchar * text)14060 clutter_actor_create_pango_layout (ClutterActor *self,
14061                                    const gchar  *text)
14062 {
14063   PangoContext *context;
14064   PangoLayout *layout;
14065 
14066   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
14067 
14068   context = clutter_actor_get_pango_context (self);
14069   layout = pango_layout_new (context);
14070 
14071   if (text)
14072     pango_layout_set_text (layout, text, -1);
14073 
14074   return layout;
14075 }
14076 
14077 /**
14078  * clutter_actor_set_opacity_override:
14079  * @self: a #ClutterActor
14080  * @opacity: the override opacity value, or -1 to reset
14081  *
14082  * Allows overriding the calculated paint opacity (as returned by
14083  * clutter_actor_get_paint_opacity()). This is used internally by
14084  * ClutterClone and ClutterOffscreenEffect, and should be used by
14085  * actors that need to mimic those.
14086  *
14087  * In almost all cases this should not used by applications.
14088  *
14089  * Stability: unstable
14090  */
14091 void
clutter_actor_set_opacity_override(ClutterActor * self,gint opacity)14092 clutter_actor_set_opacity_override (ClutterActor *self,
14093                                      gint          opacity)
14094 {
14095   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14096 
14097   /* ensure bounds */
14098   if (opacity >= 0)
14099     opacity = CLAMP (opacity, 0, 255);
14100   else
14101     opacity = -1;
14102 
14103   self->priv->opacity_override = opacity;
14104 }
14105 
14106 /**
14107  * clutter_actor_get_opacity_override:
14108  * @self: a #ClutterActor
14109  *
14110  * See clutter_actor_set_opacity_override()
14111  *
14112  * Returns: the override value for the actor's opacity, or -1 if no override
14113  *   is set.
14114  *
14115  * Since: 1.22
14116  *
14117  * Stability: unstable
14118  */
14119 gint
clutter_actor_get_opacity_override(ClutterActor * self)14120 clutter_actor_get_opacity_override (ClutterActor *self)
14121 {
14122   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), -1);
14123 
14124   return self->priv->opacity_override;
14125 }
14126 
14127 /**
14128  * clutter_actor_inhibit_culling:
14129  * @actor: a #ClutterActor
14130  *
14131  * Increases the culling inhibitor counter. Inhibiting culling
14132  * forces the actor to be painted even when outside the visible
14133  * bounds of the stage view.
14134  *
14135  * This is usually necessary when an actor is being painted on
14136  * another paint context.
14137  *
14138  * Pair with clutter_actor_uninhibit_culling() when the actor doesn't
14139  * need to be painted anymore.
14140  */
14141 void
clutter_actor_inhibit_culling(ClutterActor * actor)14142 clutter_actor_inhibit_culling (ClutterActor *actor)
14143 {
14144   ClutterActorPrivate *priv;
14145 
14146   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
14147 
14148   priv = actor->priv;
14149 
14150   priv->inhibit_culling_counter++;
14151   _clutter_actor_set_enable_paint_unmapped (actor, TRUE);
14152 }
14153 
14154 /**
14155  * clutter_actor_uninhibit_culling:
14156  * @actor: a #ClutterActor
14157  *
14158  * Decreases the culling inhibitor counter. See clutter_actor_inhibit_culling()
14159  * for when inhibit culling is necessary.
14160  *
14161  * Calling this function without a matching call to
14162  * clutter_actor_inhibit_culling() is a programming error.
14163  */
14164 void
clutter_actor_uninhibit_culling(ClutterActor * actor)14165 clutter_actor_uninhibit_culling (ClutterActor *actor)
14166 {
14167   ClutterActorPrivate *priv;
14168 
14169   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
14170 
14171   priv = actor->priv;
14172 
14173   if (priv->inhibit_culling_counter == 0)
14174     {
14175       g_critical ("Unpaired call to clutter_actor_uninhibit_culling");
14176       return;
14177     }
14178 
14179   priv->inhibit_culling_counter--;
14180   if (priv->inhibit_culling_counter == 0)
14181     _clutter_actor_set_enable_paint_unmapped (actor, FALSE);
14182 }
14183 
14184 /* Allows you to disable applying the actors model view transform during
14185  * a paint. Used by ClutterClone. */
14186 void
_clutter_actor_set_enable_model_view_transform(ClutterActor * self,gboolean enable)14187 _clutter_actor_set_enable_model_view_transform (ClutterActor *self,
14188                                                 gboolean      enable)
14189 {
14190   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14191 
14192   self->priv->enable_model_view_transform = enable;
14193 }
14194 
14195 void
_clutter_actor_set_enable_paint_unmapped(ClutterActor * self,gboolean enable)14196 _clutter_actor_set_enable_paint_unmapped (ClutterActor *self,
14197                                           gboolean      enable)
14198 {
14199   ClutterActorPrivate *priv;
14200 
14201   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14202 
14203   priv = self->priv;
14204 
14205   if (priv->enable_paint_unmapped == enable)
14206     return;
14207 
14208   priv->enable_paint_unmapped = enable;
14209 
14210   if (enable)
14211     {
14212       push_in_paint_unmapped_branch (self, 1);
14213 
14214       /* Make sure that the parents of the widget are realized first;
14215        * otherwise checks in clutter_actor_update_map_state() will
14216        * fail.
14217        */
14218       clutter_actor_realize (self);
14219 
14220       /* If the actor isn't ultimately connected to a toplevel, it can't be
14221        * realized or painted.
14222        */
14223       if (CLUTTER_ACTOR_IS_REALIZED (self))
14224           clutter_actor_update_map_state (self, MAP_STATE_MAKE_MAPPED);
14225     }
14226   else
14227     {
14228       clutter_actor_update_map_state (self, MAP_STATE_CHECK);
14229       pop_in_paint_unmapped_branch (self, 1);
14230     }
14231 }
14232 
14233 /**
14234  * clutter_actor_get_flags:
14235  * @self: a #ClutterActor
14236  *
14237  * Retrieves the flags set on @self
14238  *
14239  * Return value: a bitwise or of #ClutterActorFlags or 0
14240  *
14241  * Since: 1.0
14242  */
14243 ClutterActorFlags
clutter_actor_get_flags(ClutterActor * self)14244 clutter_actor_get_flags (ClutterActor *self)
14245 {
14246   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
14247 
14248   return self->flags;
14249 }
14250 
14251 /**
14252  * clutter_actor_set_flags:
14253  * @self: a #ClutterActor
14254  * @flags: the flags to set
14255  *
14256  * Sets @flags on @self
14257  *
14258  * This function will emit notifications for the changed properties
14259  *
14260  * Since: 1.0
14261  */
14262 void
clutter_actor_set_flags(ClutterActor * self,ClutterActorFlags flags)14263 clutter_actor_set_flags (ClutterActor      *self,
14264                          ClutterActorFlags  flags)
14265 {
14266   ClutterActorFlags old_flags;
14267   GObject *obj;
14268   gboolean was_reactive_set, reactive_set;
14269   gboolean was_realized_set, realized_set;
14270   gboolean was_mapped_set, mapped_set;
14271   gboolean was_visible_set, visible_set;
14272 
14273   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14274 
14275   if (self->flags == flags)
14276     return;
14277 
14278   obj = G_OBJECT (self);
14279   g_object_ref (obj);
14280   g_object_freeze_notify (obj);
14281 
14282   old_flags = self->flags;
14283 
14284   was_reactive_set = ((old_flags & CLUTTER_ACTOR_REACTIVE) != 0);
14285   was_realized_set = ((old_flags & CLUTTER_ACTOR_REALIZED) != 0);
14286   was_mapped_set   = ((old_flags & CLUTTER_ACTOR_MAPPED)   != 0);
14287   was_visible_set  = ((old_flags & CLUTTER_ACTOR_VISIBLE)  != 0);
14288 
14289   self->flags |= flags;
14290 
14291   reactive_set = ((self->flags & CLUTTER_ACTOR_REACTIVE) != 0);
14292   realized_set = ((self->flags & CLUTTER_ACTOR_REALIZED) != 0);
14293   mapped_set   = ((self->flags & CLUTTER_ACTOR_MAPPED)   != 0);
14294   visible_set  = ((self->flags & CLUTTER_ACTOR_VISIBLE)  != 0);
14295 
14296   if (reactive_set != was_reactive_set)
14297     g_object_notify_by_pspec (obj, obj_props[PROP_REACTIVE]);
14298 
14299   if (realized_set != was_realized_set)
14300     g_object_notify_by_pspec (obj, obj_props[PROP_REALIZED]);
14301 
14302   if (mapped_set != was_mapped_set)
14303     g_object_notify_by_pspec (obj, obj_props[PROP_MAPPED]);
14304 
14305   if (visible_set != was_visible_set)
14306     g_object_notify_by_pspec (obj, obj_props[PROP_VISIBLE]);
14307 
14308   g_object_thaw_notify (obj);
14309   g_object_unref (obj);
14310 }
14311 
14312 /**
14313  * clutter_actor_unset_flags:
14314  * @self: a #ClutterActor
14315  * @flags: the flags to unset
14316  *
14317  * Unsets @flags on @self
14318  *
14319  * This function will emit notifications for the changed properties
14320  *
14321  * Since: 1.0
14322  */
14323 void
clutter_actor_unset_flags(ClutterActor * self,ClutterActorFlags flags)14324 clutter_actor_unset_flags (ClutterActor      *self,
14325                            ClutterActorFlags  flags)
14326 {
14327   ClutterActorFlags old_flags;
14328   GObject *obj;
14329   gboolean was_reactive_set, reactive_set;
14330   gboolean was_realized_set, realized_set;
14331   gboolean was_mapped_set, mapped_set;
14332   gboolean was_visible_set, visible_set;
14333 
14334   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14335 
14336   obj = G_OBJECT (self);
14337   g_object_freeze_notify (obj);
14338 
14339   old_flags = self->flags;
14340 
14341   was_reactive_set = ((old_flags & CLUTTER_ACTOR_REACTIVE) != 0);
14342   was_realized_set = ((old_flags & CLUTTER_ACTOR_REALIZED) != 0);
14343   was_mapped_set   = ((old_flags & CLUTTER_ACTOR_MAPPED)   != 0);
14344   was_visible_set  = ((old_flags & CLUTTER_ACTOR_VISIBLE)  != 0);
14345 
14346   self->flags &= ~flags;
14347 
14348   if (self->flags == old_flags)
14349     return;
14350 
14351   reactive_set = ((self->flags & CLUTTER_ACTOR_REACTIVE) != 0);
14352   realized_set = ((self->flags & CLUTTER_ACTOR_REALIZED) != 0);
14353   mapped_set   = ((self->flags & CLUTTER_ACTOR_MAPPED)   != 0);
14354   visible_set  = ((self->flags & CLUTTER_ACTOR_VISIBLE)  != 0);
14355 
14356   if (reactive_set != was_reactive_set)
14357     g_object_notify_by_pspec (obj, obj_props[PROP_REACTIVE]);
14358 
14359   if (realized_set != was_realized_set)
14360     g_object_notify_by_pspec (obj, obj_props[PROP_REALIZED]);
14361 
14362   if (mapped_set != was_mapped_set)
14363     g_object_notify_by_pspec (obj, obj_props[PROP_MAPPED]);
14364 
14365   if (visible_set != was_visible_set)
14366     g_object_notify_by_pspec (obj, obj_props[PROP_VISIBLE]);
14367 
14368   g_object_thaw_notify (obj);
14369 }
14370 
14371 static void
clutter_actor_set_transform_internal(ClutterActor * self,const graphene_matrix_t * transform)14372 clutter_actor_set_transform_internal (ClutterActor            *self,
14373                                       const graphene_matrix_t *transform)
14374 {
14375   ClutterTransformInfo *info;
14376   gboolean was_set;
14377   GObject *obj;
14378 
14379   obj = G_OBJECT (self);
14380 
14381   info = _clutter_actor_get_transform_info (self);
14382 
14383   was_set = info->transform_set;
14384 
14385   info->transform = *transform;
14386   info->transform_set = !graphene_matrix_is_identity (&info->transform);
14387 
14388   transform_changed (self);
14389 
14390   clutter_actor_queue_redraw (self);
14391 
14392   g_object_notify_by_pspec (obj, obj_props[PROP_TRANSFORM]);
14393 
14394   if (was_set != info->transform_set)
14395     g_object_notify_by_pspec (obj, obj_props[PROP_TRANSFORM_SET]);
14396 }
14397 
14398 /**
14399  * clutter_actor_set_transform:
14400  * @self: a #ClutterActor
14401  * @transform: (allow-none): a #graphene_matrix_t, or %NULL to
14402  *   unset a custom transformation
14403  *
14404  * Overrides the transformations of a #ClutterActor with a custom
14405  * matrix, which will be applied relative to the origin of the
14406  * actor's allocation and to the actor's pivot point.
14407  *
14408  * The #ClutterActor:transform property is animatable.
14409  *
14410  * Since: 1.12
14411  */
14412 void
clutter_actor_set_transform(ClutterActor * self,const graphene_matrix_t * transform)14413 clutter_actor_set_transform (ClutterActor            *self,
14414                              const graphene_matrix_t *transform)
14415 {
14416   const ClutterTransformInfo *info;
14417   graphene_matrix_t new_transform;
14418 
14419   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14420 
14421   info = _clutter_actor_get_transform_info_or_defaults (self);
14422 
14423   if (transform != NULL)
14424     graphene_matrix_init_from_matrix (&new_transform, transform);
14425   else
14426     graphene_matrix_init_identity (&new_transform);
14427 
14428   _clutter_actor_create_transition (self, obj_props[PROP_TRANSFORM],
14429                                     &info->transform,
14430                                     &new_transform);
14431 }
14432 
14433 /**
14434  * clutter_actor_get_transform:
14435  * @self: a #ClutterActor
14436  * @transform: (out caller-allocates): a #graphene_matrix_t
14437  *
14438  * Retrieves the current transformation matrix of a #ClutterActor.
14439  *
14440  * Since: 1.12
14441  */
14442 void
clutter_actor_get_transform(ClutterActor * self,graphene_matrix_t * transform)14443 clutter_actor_get_transform (ClutterActor      *self,
14444                              graphene_matrix_t *transform)
14445 {
14446   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14447   g_return_if_fail (transform != NULL);
14448 
14449   graphene_matrix_init_identity (transform);
14450   _clutter_actor_apply_modelview_transform (self, transform);
14451 }
14452 
14453 void
_clutter_actor_set_in_clone_paint(ClutterActor * self,gboolean is_in_clone_paint)14454 _clutter_actor_set_in_clone_paint (ClutterActor *self,
14455                                    gboolean      is_in_clone_paint)
14456 {
14457   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14458   self->priv->in_clone_paint = is_in_clone_paint;
14459 }
14460 
14461 /**
14462  * clutter_actor_is_in_clone_paint:
14463  * @self: a #ClutterActor
14464  *
14465  * Checks whether @self is being currently painted by a #ClutterClone
14466  *
14467  * This function is useful only inside implementations of the
14468  * #ClutterActorClass.paint() virtual function.
14469  *
14470  * This function should not be used by applications
14471  *
14472  * Return value: %TRUE if the #ClutterActor is currently being painted
14473  *   by a #ClutterClone, and %FALSE otherwise
14474  *
14475  * Since: 1.0
14476  */
14477 gboolean
clutter_actor_is_in_clone_paint(ClutterActor * self)14478 clutter_actor_is_in_clone_paint (ClutterActor *self)
14479 {
14480   ClutterActor *parent;
14481 
14482   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
14483 
14484   if (self->priv->in_clone_paint)
14485     return TRUE;
14486 
14487   if (self->priv->in_cloned_branch == 0)
14488     return FALSE;
14489 
14490   parent = self->priv->parent;
14491   while (parent != NULL)
14492     {
14493       if (parent->priv->in_cloned_branch == 0)
14494         break;
14495 
14496       if (parent->priv->in_clone_paint)
14497         return TRUE;
14498 
14499       parent = parent->priv->parent;
14500     }
14501 
14502   return FALSE;
14503 }
14504 
14505 gboolean
clutter_actor_is_painting_unmapped(ClutterActor * self)14506 clutter_actor_is_painting_unmapped (ClutterActor *self)
14507 {
14508   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
14509 
14510   return self->priv->unmapped_paint_branch_counter > 0;
14511 }
14512 
14513 gboolean
clutter_actor_has_damage(ClutterActor * actor)14514 clutter_actor_has_damage (ClutterActor *actor)
14515 {
14516   return actor->priv->is_dirty;
14517 }
14518 
14519 static gboolean
set_direction_recursive(ClutterActor * actor,gpointer user_data)14520 set_direction_recursive (ClutterActor *actor,
14521                          gpointer      user_data)
14522 {
14523   ClutterTextDirection text_dir = GPOINTER_TO_INT (user_data);
14524 
14525   clutter_actor_set_text_direction (actor, text_dir);
14526 
14527   return TRUE;
14528 }
14529 
14530 /**
14531  * clutter_actor_set_text_direction:
14532  * @self: a #ClutterActor
14533  * @text_dir: the text direction for @self
14534  *
14535  * Sets the #ClutterTextDirection for an actor
14536  *
14537  * The passed text direction must not be %CLUTTER_TEXT_DIRECTION_DEFAULT
14538  *
14539  * If @self implements #ClutterContainer then this function will recurse
14540  * inside all the children of @self (including the internal ones).
14541  *
14542  * Composite actors not implementing #ClutterContainer, or actors requiring
14543  * special handling when the text direction changes, should connect to
14544  * the #GObject::notify signal for the #ClutterActor:text-direction property
14545  *
14546  * Since: 1.2
14547  */
14548 void
clutter_actor_set_text_direction(ClutterActor * self,ClutterTextDirection text_dir)14549 clutter_actor_set_text_direction (ClutterActor         *self,
14550                                   ClutterTextDirection  text_dir)
14551 {
14552   ClutterActorPrivate *priv;
14553 
14554   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14555   g_return_if_fail (text_dir != CLUTTER_TEXT_DIRECTION_DEFAULT);
14556 
14557   priv = self->priv;
14558 
14559   if (priv->text_direction != text_dir)
14560     {
14561       priv->text_direction = text_dir;
14562 
14563       /* we need to emit the notify::text-direction first, so that
14564        * the sub-classes can catch that and do specific handling of
14565        * the text direction; see clutter_text_direction_changed_cb()
14566        * inside clutter-text.c
14567        */
14568       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT_DIRECTION]);
14569 
14570       _clutter_actor_foreach_child (self, set_direction_recursive,
14571                                     GINT_TO_POINTER (text_dir));
14572 
14573       clutter_actor_queue_relayout (self);
14574     }
14575 }
14576 
14577 void
_clutter_actor_set_has_pointer(ClutterActor * self,gboolean has_pointer)14578 _clutter_actor_set_has_pointer (ClutterActor *self,
14579                                 gboolean      has_pointer)
14580 {
14581   ClutterActorPrivate *priv = self->priv;
14582 
14583   if (priv->has_pointer != has_pointer)
14584     {
14585       priv->has_pointer = has_pointer;
14586 
14587       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_POINTER]);
14588     }
14589 }
14590 
14591 void
_clutter_actor_set_has_key_focus(ClutterActor * self,gboolean has_key_focus)14592 _clutter_actor_set_has_key_focus (ClutterActor *self,
14593                                   gboolean      has_key_focus)
14594 {
14595   ClutterActorPrivate *priv = self->priv;
14596 
14597   if (priv->has_key_focus != has_key_focus)
14598     {
14599       priv->has_key_focus = has_key_focus;
14600 
14601       if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
14602         return;
14603 
14604       if (has_key_focus)
14605         g_signal_emit (self, actor_signals[KEY_FOCUS_IN], 0);
14606       else
14607         g_signal_emit (self, actor_signals[KEY_FOCUS_OUT], 0);
14608     }
14609 }
14610 
14611 /**
14612  * clutter_actor_get_text_direction:
14613  * @self: a #ClutterActor
14614  *
14615  * Retrieves the value set using clutter_actor_set_text_direction()
14616  *
14617  * If no text direction has been previously set, the default text
14618  * direction, as returned by clutter_get_default_text_direction(), will
14619  * be returned instead
14620  *
14621  * Return value: the #ClutterTextDirection for the actor
14622  *
14623  * Since: 1.2
14624  */
14625 ClutterTextDirection
clutter_actor_get_text_direction(ClutterActor * self)14626 clutter_actor_get_text_direction (ClutterActor *self)
14627 {
14628   ClutterActorPrivate *priv;
14629 
14630   g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
14631                         CLUTTER_TEXT_DIRECTION_LTR);
14632 
14633   priv = self->priv;
14634 
14635   /* if no direction has been set yet use the default */
14636   if (priv->text_direction == CLUTTER_TEXT_DIRECTION_DEFAULT)
14637     priv->text_direction = clutter_get_default_text_direction ();
14638 
14639   return priv->text_direction;
14640 }
14641 
14642 /**
14643  * clutter_actor_has_pointer:
14644  * @self: a #ClutterActor
14645  *
14646  * Checks whether an actor contains the pointer of a
14647  * #ClutterInputDevice
14648  *
14649  * Return value: %TRUE if the actor contains the pointer, and
14650  *   %FALSE otherwise
14651  *
14652  * Since: 1.2
14653  */
14654 gboolean
clutter_actor_has_pointer(ClutterActor * self)14655 clutter_actor_has_pointer (ClutterActor *self)
14656 {
14657   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
14658 
14659   return self->priv->has_pointer;
14660 }
14661 
14662 /**
14663  * clutter_actor_has_allocation:
14664  * @self: a #ClutterActor
14665  *
14666  * Checks if the actor has an up-to-date allocation assigned to
14667  * it. This means that the actor should have an allocation: it's
14668  * visible and has a parent. It also means that there is no
14669  * outstanding relayout request in progress for the actor or its
14670  * children (There might be other outstanding layout requests in
14671  * progress that will cause the actor to get a new allocation
14672  * when the stage is laid out, however).
14673  *
14674  * If this function returns %FALSE, then the actor will normally
14675  * be allocated before it is next drawn on the screen.
14676  *
14677  * Return value: %TRUE if the actor has an up-to-date allocation
14678  *
14679  * Since: 1.4
14680  */
14681 gboolean
clutter_actor_has_allocation(ClutterActor * self)14682 clutter_actor_has_allocation (ClutterActor *self)
14683 {
14684   ClutterActorPrivate *priv;
14685 
14686   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
14687 
14688   priv = self->priv;
14689 
14690   return priv->parent != NULL &&
14691          CLUTTER_ACTOR_IS_VISIBLE (self) &&
14692          !priv->needs_allocation;
14693 }
14694 
14695 /**
14696  * clutter_actor_add_action:
14697  * @self: a #ClutterActor
14698  * @action: a #ClutterAction
14699  *
14700  * Adds @action to the list of actions applied to @self
14701  *
14702  * A #ClutterAction can only belong to one actor at a time
14703  *
14704  * The #ClutterActor will hold a reference on @action until either
14705  * clutter_actor_remove_action() or clutter_actor_clear_actions()
14706  * is called
14707  *
14708  * Since: 1.4
14709  */
14710 void
clutter_actor_add_action(ClutterActor * self,ClutterAction * action)14711 clutter_actor_add_action (ClutterActor  *self,
14712                           ClutterAction *action)
14713 {
14714   ClutterActorPrivate *priv;
14715 
14716   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14717   g_return_if_fail (CLUTTER_IS_ACTION (action));
14718 
14719   priv = self->priv;
14720 
14721   if (priv->actions == NULL)
14722     {
14723       priv->actions = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
14724       priv->actions->actor = self;
14725     }
14726 
14727   _clutter_meta_group_add_meta (priv->actions, CLUTTER_ACTOR_META (action));
14728 
14729   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
14730 }
14731 
14732 /**
14733  * clutter_actor_add_action_with_name:
14734  * @self: a #ClutterActor
14735  * @name: the name to set on the action
14736  * @action: a #ClutterAction
14737  *
14738  * A convenience function for setting the name of a #ClutterAction
14739  * while adding it to the list of actions applied to @self
14740  *
14741  * This function is the logical equivalent of:
14742  *
14743  * |[<!-- language="C" -->
14744  *   clutter_actor_meta_set_name (CLUTTER_ACTOR_META (action), name);
14745  *   clutter_actor_add_action (self, action);
14746  * ]|
14747  *
14748  * Since: 1.4
14749  */
14750 void
clutter_actor_add_action_with_name(ClutterActor * self,const gchar * name,ClutterAction * action)14751 clutter_actor_add_action_with_name (ClutterActor  *self,
14752                                     const gchar   *name,
14753                                     ClutterAction *action)
14754 {
14755   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14756   g_return_if_fail (name != NULL);
14757   g_return_if_fail (CLUTTER_IS_ACTION (action));
14758 
14759   clutter_actor_meta_set_name (CLUTTER_ACTOR_META (action), name);
14760   clutter_actor_add_action (self, action);
14761 }
14762 
14763 /**
14764  * clutter_actor_remove_action:
14765  * @self: a #ClutterActor
14766  * @action: a #ClutterAction
14767  *
14768  * Removes @action from the list of actions applied to @self
14769  *
14770  * The reference held by @self on the #ClutterAction will be released
14771  *
14772  * Since: 1.4
14773  */
14774 void
clutter_actor_remove_action(ClutterActor * self,ClutterAction * action)14775 clutter_actor_remove_action (ClutterActor  *self,
14776                              ClutterAction *action)
14777 {
14778   ClutterActorPrivate *priv;
14779 
14780   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14781   g_return_if_fail (CLUTTER_IS_ACTION (action));
14782 
14783   priv = self->priv;
14784 
14785   if (priv->actions == NULL)
14786     return;
14787 
14788   _clutter_meta_group_remove_meta (priv->actions, CLUTTER_ACTOR_META (action));
14789 
14790   if (_clutter_meta_group_peek_metas (priv->actions) == NULL)
14791     g_clear_object (&priv->actions);
14792 
14793   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
14794 }
14795 
14796 /**
14797  * clutter_actor_remove_action_by_name:
14798  * @self: a #ClutterActor
14799  * @name: the name of the action to remove
14800  *
14801  * Removes the #ClutterAction with the given name from the list
14802  * of actions applied to @self
14803  *
14804  * Since: 1.4
14805  */
14806 void
clutter_actor_remove_action_by_name(ClutterActor * self,const gchar * name)14807 clutter_actor_remove_action_by_name (ClutterActor *self,
14808                                      const gchar  *name)
14809 {
14810   ClutterActorPrivate *priv;
14811   ClutterActorMeta *meta;
14812 
14813   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14814   g_return_if_fail (name != NULL);
14815 
14816   priv = self->priv;
14817 
14818   if (priv->actions == NULL)
14819     return;
14820 
14821   meta = _clutter_meta_group_get_meta (priv->actions, name);
14822   if (meta == NULL)
14823     return;
14824 
14825   _clutter_meta_group_remove_meta (priv->actions, meta);
14826 
14827   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
14828 }
14829 
14830 /**
14831  * clutter_actor_get_actions:
14832  * @self: a #ClutterActor
14833  *
14834  * Retrieves the list of actions applied to @self
14835  *
14836  * Return value: (transfer container) (element-type Clutter.Action): a copy
14837  *   of the list of #ClutterAction<!-- -->s. The contents of the list are
14838  *   owned by the #ClutterActor. Use g_list_free() to free the resources
14839  *   allocated by the returned #GList
14840  *
14841  * Since: 1.4
14842  */
14843 GList *
clutter_actor_get_actions(ClutterActor * self)14844 clutter_actor_get_actions (ClutterActor *self)
14845 {
14846   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
14847 
14848   if (self->priv->actions == NULL)
14849     return NULL;
14850 
14851   return _clutter_meta_group_get_metas_no_internal (self->priv->actions);
14852 }
14853 
14854 /**
14855  * clutter_actor_get_action:
14856  * @self: a #ClutterActor
14857  * @name: the name of the action to retrieve
14858  *
14859  * Retrieves the #ClutterAction with the given name in the list
14860  * of actions applied to @self
14861  *
14862  * Return value: (transfer none): a #ClutterAction for the given
14863  *   name, or %NULL. The returned #ClutterAction is owned by the
14864  *   actor and it should not be unreferenced directly
14865  *
14866  * Since: 1.4
14867  */
14868 ClutterAction *
clutter_actor_get_action(ClutterActor * self,const gchar * name)14869 clutter_actor_get_action (ClutterActor *self,
14870                           const gchar  *name)
14871 {
14872   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
14873   g_return_val_if_fail (name != NULL, NULL);
14874 
14875   if (self->priv->actions == NULL)
14876     return NULL;
14877 
14878   return CLUTTER_ACTION (_clutter_meta_group_get_meta (self->priv->actions, name));
14879 }
14880 
14881 /**
14882  * clutter_actor_clear_actions:
14883  * @self: a #ClutterActor
14884  *
14885  * Clears the list of actions applied to @self
14886  *
14887  * Since: 1.4
14888  */
14889 void
clutter_actor_clear_actions(ClutterActor * self)14890 clutter_actor_clear_actions (ClutterActor *self)
14891 {
14892   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14893 
14894   if (self->priv->actions == NULL)
14895     return;
14896 
14897   _clutter_meta_group_clear_metas_no_internal (self->priv->actions);
14898 }
14899 
14900 /**
14901  * clutter_actor_add_constraint:
14902  * @self: a #ClutterActor
14903  * @constraint: a #ClutterConstraint
14904  *
14905  * Adds @constraint to the list of #ClutterConstraint<!-- -->s applied
14906  * to @self
14907  *
14908  * The #ClutterActor will hold a reference on the @constraint until
14909  * either clutter_actor_remove_constraint() or
14910  * clutter_actor_clear_constraints() is called.
14911  *
14912  * Since: 1.4
14913  */
14914 void
clutter_actor_add_constraint(ClutterActor * self,ClutterConstraint * constraint)14915 clutter_actor_add_constraint (ClutterActor      *self,
14916                               ClutterConstraint *constraint)
14917 {
14918   ClutterActorPrivate *priv;
14919 
14920   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14921   g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
14922 
14923   priv = self->priv;
14924 
14925   if (priv->constraints == NULL)
14926     {
14927       priv->constraints = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
14928       priv->constraints->actor = self;
14929     }
14930 
14931   _clutter_meta_group_add_meta (priv->constraints,
14932                                 CLUTTER_ACTOR_META (constraint));
14933   clutter_actor_queue_relayout (self);
14934 
14935   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]);
14936 }
14937 
14938 /**
14939  * clutter_actor_add_constraint_with_name:
14940  * @self: a #ClutterActor
14941  * @name: the name to set on the constraint
14942  * @constraint: a #ClutterConstraint
14943  *
14944  * A convenience function for setting the name of a #ClutterConstraint
14945  * while adding it to the list of constraints applied to @self
14946  *
14947  * This function is the logical equivalent of:
14948  *
14949  * |[<!-- language="C" -->
14950  *   clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), name);
14951  *   clutter_actor_add_constraint (self, constraint);
14952  * ]|
14953  *
14954  * Since: 1.4
14955  */
14956 void
clutter_actor_add_constraint_with_name(ClutterActor * self,const gchar * name,ClutterConstraint * constraint)14957 clutter_actor_add_constraint_with_name (ClutterActor      *self,
14958                                         const gchar       *name,
14959                                         ClutterConstraint *constraint)
14960 {
14961   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14962   g_return_if_fail (name != NULL);
14963   g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
14964 
14965   clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), name);
14966   clutter_actor_add_constraint (self, constraint);
14967 }
14968 
14969 /**
14970  * clutter_actor_remove_constraint:
14971  * @self: a #ClutterActor
14972  * @constraint: a #ClutterConstraint
14973  *
14974  * Removes @constraint from the list of constraints applied to @self
14975  *
14976  * The reference held by @self on the #ClutterConstraint will be released
14977  *
14978  * Since: 1.4
14979  */
14980 void
clutter_actor_remove_constraint(ClutterActor * self,ClutterConstraint * constraint)14981 clutter_actor_remove_constraint (ClutterActor      *self,
14982                                  ClutterConstraint *constraint)
14983 {
14984   ClutterActorPrivate *priv;
14985 
14986   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14987   g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
14988 
14989   priv = self->priv;
14990 
14991   if (priv->constraints == NULL)
14992     return;
14993 
14994   _clutter_meta_group_remove_meta (priv->constraints,
14995                                    CLUTTER_ACTOR_META (constraint));
14996 
14997   if (_clutter_meta_group_peek_metas (priv->constraints) == NULL)
14998     g_clear_object (&priv->constraints);
14999 
15000   clutter_actor_queue_relayout (self);
15001 
15002   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]);
15003 }
15004 
15005 /**
15006  * clutter_actor_remove_constraint_by_name:
15007  * @self: a #ClutterActor
15008  * @name: the name of the constraint to remove
15009  *
15010  * Removes the #ClutterConstraint with the given name from the list
15011  * of constraints applied to @self
15012  *
15013  * Since: 1.4
15014  */
15015 void
clutter_actor_remove_constraint_by_name(ClutterActor * self,const gchar * name)15016 clutter_actor_remove_constraint_by_name (ClutterActor *self,
15017                                          const gchar  *name)
15018 {
15019   ClutterActorPrivate *priv;
15020   ClutterActorMeta *meta;
15021 
15022   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15023   g_return_if_fail (name != NULL);
15024 
15025   priv = self->priv;
15026 
15027   if (priv->constraints == NULL)
15028     return;
15029 
15030   meta = _clutter_meta_group_get_meta (priv->constraints, name);
15031   if (meta == NULL)
15032     return;
15033 
15034   _clutter_meta_group_remove_meta (priv->constraints, meta);
15035   clutter_actor_queue_relayout (self);
15036 }
15037 
15038 /**
15039  * clutter_actor_get_constraints:
15040  * @self: a #ClutterActor
15041  *
15042  * Retrieves the list of constraints applied to @self
15043  *
15044  * Return value: (transfer container) (element-type Clutter.Constraint): a copy
15045  *   of the list of #ClutterConstraint<!-- -->s. The contents of the list are
15046  *   owned by the #ClutterActor. Use g_list_free() to free the resources
15047  *   allocated by the returned #GList
15048  *
15049  * Since: 1.4
15050  */
15051 GList *
clutter_actor_get_constraints(ClutterActor * self)15052 clutter_actor_get_constraints (ClutterActor *self)
15053 {
15054   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15055 
15056   if (self->priv->constraints == NULL)
15057     return NULL;
15058 
15059   return _clutter_meta_group_get_metas_no_internal (self->priv->constraints);
15060 }
15061 
15062 /**
15063  * clutter_actor_get_constraint:
15064  * @self: a #ClutterActor
15065  * @name: the name of the constraint to retrieve
15066  *
15067  * Retrieves the #ClutterConstraint with the given name in the list
15068  * of constraints applied to @self
15069  *
15070  * Return value: (transfer none): a #ClutterConstraint for the given
15071  *   name, or %NULL. The returned #ClutterConstraint is owned by the
15072  *   actor and it should not be unreferenced directly
15073  *
15074  * Since: 1.4
15075  */
15076 ClutterConstraint *
clutter_actor_get_constraint(ClutterActor * self,const gchar * name)15077 clutter_actor_get_constraint (ClutterActor *self,
15078                               const gchar  *name)
15079 {
15080   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15081   g_return_val_if_fail (name != NULL, NULL);
15082 
15083   if (self->priv->constraints == NULL)
15084     return NULL;
15085 
15086   return CLUTTER_CONSTRAINT (_clutter_meta_group_get_meta (self->priv->constraints, name));
15087 }
15088 
15089 /**
15090  * clutter_actor_clear_constraints:
15091  * @self: a #ClutterActor
15092  *
15093  * Clears the list of constraints applied to @self
15094  *
15095  * Since: 1.4
15096  */
15097 void
clutter_actor_clear_constraints(ClutterActor * self)15098 clutter_actor_clear_constraints (ClutterActor *self)
15099 {
15100   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15101 
15102   if (self->priv->constraints == NULL)
15103     return;
15104 
15105   _clutter_meta_group_clear_metas_no_internal (self->priv->constraints);
15106 
15107   clutter_actor_queue_relayout (self);
15108 }
15109 
15110 /**
15111  * clutter_actor_set_clip_to_allocation:
15112  * @self: a #ClutterActor
15113  * @clip_set: %TRUE to apply a clip tracking the allocation
15114  *
15115  * Sets whether @self should be clipped to the same size as its
15116  * allocation
15117  *
15118  * Since: 1.4
15119  */
15120 void
clutter_actor_set_clip_to_allocation(ClutterActor * self,gboolean clip_set)15121 clutter_actor_set_clip_to_allocation (ClutterActor *self,
15122                                       gboolean      clip_set)
15123 {
15124   ClutterActorPrivate *priv;
15125 
15126   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15127 
15128   clip_set = !!clip_set;
15129 
15130   priv = self->priv;
15131 
15132   if (priv->clip_to_allocation != clip_set)
15133     {
15134       priv->clip_to_allocation = clip_set;
15135 
15136       queue_update_paint_volume (self);
15137       clutter_actor_queue_redraw (self);
15138 
15139       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CLIP_TO_ALLOCATION]);
15140       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]);
15141     }
15142 }
15143 
15144 /**
15145  * clutter_actor_get_clip_to_allocation:
15146  * @self: a #ClutterActor
15147  *
15148  * Retrieves the value set using clutter_actor_set_clip_to_allocation()
15149  *
15150  * Return value: %TRUE if the #ClutterActor is clipped to its allocation
15151  *
15152  * Since: 1.4
15153  */
15154 gboolean
clutter_actor_get_clip_to_allocation(ClutterActor * self)15155 clutter_actor_get_clip_to_allocation (ClutterActor *self)
15156 {
15157   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15158 
15159   return self->priv->clip_to_allocation;
15160 }
15161 
15162 /**
15163  * clutter_actor_add_effect:
15164  * @self: a #ClutterActor
15165  * @effect: a #ClutterEffect
15166  *
15167  * Adds @effect to the list of #ClutterEffect<!-- -->s applied to @self
15168  *
15169  * The #ClutterActor will hold a reference on the @effect until either
15170  * clutter_actor_remove_effect() or clutter_actor_clear_effects() is
15171  * called.
15172  *
15173  * Since: 1.4
15174  */
15175 void
clutter_actor_add_effect(ClutterActor * self,ClutterEffect * effect)15176 clutter_actor_add_effect (ClutterActor  *self,
15177                           ClutterEffect *effect)
15178 {
15179   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15180   g_return_if_fail (CLUTTER_IS_EFFECT (effect));
15181 
15182   _clutter_actor_add_effect_internal (self, effect);
15183 
15184   clutter_actor_queue_redraw (self);
15185 
15186   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EFFECT]);
15187 }
15188 
15189 /**
15190  * clutter_actor_add_effect_with_name:
15191  * @self: a #ClutterActor
15192  * @name: the name to set on the effect
15193  * @effect: a #ClutterEffect
15194  *
15195  * A convenience function for setting the name of a #ClutterEffect
15196  * while adding it to the list of effectss applied to @self
15197  *
15198  * This function is the logical equivalent of:
15199  *
15200  * |[<!-- language="C" -->
15201  *   clutter_actor_meta_set_name (CLUTTER_ACTOR_META (effect), name);
15202  *   clutter_actor_add_effect (self, effect);
15203  * ]|
15204  *
15205  * Since: 1.4
15206  */
15207 void
clutter_actor_add_effect_with_name(ClutterActor * self,const gchar * name,ClutterEffect * effect)15208 clutter_actor_add_effect_with_name (ClutterActor  *self,
15209                                     const gchar   *name,
15210                                     ClutterEffect *effect)
15211 {
15212   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15213   g_return_if_fail (name != NULL);
15214   g_return_if_fail (CLUTTER_IS_EFFECT (effect));
15215 
15216   clutter_actor_meta_set_name (CLUTTER_ACTOR_META (effect), name);
15217   clutter_actor_add_effect (self, effect);
15218 }
15219 
15220 /**
15221  * clutter_actor_remove_effect:
15222  * @self: a #ClutterActor
15223  * @effect: a #ClutterEffect
15224  *
15225  * Removes @effect from the list of effects applied to @self
15226  *
15227  * The reference held by @self on the #ClutterEffect will be released
15228  *
15229  * Since: 1.4
15230  */
15231 void
clutter_actor_remove_effect(ClutterActor * self,ClutterEffect * effect)15232 clutter_actor_remove_effect (ClutterActor  *self,
15233                              ClutterEffect *effect)
15234 {
15235   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15236   g_return_if_fail (CLUTTER_IS_EFFECT (effect));
15237 
15238   _clutter_actor_remove_effect_internal (self, effect);
15239 
15240   clutter_actor_queue_redraw (self);
15241 
15242   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EFFECT]);
15243 }
15244 
15245 /**
15246  * clutter_actor_remove_effect_by_name:
15247  * @self: a #ClutterActor
15248  * @name: the name of the effect to remove
15249  *
15250  * Removes the #ClutterEffect with the given name from the list
15251  * of effects applied to @self
15252  *
15253  * Since: 1.4
15254  */
15255 void
clutter_actor_remove_effect_by_name(ClutterActor * self,const gchar * name)15256 clutter_actor_remove_effect_by_name (ClutterActor *self,
15257                                      const gchar  *name)
15258 {
15259   ClutterActorPrivate *priv;
15260   ClutterActorMeta *meta;
15261 
15262   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15263   g_return_if_fail (name != NULL);
15264 
15265   priv = self->priv;
15266 
15267   if (priv->effects == NULL)
15268     return;
15269 
15270   meta = _clutter_meta_group_get_meta (priv->effects, name);
15271   if (meta == NULL)
15272     return;
15273 
15274   clutter_actor_remove_effect (self, CLUTTER_EFFECT (meta));
15275 }
15276 
15277 /**
15278  * clutter_actor_get_effects:
15279  * @self: a #ClutterActor
15280  *
15281  * Retrieves the #ClutterEffect<!-- -->s applied on @self, if any
15282  *
15283  * Return value: (transfer container) (element-type Clutter.Effect): a list
15284  *   of #ClutterEffect<!-- -->s, or %NULL. The elements of the returned
15285  *   list are owned by Clutter and they should not be freed. You should
15286  *   free the returned list using g_list_free() when done
15287  *
15288  * Since: 1.4
15289  */
15290 GList *
clutter_actor_get_effects(ClutterActor * self)15291 clutter_actor_get_effects (ClutterActor *self)
15292 {
15293   ClutterActorPrivate *priv;
15294 
15295   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15296 
15297   priv = self->priv;
15298 
15299   if (priv->effects == NULL)
15300     return NULL;
15301 
15302   return _clutter_meta_group_get_metas_no_internal (priv->effects);
15303 }
15304 
15305 /**
15306  * clutter_actor_get_effect:
15307  * @self: a #ClutterActor
15308  * @name: the name of the effect to retrieve
15309  *
15310  * Retrieves the #ClutterEffect with the given name in the list
15311  * of effects applied to @self
15312  *
15313  * Return value: (transfer none): a #ClutterEffect for the given
15314  *   name, or %NULL. The returned #ClutterEffect is owned by the
15315  *   actor and it should not be unreferenced directly
15316  *
15317  * Since: 1.4
15318  */
15319 ClutterEffect *
clutter_actor_get_effect(ClutterActor * self,const gchar * name)15320 clutter_actor_get_effect (ClutterActor *self,
15321                           const gchar  *name)
15322 {
15323   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15324   g_return_val_if_fail (name != NULL, NULL);
15325 
15326   if (self->priv->effects == NULL)
15327     return NULL;
15328 
15329   return CLUTTER_EFFECT (_clutter_meta_group_get_meta (self->priv->effects, name));
15330 }
15331 
15332 /**
15333  * clutter_actor_clear_effects:
15334  * @self: a #ClutterActor
15335  *
15336  * Clears the list of effects applied to @self
15337  *
15338  * Since: 1.4
15339  */
15340 void
clutter_actor_clear_effects(ClutterActor * self)15341 clutter_actor_clear_effects (ClutterActor *self)
15342 {
15343   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15344 
15345   if (self->priv->effects == NULL)
15346     return;
15347 
15348   _clutter_meta_group_clear_metas_no_internal (self->priv->effects);
15349 
15350   clutter_actor_queue_redraw (self);
15351 }
15352 
15353 /**
15354  * clutter_actor_has_key_focus:
15355  * @self: a #ClutterActor
15356  *
15357  * Checks whether @self is the #ClutterActor that has key focus
15358  *
15359  * Return value: %TRUE if the actor has key focus, and %FALSE otherwise
15360  *
15361  * Since: 1.4
15362  */
15363 gboolean
clutter_actor_has_key_focus(ClutterActor * self)15364 clutter_actor_has_key_focus (ClutterActor *self)
15365 {
15366   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15367 
15368   return self->priv->has_key_focus;
15369 }
15370 
15371 static gboolean
_clutter_actor_get_paint_volume_real(ClutterActor * self,ClutterPaintVolume * pv)15372 _clutter_actor_get_paint_volume_real (ClutterActor *self,
15373                                       ClutterPaintVolume *pv)
15374 {
15375   ClutterActorPrivate *priv = self->priv;
15376 
15377   /* Actors are only expected to report a valid paint volume
15378    * while they have a valid allocation. */
15379   if (G_UNLIKELY (priv->needs_allocation))
15380     {
15381       CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
15382                     "Actor needs allocation",
15383                     _clutter_actor_get_debug_name (self));
15384       return FALSE;
15385     }
15386 
15387   _clutter_paint_volume_init_static (pv, self);
15388 
15389   if (!CLUTTER_ACTOR_GET_CLASS (self)->get_paint_volume (self, pv))
15390     {
15391       clutter_paint_volume_free (pv);
15392       CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
15393                     "Actor failed to report a volume",
15394                     _clutter_actor_get_debug_name (self));
15395       return FALSE;
15396     }
15397 
15398   /* since effects can modify the paint volume, we allow them to actually
15399    * do this by making get_paint_volume() "context sensitive"
15400    */
15401   if (priv->effects != NULL)
15402     {
15403       if (priv->current_effect != NULL)
15404         {
15405           const GList *effects, *l;
15406 
15407           /* if we are being called from within the paint sequence of
15408            * an actor, get the paint volume up to the current effect
15409            */
15410           effects = _clutter_meta_group_peek_metas (priv->effects);
15411           for (l = effects;
15412                l != NULL && l->data != priv->current_effect;
15413                l = l->next)
15414             {
15415               if (!_clutter_effect_modify_paint_volume (l->data, pv))
15416                 {
15417                   clutter_paint_volume_free (pv);
15418                   CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
15419                                 "Effect (%s) failed to report a volume",
15420                                 _clutter_actor_get_debug_name (self),
15421                                 _clutter_actor_meta_get_debug_name (l->data));
15422                   return FALSE;
15423                 }
15424             }
15425         }
15426       else
15427         {
15428           const GList *effects, *l;
15429 
15430           /* otherwise, get the cumulative volume */
15431           effects = _clutter_meta_group_peek_metas (priv->effects);
15432           for (l = effects; l != NULL; l = l->next)
15433             if (!_clutter_effect_modify_paint_volume (l->data, pv))
15434               {
15435                 clutter_paint_volume_free (pv);
15436                 CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
15437                               "Effect (%s) failed to report a volume",
15438                               _clutter_actor_get_debug_name (self),
15439                               _clutter_actor_meta_get_debug_name (l->data));
15440                 return FALSE;
15441               }
15442         }
15443     }
15444 
15445   return TRUE;
15446 }
15447 
15448 static gboolean
_clutter_actor_has_active_paint_volume_override_effects(ClutterActor * self)15449 _clutter_actor_has_active_paint_volume_override_effects (ClutterActor *self)
15450 {
15451   const GList *l;
15452 
15453   if (self->priv->effects == NULL)
15454     return FALSE;
15455 
15456   /* We just need to all effects current effect to see
15457    * if anyone wants to override the paint volume. If so, then
15458    * we need to recompute, since the paint volume returned can
15459    * change from call to call. */
15460   for (l = _clutter_meta_group_peek_metas (self->priv->effects);
15461        l != NULL;
15462        l = l->next)
15463     {
15464       ClutterEffect *effect = l->data;
15465 
15466       if (clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)) &&
15467           _clutter_effect_has_custom_paint_volume (effect))
15468         return TRUE;
15469     }
15470 
15471   return FALSE;
15472 }
15473 
15474 /* The public clutter_actor_get_paint_volume API returns a const
15475  * pointer since we return a pointer directly to the cached
15476  * PaintVolume associated with the actor and don't want the user to
15477  * inadvertently modify it, but for internal uses we sometimes need
15478  * access to the same PaintVolume but need to apply some book-keeping
15479  * modifications to it so we don't want a const pointer.
15480  */
15481 static ClutterPaintVolume *
_clutter_actor_get_paint_volume_mutable(ClutterActor * self)15482 _clutter_actor_get_paint_volume_mutable (ClutterActor *self)
15483 {
15484   gboolean has_paint_volume_override_effects;
15485   ClutterActorPrivate *priv;
15486 
15487   priv = self->priv;
15488 
15489   has_paint_volume_override_effects = _clutter_actor_has_active_paint_volume_override_effects (self);
15490 
15491   if (priv->paint_volume_valid)
15492     {
15493       /* If effects are applied, the actor paint volume
15494        * needs to be recomputed on each paint, since those
15495        * paint volumes could change over the duration of the
15496        * effect.
15497        *
15498        * We also need to update the paint volume if we went
15499        * from having effects to not having effects on the last
15500        * paint volume update. */
15501       if (!priv->needs_paint_volume_update &&
15502           priv->current_effect == NULL &&
15503           !has_paint_volume_override_effects &&
15504           !priv->had_effects_on_last_paint_volume_update)
15505         return &priv->paint_volume;
15506       clutter_paint_volume_free (&priv->paint_volume);
15507     }
15508 
15509   priv->had_effects_on_last_paint_volume_update = has_paint_volume_override_effects;
15510 
15511   if (_clutter_actor_get_paint_volume_real (self, &priv->paint_volume))
15512     {
15513       priv->paint_volume_valid = TRUE;
15514       priv->needs_paint_volume_update = FALSE;
15515       return &priv->paint_volume;
15516     }
15517   else
15518     {
15519       priv->paint_volume_valid = FALSE;
15520       return NULL;
15521     }
15522 }
15523 
15524 /**
15525  * clutter_actor_get_paint_volume:
15526  * @self: a #ClutterActor
15527  *
15528  * Retrieves the paint volume of the passed #ClutterActor, or %NULL
15529  * when a paint volume can't be determined.
15530  *
15531  * The paint volume is defined as the 3D space occupied by an actor
15532  * when being painted.
15533  *
15534  * This function will call the #ClutterActorClass.get_paint_volume()
15535  * virtual function of the #ClutterActor class. Sub-classes of #ClutterActor
15536  * should not usually care about overriding the default implementation,
15537  * unless they are, for instance: painting outside their allocation, or
15538  * actors with a depth factor (not in terms of #ClutterActor:depth but real
15539  * 3D depth).
15540  *
15541  * Note: 2D actors overriding #ClutterActorClass.get_paint_volume()
15542  * should ensure that their volume has a depth of 0. (This will be true
15543  * as long as you don't call clutter_paint_volume_set_depth().)
15544  *
15545  * Return value: (transfer none): a pointer to a #ClutterPaintVolume,
15546  *   or %NULL if no volume could be determined. The returned pointer
15547  *   is not guaranteed to be valid across multiple frames; if you want
15548  *   to keep it, you will need to copy it using clutter_paint_volume_copy().
15549  *
15550  * Since: 1.6
15551  */
15552 const ClutterPaintVolume *
clutter_actor_get_paint_volume(ClutterActor * self)15553 clutter_actor_get_paint_volume (ClutterActor *self)
15554 {
15555   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15556 
15557   return _clutter_actor_get_paint_volume_mutable (self);
15558 }
15559 
15560 /**
15561  * clutter_actor_get_transformed_paint_volume:
15562  * @self: a #ClutterActor
15563  * @relative_to_ancestor: A #ClutterActor that is an ancestor of @self
15564  *    (or %NULL for the stage)
15565  *
15566  * Retrieves the 3D paint volume of an actor like
15567  * clutter_actor_get_paint_volume() does (Please refer to the
15568  * documentation of clutter_actor_get_paint_volume() for more
15569  * details.) and it additionally transforms the paint volume into the
15570  * coordinate space of @relative_to_ancestor. (Or the stage if %NULL
15571  * is passed for @relative_to_ancestor)
15572  *
15573  * This can be used by containers that base their paint volume on
15574  * the volume of their children. Such containers can query the
15575  * transformed paint volume of all of its children and union them
15576  * together using clutter_paint_volume_union().
15577  *
15578  * Return value: (transfer none): a pointer to a #ClutterPaintVolume,
15579  *   or %NULL if no volume could be determined. The returned pointer is
15580  *   not guaranteed to be valid across multiple frames; if you wish to
15581  *   keep it, you will have to copy it using clutter_paint_volume_copy().
15582  *
15583  * Since: 1.6
15584  */
15585 const ClutterPaintVolume *
clutter_actor_get_transformed_paint_volume(ClutterActor * self,ClutterActor * relative_to_ancestor)15586 clutter_actor_get_transformed_paint_volume (ClutterActor *self,
15587                                             ClutterActor *relative_to_ancestor)
15588 {
15589   const ClutterPaintVolume *volume;
15590   ClutterActor *stage;
15591   ClutterPaintVolume *transformed_volume;
15592 
15593   stage = _clutter_actor_get_stage_internal (self);
15594   if (G_UNLIKELY (stage == NULL))
15595     return NULL;
15596 
15597   if (relative_to_ancestor == NULL)
15598     relative_to_ancestor = stage;
15599 
15600   volume = clutter_actor_get_paint_volume (self);
15601   if (volume == NULL)
15602     return NULL;
15603 
15604   transformed_volume =
15605     _clutter_stage_paint_volume_stack_allocate (CLUTTER_STAGE (stage));
15606 
15607   _clutter_paint_volume_copy_static (volume, transformed_volume);
15608 
15609   _clutter_paint_volume_transform_relative (transformed_volume,
15610                                             relative_to_ancestor);
15611 
15612   return transformed_volume;
15613 }
15614 
15615 /**
15616  * clutter_actor_get_paint_box:
15617  * @self: a #ClutterActor
15618  * @box: (out): return location for a #ClutterActorBox
15619  *
15620  * Retrieves the paint volume of the passed #ClutterActor, and
15621  * transforms it into a 2D bounding box in stage coordinates.
15622  *
15623  * This function is useful to determine the on screen area occupied by
15624  * the actor. The box is only an approximation and may often be
15625  * considerably larger due to the optimizations used to calculate the
15626  * box. The box is never smaller though, so it can reliably be used
15627  * for culling.
15628  *
15629  * There are times when a 2D paint box can't be determined, e.g.
15630  * because the actor isn't yet parented under a stage or because
15631  * the actor is unable to determine a paint volume.
15632  *
15633  * Return value: %TRUE if a 2D paint box could be determined, else
15634  * %FALSE.
15635  *
15636  * Since: 1.6
15637  */
15638 gboolean
clutter_actor_get_paint_box(ClutterActor * self,ClutterActorBox * box)15639 clutter_actor_get_paint_box (ClutterActor    *self,
15640                              ClutterActorBox *box)
15641 {
15642   ClutterActor *stage;
15643   ClutterPaintVolume *pv;
15644 
15645   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15646   g_return_val_if_fail (box != NULL, FALSE);
15647 
15648   stage = _clutter_actor_get_stage_internal (self);
15649   if (G_UNLIKELY (!stage))
15650     return FALSE;
15651 
15652   pv = _clutter_actor_get_paint_volume_mutable (self);
15653   if (G_UNLIKELY (!pv))
15654     return FALSE;
15655 
15656   _clutter_paint_volume_get_stage_paint_box (pv, CLUTTER_STAGE (stage), box);
15657 
15658   return TRUE;
15659 }
15660 
15661 static ClutterActorTraverseVisitFlags
clear_stage_views_cb(ClutterActor * actor,int depth,gpointer user_data)15662 clear_stage_views_cb (ClutterActor *actor,
15663                       int           depth,
15664                       gpointer      user_data)
15665 {
15666   g_autoptr (GList) old_stage_views = NULL;
15667 
15668   actor->priv->needs_update_stage_views = TRUE;
15669 
15670   old_stage_views = g_steal_pointer (&actor->priv->stage_views);
15671 
15672   if (old_stage_views)
15673     g_signal_emit (actor, actor_signals[STAGE_VIEWS_CHANGED], 0);
15674 
15675   return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
15676 }
15677 
15678 void
clutter_actor_clear_stage_views_recursive(ClutterActor * self)15679 clutter_actor_clear_stage_views_recursive (ClutterActor *self)
15680 {
15681   _clutter_actor_traverse (self,
15682                            CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST,
15683                            clear_stage_views_cb,
15684                            NULL,
15685                            NULL);
15686 }
15687 
15688 float
clutter_actor_get_real_resource_scale(ClutterActor * self)15689 clutter_actor_get_real_resource_scale (ClutterActor *self)
15690 {
15691   ClutterActorPrivate *priv = self->priv;
15692   float guessed_scale;
15693 
15694   if (priv->resource_scale != -1.f)
15695     return priv->resource_scale;
15696 
15697   /* If the scale hasn't been computed yet, we return a best guess */
15698 
15699   if (priv->parent != NULL)
15700     {
15701       /* If the scale hasn't been calculated yet, assume this actor is located
15702        * inside its parents box and go up the hierarchy.
15703        */
15704       guessed_scale = clutter_actor_get_real_resource_scale (priv->parent);
15705     }
15706   else if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
15707     {
15708       /* This must be the first allocation cycle and the resource scale of
15709        * the stage has not been updated yet, so return it manually.
15710        */
15711       GList *l;
15712       ClutterStage *stage = CLUTTER_STAGE (self);
15713       float max_scale = -1.f;
15714 
15715       for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
15716         {
15717           ClutterStageView *view = l->data;
15718 
15719           max_scale = MAX (clutter_stage_view_get_scale (view), max_scale);
15720         }
15721 
15722       guessed_scale = max_scale;
15723     }
15724   else
15725     {
15726       ClutterBackend *backend = clutter_get_default_backend ();
15727 
15728       guessed_scale = clutter_backend_get_fallback_resource_scale (backend);
15729     }
15730 
15731   g_assert (guessed_scale >= 1.f);
15732 
15733   /* Always return this value until we compute the correct one later.
15734    * If our guess turns out to be wrong, we'll emit "resource-scale-changed"
15735    * and correct it before painting.
15736    */
15737   priv->resource_scale = guessed_scale;
15738 
15739   return priv->resource_scale;
15740 }
15741 
15742 /**
15743  * clutter_actor_get_resource_scale:
15744  * @self: A #ClutterActor
15745  *
15746  * Retrieves the resource scale for this actor.
15747  *
15748  * The resource scale refers to the scale the actor should use for its resources.
15749  * For example if an actor draws a a picture of size 100 x 100 in the stage
15750  * coordinate space, it should use a texture of twice the size (i.e. 200 x 200)
15751  * if the resource scale is 2.
15752  *
15753  * The resource scale is determined by calculating the highest #ClutterStageView
15754  * scale the actor will get painted on.
15755  *
15756  * Note that the scale returned by this function is only guaranteed to be
15757  * correct when queried during the paint cycle, in all other cases this
15758  * function will only return a best guess. If your implementation really
15759  * needs to get a resource scale outside of the paint cycle, make sure to
15760  * subscribe to the "resource-scale-changed" signal to get notified about
15761  * the new, correct resource scale before painting.
15762  *
15763  * Also avoid getting the resource scale for actors that are not attached
15764  * to a stage. There's no sane way for Clutter to guess which #ClutterStageView
15765  * the actor is going to be painted on, so you'll probably end up receiving
15766  * the "resource-scale-changed" signal and having to rebuild your resources.
15767  *
15768  * The best guess this function may return is usually just the last resource
15769  * scale the actor got painted with. If this resource scale couldn't be found
15770  * because the actor was never painted so far or Clutter was unable to
15771  * determine its position and size, this function will return the resource
15772  * scale of a parent.
15773  *
15774  * Returns: The resource scale the actor should use for its textures
15775  */
15776 float
clutter_actor_get_resource_scale(ClutterActor * self)15777 clutter_actor_get_resource_scale (ClutterActor *self)
15778 {
15779   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 1.f);
15780 
15781   return ceilf (clutter_actor_get_real_resource_scale (self));
15782 }
15783 
15784 static gboolean
sorted_lists_equal(GList * list_a,GList * list_b)15785 sorted_lists_equal (GList *list_a,
15786                     GList *list_b)
15787 {
15788   GList *a, *b;
15789 
15790   if (!list_a && !list_b)
15791     return TRUE;
15792 
15793   for (a = list_a, b = list_b;
15794        a && b;
15795        a = a->next, b = b->next)
15796     {
15797       if (a->data != b->data)
15798         break;
15799 
15800       if (!a->next && !b->next)
15801         return TRUE;
15802     }
15803 
15804   return FALSE;
15805 }
15806 
15807 static void
update_stage_views(ClutterActor * self)15808 update_stage_views (ClutterActor *self)
15809 {
15810   ClutterActorPrivate *priv = self->priv;
15811   g_autoptr (GList) old_stage_views = NULL;
15812   ClutterStage *stage;
15813   graphene_rect_t bounding_rect;
15814 
15815   old_stage_views = g_steal_pointer (&priv->stage_views);
15816 
15817   if (priv->needs_allocation)
15818     {
15819       g_warning ("Can't update stage views actor %s is on because it needs an "
15820                  "allocation.", _clutter_actor_get_debug_name (self));
15821       goto out;
15822     }
15823 
15824   stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
15825   g_return_if_fail (stage);
15826 
15827   clutter_actor_get_transformed_extents (self, &bounding_rect);
15828 
15829   if (bounding_rect.size.width == 0.0 ||
15830       bounding_rect.size.height == 0.0)
15831     goto out;
15832 
15833   priv->stage_views = clutter_stage_get_views_for_rect (stage,
15834                                                         &bounding_rect);
15835 
15836 out:
15837   if (!sorted_lists_equal (old_stage_views, priv->stage_views))
15838     g_signal_emit (self, actor_signals[STAGE_VIEWS_CHANGED], 0);
15839 }
15840 
15841 static void
update_resource_scale(ClutterActor * self,int phase)15842 update_resource_scale (ClutterActor *self,
15843                        int           phase)
15844 {
15845   ClutterActorPrivate *priv = self->priv;
15846   float new_resource_scale, old_resource_scale;
15847 
15848   new_resource_scale =
15849     CLUTTER_ACTOR_GET_CLASS (self)->calculate_resource_scale (self, phase);
15850 
15851   if (priv->resource_scale == new_resource_scale)
15852     return;
15853 
15854   /* If the actor moved out of the stage, simply keep the last scale */
15855   if (new_resource_scale == -1.f)
15856     return;
15857 
15858   old_resource_scale = priv->resource_scale;
15859   priv->resource_scale = new_resource_scale;
15860 
15861   /* Never notify the initial change, otherwise, to be consistent,
15862    * we'd also have to notify if we guessed correctly in
15863    * clutter_actor_get_real_resource_scale().
15864    */
15865   if (old_resource_scale == -1.f)
15866     return;
15867 
15868   if (ceilf (old_resource_scale) != ceilf (priv->resource_scale))
15869     g_signal_emit (self, actor_signals[RESOURCE_SCALE_CHANGED], 0);
15870 }
15871 
15872 void
clutter_actor_finish_layout(ClutterActor * self,gboolean use_max_scale)15873 clutter_actor_finish_layout (ClutterActor *self,
15874                              gboolean      use_max_scale)
15875 {
15876   ClutterActorPrivate *priv = self->priv;
15877   ClutterActor *child;
15878 
15879   if (!CLUTTER_ACTOR_IS_MAPPED (self) ||
15880       CLUTTER_ACTOR_IN_DESTRUCTION (self))
15881     return;
15882 
15883   _clutter_actor_update_last_paint_volume (self);
15884 
15885   if (priv->needs_update_stage_views)
15886     {
15887       update_stage_views (self);
15888       update_resource_scale (self, use_max_scale);
15889 
15890       priv->needs_update_stage_views = FALSE;
15891     }
15892 
15893   for (child = priv->first_child; child; child = child->priv->next_sibling)
15894     clutter_actor_finish_layout (child, use_max_scale);
15895 }
15896 
15897 /**
15898  * clutter_actor_peek_stage_views:
15899  * @self: A #ClutterActor
15900  *
15901  * Retrieves the list of #ClutterStageView<!-- -->s the actor is being
15902  * painted on.
15903  *
15904  * If this function is called during the paint cycle, the list is guaranteed
15905  * to be up-to-date, if called outside the paint cycle, the list will
15906  * contain the views the actor was painted on last.
15907  *
15908  * The list returned by this function is not updated when the actors
15909  * visibility changes: If an actor gets hidden and is not being painted
15910  * anymore, this function will return the list of views the actor was
15911  * painted on last.
15912  *
15913  * If an actor is not attached to a stage (realized), this function will
15914  * always return an empty list.
15915  *
15916  * Returns: (transfer none) (element-type Clutter.StageView): The list of
15917  *   #ClutterStageView<!-- -->s the actor is being painted on. The list and
15918  *   its contents are owned by the #ClutterActor and the list may not be
15919  *   freed or modified.
15920  */
15921 GList *
clutter_actor_peek_stage_views(ClutterActor * self)15922 clutter_actor_peek_stage_views (ClutterActor *self)
15923 {
15924   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15925 
15926   return self->priv->stage_views;
15927 }
15928 
15929 gboolean
clutter_actor_is_effectively_on_stage_view(ClutterActor * self,ClutterStageView * view)15930 clutter_actor_is_effectively_on_stage_view (ClutterActor     *self,
15931                                             ClutterStageView *view)
15932 {
15933   ClutterActor *actor;
15934 
15935   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15936 
15937   if (g_list_find (self->priv->stage_views, view))
15938     return TRUE;
15939 
15940   for (actor = self; actor; actor = actor->priv->parent)
15941     {
15942       if (actor->priv->clones)
15943         {
15944           GHashTableIter iter;
15945           gpointer key;
15946 
15947           g_hash_table_iter_init (&iter, actor->priv->clones);
15948           while (g_hash_table_iter_next (&iter, &key, NULL))
15949             {
15950               ClutterActor *clone = key;
15951               GList *clone_views;
15952 
15953               clone_views = clutter_actor_peek_stage_views (clone);
15954               if (g_list_find (clone_views, view))
15955                 return TRUE;
15956             }
15957         }
15958     }
15959 
15960   return FALSE;
15961 }
15962 
15963 /**
15964  * clutter_actor_pick_frame_clock: (skip)
15965  * @self: a #ClutterActor
15966  * @out_actor: (nullable): a pointer to an #ClutterActor
15967  *
15968  * Pick the most suitable frame clock for driving animations for this actor.
15969  *
15970  * The #ClutterActor used for picking the frame clock is written @out_actor.
15971  *
15972  * Returns: (transfer none): a #ClutterFrameClock
15973  */
15974 ClutterFrameClock *
clutter_actor_pick_frame_clock(ClutterActor * self,ClutterActor ** out_actor)15975 clutter_actor_pick_frame_clock (ClutterActor  *self,
15976                                 ClutterActor **out_actor)
15977 {
15978   ClutterActorPrivate *priv = self->priv;
15979   GList *stage_views_list;
15980   float max_refresh_rate = 0.0;
15981   ClutterStageView *best_view = NULL;
15982   GList *l;
15983 
15984   stage_views_list = CLUTTER_IS_STAGE (self)
15985     ? clutter_stage_peek_stage_views (CLUTTER_STAGE (self))
15986     : priv->stage_views;
15987 
15988   if (!stage_views_list)
15989     {
15990      if (priv->parent)
15991        return clutter_actor_pick_frame_clock (priv->parent, out_actor);
15992      else
15993        return NULL;
15994     }
15995 
15996   for (l = stage_views_list; l; l = l->next)
15997     {
15998       ClutterStageView *view = l->data;
15999       float refresh_rate;
16000 
16001       refresh_rate = clutter_stage_view_get_refresh_rate (view);
16002       if (refresh_rate > max_refresh_rate)
16003         {
16004           best_view = view;
16005           max_refresh_rate = refresh_rate;
16006         }
16007     }
16008 
16009   if (best_view)
16010     {
16011       if (out_actor)
16012         *out_actor = self;
16013       return clutter_stage_view_get_frame_clock (best_view);
16014     }
16015   else
16016     {
16017       return NULL;
16018     }
16019 }
16020 
16021 /**
16022  * clutter_actor_has_overlaps:
16023  * @self: A #ClutterActor
16024  *
16025  * Asks the actor's implementation whether it may contain overlapping
16026  * primitives.
16027  *
16028  * For example; Clutter may use this to determine whether the painting
16029  * should be redirected to an offscreen buffer to correctly implement
16030  * the opacity property.
16031  *
16032  * Custom actors can override the default response by implementing the
16033  * #ClutterActorClass.has_overlaps() virtual function. See
16034  * clutter_actor_set_offscreen_redirect() for more information.
16035  *
16036  * Return value: %TRUE if the actor may have overlapping primitives, and
16037  *   %FALSE otherwise
16038  *
16039  * Since: 1.8
16040  */
16041 gboolean
clutter_actor_has_overlaps(ClutterActor * self)16042 clutter_actor_has_overlaps (ClutterActor *self)
16043 {
16044   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), TRUE);
16045 
16046   return CLUTTER_ACTOR_GET_CLASS (self)->has_overlaps (self);
16047 }
16048 
16049 /**
16050  * clutter_actor_has_effects:
16051  * @self: A #ClutterActor
16052  *
16053  * Returns whether the actor has any effects applied.
16054  *
16055  * Return value: %TRUE if the actor has any effects,
16056  *   %FALSE otherwise
16057  *
16058  * Since: 1.10
16059  */
16060 gboolean
clutter_actor_has_effects(ClutterActor * self)16061 clutter_actor_has_effects (ClutterActor *self)
16062 {
16063   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
16064 
16065   if (self->priv->effects == NULL)
16066     return FALSE;
16067 
16068   return _clutter_meta_group_has_metas_no_internal (self->priv->effects);
16069 }
16070 
16071 /**
16072  * clutter_actor_has_constraints:
16073  * @self: A #ClutterActor
16074  *
16075  * Returns whether the actor has any constraints applied.
16076  *
16077  * Return value: %TRUE if the actor has any constraints,
16078  *   %FALSE otherwise
16079  *
16080  * Since: 1.10
16081  */
16082 gboolean
clutter_actor_has_constraints(ClutterActor * self)16083 clutter_actor_has_constraints (ClutterActor *self)
16084 {
16085   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
16086 
16087   if (self->priv->constraints == NULL)
16088     return FALSE;
16089 
16090   return _clutter_meta_group_has_metas_no_internal (self->priv->constraints);
16091 }
16092 
16093 /**
16094  * clutter_actor_has_actions:
16095  * @self: A #ClutterActor
16096  *
16097  * Returns whether the actor has any actions applied.
16098  *
16099  * Return value: %TRUE if the actor has any actions,
16100  *   %FALSE otherwise
16101  *
16102  * Since: 1.10
16103  */
16104 gboolean
clutter_actor_has_actions(ClutterActor * self)16105 clutter_actor_has_actions (ClutterActor *self)
16106 {
16107   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
16108 
16109   if (self->priv->actions == NULL)
16110     return FALSE;
16111 
16112   return _clutter_meta_group_has_metas_no_internal (self->priv->actions);
16113 }
16114 
16115 /**
16116  * clutter_actor_get_n_children:
16117  * @self: a #ClutterActor
16118  *
16119  * Retrieves the number of children of @self.
16120  *
16121  * Return value: the number of children of an actor
16122  *
16123  * Since: 1.10
16124  */
16125 gint
clutter_actor_get_n_children(ClutterActor * self)16126 clutter_actor_get_n_children (ClutterActor *self)
16127 {
16128   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
16129 
16130   return self->priv->n_children;
16131 }
16132 
16133 /**
16134  * clutter_actor_get_child_at_index:
16135  * @self: a #ClutterActor
16136  * @index_: the position in the list of children
16137  *
16138  * Retrieves the actor at the given @index_ inside the list of
16139  * children of @self.
16140  *
16141  * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
16142  *
16143  * Since: 1.10
16144  */
16145 ClutterActor *
clutter_actor_get_child_at_index(ClutterActor * self,gint index_)16146 clutter_actor_get_child_at_index (ClutterActor *self,
16147                                   gint          index_)
16148 {
16149   ClutterActor *iter;
16150   int i;
16151 
16152   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
16153   g_return_val_if_fail (index_ <= self->priv->n_children, NULL);
16154 
16155   for (iter = self->priv->first_child, i = 0;
16156        iter != NULL && i < index_;
16157        iter = iter->priv->next_sibling, i += 1)
16158     ;
16159 
16160   return iter;
16161 }
16162 
16163 /*< private >
16164  * _clutter_actor_foreach_child:
16165  * @actor: The actor whose children you want to iterate
16166  * @callback: The function to call for each child
16167  * @user_data: Private data to pass to @callback
16168  *
16169  * Calls a given @callback once for each child of the specified @actor and
16170  * passing the @user_data pointer each time.
16171  *
16172  * Return value: returns %TRUE if all children were iterated, else
16173  *    %FALSE if a callback broke out of iteration early.
16174  */
16175 gboolean
_clutter_actor_foreach_child(ClutterActor * self,ClutterForeachCallback callback,gpointer user_data)16176 _clutter_actor_foreach_child (ClutterActor           *self,
16177                               ClutterForeachCallback  callback,
16178                               gpointer                user_data)
16179 {
16180   ClutterActor *iter;
16181   gboolean cont;
16182 
16183   if (self->priv->first_child == NULL)
16184     return TRUE;
16185 
16186   cont = TRUE;
16187   iter = self->priv->first_child;
16188 
16189   /* we use this form so that it's safe to change the children
16190    * list while iterating it
16191    */
16192   while (cont && iter != NULL)
16193     {
16194       ClutterActor *next = iter->priv->next_sibling;
16195 
16196       cont = callback (iter, user_data);
16197 
16198       iter = next;
16199     }
16200 
16201   return cont;
16202 }
16203 
16204 #if 0
16205 /* For debugging purposes this gives us a simple way to print out
16206  * the scenegraph e.g in gdb using:
16207  * [|
16208  *   _clutter_actor_traverse (stage,
16209  *                            0,
16210  *                            clutter_debug_print_actor_cb,
16211  *                            NULL,
16212  *                            NULL);
16213  * |]
16214  */
16215 static ClutterActorTraverseVisitFlags
16216 clutter_debug_print_actor_cb (ClutterActor *actor,
16217                               int depth,
16218                               void *user_data)
16219 {
16220   g_print ("%*s%s:%p\n",
16221            depth * 2, "",
16222            _clutter_actor_get_debug_name (actor),
16223            actor);
16224 
16225   return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
16226 }
16227 #endif
16228 
16229 static void
_clutter_actor_traverse_breadth(ClutterActor * actor,ClutterTraverseCallback callback,gpointer user_data)16230 _clutter_actor_traverse_breadth (ClutterActor           *actor,
16231                                  ClutterTraverseCallback callback,
16232                                  gpointer                user_data)
16233 {
16234   GQueue *queue = g_queue_new ();
16235   ClutterActor dummy;
16236   int current_depth = 0;
16237 
16238   g_queue_push_tail (queue, actor);
16239   g_queue_push_tail (queue, &dummy); /* use to delimit depth changes */
16240 
16241   while ((actor = g_queue_pop_head (queue)))
16242     {
16243       ClutterActorTraverseVisitFlags flags;
16244 
16245       if (actor == &dummy)
16246         {
16247           current_depth++;
16248           g_queue_push_tail (queue, &dummy);
16249           continue;
16250         }
16251 
16252       flags = callback (actor, current_depth, user_data);
16253       if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
16254         break;
16255       else if (!(flags & CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN))
16256         {
16257           ClutterActor *iter;
16258 
16259           for (iter = actor->priv->first_child;
16260                iter != NULL;
16261                iter = iter->priv->next_sibling)
16262             {
16263               g_queue_push_tail (queue, iter);
16264             }
16265         }
16266     }
16267 
16268   g_queue_free (queue);
16269 }
16270 
16271 static ClutterActorTraverseVisitFlags
_clutter_actor_traverse_depth(ClutterActor * actor,ClutterTraverseCallback before_children_callback,ClutterTraverseCallback after_children_callback,int current_depth,gpointer user_data)16272 _clutter_actor_traverse_depth (ClutterActor           *actor,
16273                                ClutterTraverseCallback before_children_callback,
16274                                ClutterTraverseCallback after_children_callback,
16275                                int                     current_depth,
16276                                gpointer                user_data)
16277 {
16278   ClutterActorTraverseVisitFlags flags;
16279 
16280   flags = before_children_callback (actor, current_depth, user_data);
16281   if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
16282     return CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK;
16283 
16284   if (!(flags & CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN))
16285     {
16286       ClutterActor *iter;
16287 
16288       for (iter = actor->priv->first_child;
16289            iter != NULL;
16290            iter = iter->priv->next_sibling)
16291         {
16292           flags = _clutter_actor_traverse_depth (iter,
16293                                                  before_children_callback,
16294                                                  after_children_callback,
16295                                                  current_depth + 1,
16296                                                  user_data);
16297 
16298           if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
16299             return CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK;
16300         }
16301     }
16302 
16303   if (after_children_callback)
16304     return after_children_callback (actor, current_depth, user_data);
16305   else
16306     return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
16307 }
16308 
16309 /* _clutter_actor_traverse:
16310  * @actor: The actor to start traversing the graph from
16311  * @flags: These flags may affect how the traversal is done
16312  * @before_children_callback: A function to call before visiting the
16313  *   children of the current actor.
16314  * @after_children_callback: A function to call after visiting the
16315  *   children of the current actor. (Ignored if
16316  *   %CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST is passed to @flags.)
16317  * @user_data: The private data to pass to the callbacks
16318  *
16319  * Traverses the scenegraph starting at the specified @actor and
16320  * descending through all its children and its children's children.
16321  * For each actor traversed @before_children_callback and
16322  * @after_children_callback are called with the specified
16323  * @user_data, before and after visiting that actor's children.
16324  *
16325  * The callbacks can return flags that affect the ongoing traversal
16326  * such as by skipping over an actors children or bailing out of
16327  * any further traversing.
16328  */
16329 void
_clutter_actor_traverse(ClutterActor * actor,ClutterActorTraverseFlags flags,ClutterTraverseCallback before_children_callback,ClutterTraverseCallback after_children_callback,gpointer user_data)16330 _clutter_actor_traverse (ClutterActor              *actor,
16331                          ClutterActorTraverseFlags  flags,
16332                          ClutterTraverseCallback    before_children_callback,
16333                          ClutterTraverseCallback    after_children_callback,
16334                          gpointer                   user_data)
16335 {
16336   if (flags & CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST)
16337     _clutter_actor_traverse_breadth (actor,
16338                                      before_children_callback,
16339                                      user_data);
16340   else /* DEPTH_FIRST */
16341     _clutter_actor_traverse_depth (actor,
16342                                    before_children_callback,
16343                                    after_children_callback,
16344                                    0, /* start depth */
16345                                    user_data);
16346 }
16347 
16348 static void
on_layout_manager_changed(ClutterLayoutManager * manager,ClutterActor * self)16349 on_layout_manager_changed (ClutterLayoutManager *manager,
16350                            ClutterActor         *self)
16351 {
16352   clutter_actor_queue_relayout (self);
16353 }
16354 
16355 /**
16356  * clutter_actor_set_layout_manager:
16357  * @self: a #ClutterActor
16358  * @manager: (allow-none): a #ClutterLayoutManager, or %NULL to unset it
16359  *
16360  * Sets the #ClutterLayoutManager delegate object that will be used to
16361  * lay out the children of @self.
16362  *
16363  * The #ClutterActor will take a reference on the passed @manager which
16364  * will be released either when the layout manager is removed, or when
16365  * the actor is destroyed.
16366  *
16367  * Since: 1.10
16368  */
16369 void
clutter_actor_set_layout_manager(ClutterActor * self,ClutterLayoutManager * manager)16370 clutter_actor_set_layout_manager (ClutterActor         *self,
16371                                   ClutterLayoutManager *manager)
16372 {
16373   ClutterActorPrivate *priv;
16374 
16375   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16376   g_return_if_fail (manager == NULL || CLUTTER_IS_LAYOUT_MANAGER (manager));
16377 
16378   priv = self->priv;
16379 
16380   if (priv->layout_manager != NULL)
16381     {
16382       g_clear_signal_handler (&priv->layout_changed_id, priv->layout_manager);
16383       clutter_layout_manager_set_container (priv->layout_manager, NULL);
16384       g_clear_object (&priv->layout_manager);
16385     }
16386 
16387   priv->layout_manager = manager;
16388 
16389   if (priv->layout_manager != NULL)
16390     {
16391       g_object_ref_sink (priv->layout_manager);
16392       clutter_layout_manager_set_container (priv->layout_manager,
16393                                             CLUTTER_CONTAINER (self));
16394       priv->layout_changed_id =
16395         g_signal_connect (priv->layout_manager, "layout-changed",
16396                           G_CALLBACK (on_layout_manager_changed),
16397                           self);
16398     }
16399 
16400   clutter_actor_queue_relayout (self);
16401 
16402   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAYOUT_MANAGER]);
16403 }
16404 
16405 /**
16406  * clutter_actor_get_layout_manager:
16407  * @self: a #ClutterActor
16408  *
16409  * Retrieves the #ClutterLayoutManager used by @self.
16410  *
16411  * Return value: (transfer none): a pointer to the #ClutterLayoutManager,
16412  *   or %NULL
16413  *
16414  * Since: 1.10
16415  */
16416 ClutterLayoutManager *
clutter_actor_get_layout_manager(ClutterActor * self)16417 clutter_actor_get_layout_manager (ClutterActor *self)
16418 {
16419   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
16420 
16421   return self->priv->layout_manager;
16422 }
16423 
16424 static const ClutterLayoutInfo default_layout_info = {
16425   GRAPHENE_POINT_INIT_ZERO,     /* fixed-pos */
16426   { 0, 0, 0, 0 },               /* margin */
16427   CLUTTER_ACTOR_ALIGN_FILL,     /* x-align */
16428   CLUTTER_ACTOR_ALIGN_FILL,     /* y-align */
16429   FALSE, FALSE,                 /* expand */
16430   GRAPHENE_SIZE_INIT_ZERO,       /* minimum */
16431   GRAPHENE_SIZE_INIT_ZERO,       /* natural */
16432 };
16433 
16434 static void
layout_info_free(gpointer data)16435 layout_info_free (gpointer data)
16436 {
16437   if (G_LIKELY (data != NULL))
16438     g_free (data);
16439 }
16440 
16441 /*< private >
16442  * _clutter_actor_peek_layout_info:
16443  * @self: a #ClutterActor
16444  *
16445  * Retrieves a pointer to the ClutterLayoutInfo structure.
16446  *
16447  * If the actor does not have a ClutterLayoutInfo associated to it, %NULL is returned.
16448  *
16449  * Return value: (transfer none): a pointer to the ClutterLayoutInfo structure
16450  */
16451 ClutterLayoutInfo *
_clutter_actor_peek_layout_info(ClutterActor * self)16452 _clutter_actor_peek_layout_info (ClutterActor *self)
16453 {
16454   return g_object_get_qdata (G_OBJECT (self), quark_actor_layout_info);
16455 }
16456 
16457 /*< private >
16458  * _clutter_actor_get_layout_info:
16459  * @self: a #ClutterActor
16460  *
16461  * Retrieves a pointer to the ClutterLayoutInfo structure.
16462  *
16463  * If the actor does not have a ClutterLayoutInfo associated to it, one
16464  * will be created and initialized to the default values.
16465  *
16466  * This function should be used for setters.
16467  *
16468  * For getters, you should use _clutter_actor_get_layout_info_or_defaults()
16469  * instead.
16470  *
16471  * Return value: (transfer none): a pointer to the ClutterLayoutInfo structure
16472  */
16473 ClutterLayoutInfo *
_clutter_actor_get_layout_info(ClutterActor * self)16474 _clutter_actor_get_layout_info (ClutterActor *self)
16475 {
16476   ClutterLayoutInfo *retval;
16477 
16478   retval = _clutter_actor_peek_layout_info (self);
16479   if (retval == NULL)
16480     {
16481       retval = g_new0 (ClutterLayoutInfo, 1);
16482 
16483       *retval = default_layout_info;
16484 
16485       g_object_set_qdata_full (G_OBJECT (self), quark_actor_layout_info,
16486                                retval,
16487                                layout_info_free);
16488     }
16489 
16490   return retval;
16491 }
16492 
16493 /*< private >
16494  * _clutter_actor_get_layout_info_or_defaults:
16495  * @self: a #ClutterActor
16496  *
16497  * Retrieves the ClutterLayoutInfo structure associated to an actor.
16498  *
16499  * If the actor does not have a ClutterLayoutInfo structure associated to it,
16500  * then the default structure will be returned.
16501  *
16502  * This function should only be used for getters.
16503  *
16504  * Return value: a const pointer to the ClutterLayoutInfo structure
16505  */
16506 const ClutterLayoutInfo *
_clutter_actor_get_layout_info_or_defaults(ClutterActor * self)16507 _clutter_actor_get_layout_info_or_defaults (ClutterActor *self)
16508 {
16509   const ClutterLayoutInfo *info;
16510 
16511   info = _clutter_actor_peek_layout_info (self);
16512   if (info == NULL)
16513     return &default_layout_info;
16514 
16515   return info;
16516 }
16517 
16518 /**
16519  * clutter_actor_set_x_align:
16520  * @self: a #ClutterActor
16521  * @x_align: the horizontal alignment policy
16522  *
16523  * Sets the horizontal alignment policy of a #ClutterActor, in case the
16524  * actor received extra horizontal space.
16525  *
16526  * See also the #ClutterActor:x-align property.
16527  *
16528  * Since: 1.10
16529  */
16530 void
clutter_actor_set_x_align(ClutterActor * self,ClutterActorAlign x_align)16531 clutter_actor_set_x_align (ClutterActor      *self,
16532                            ClutterActorAlign  x_align)
16533 {
16534   ClutterLayoutInfo *info;
16535 
16536   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16537 
16538   info = _clutter_actor_get_layout_info (self);
16539 
16540   if (info->x_align != x_align)
16541     {
16542       info->x_align = x_align;
16543 
16544       clutter_actor_queue_relayout (self);
16545 
16546       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_X_ALIGN]);
16547     }
16548 }
16549 
16550 /**
16551  * clutter_actor_get_x_align:
16552  * @self: a #ClutterActor
16553  *
16554  * Retrieves the horizontal alignment policy set using
16555  * clutter_actor_set_x_align().
16556  *
16557  * Return value: the horizontal alignment policy.
16558  *
16559  * Since: 1.10
16560  */
16561 ClutterActorAlign
clutter_actor_get_x_align(ClutterActor * self)16562 clutter_actor_get_x_align (ClutterActor *self)
16563 {
16564   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_ACTOR_ALIGN_FILL);
16565 
16566   return _clutter_actor_get_layout_info_or_defaults (self)->x_align;
16567 }
16568 
16569 /**
16570  * clutter_actor_set_y_align:
16571  * @self: a #ClutterActor
16572  * @y_align: the vertical alignment policy
16573  *
16574  * Sets the vertical alignment policy of a #ClutterActor, in case the
16575  * actor received extra vertical space.
16576  *
16577  * See also the #ClutterActor:y-align property.
16578  *
16579  * Since: 1.10
16580  */
16581 void
clutter_actor_set_y_align(ClutterActor * self,ClutterActorAlign y_align)16582 clutter_actor_set_y_align (ClutterActor      *self,
16583                            ClutterActorAlign  y_align)
16584 {
16585   ClutterLayoutInfo *info;
16586 
16587   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16588 
16589   info = _clutter_actor_get_layout_info (self);
16590 
16591   if (info->y_align != y_align)
16592     {
16593       info->y_align = y_align;
16594 
16595       clutter_actor_queue_relayout (self);
16596 
16597       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_Y_ALIGN]);
16598     }
16599 }
16600 
16601 /**
16602  * clutter_actor_get_y_align:
16603  * @self: a #ClutterActor
16604  *
16605  * Retrieves the vertical alignment policy set using
16606  * clutter_actor_set_y_align().
16607  *
16608  * Return value: the vertical alignment policy.
16609  *
16610  * Since: 1.10
16611  */
16612 ClutterActorAlign
clutter_actor_get_y_align(ClutterActor * self)16613 clutter_actor_get_y_align (ClutterActor *self)
16614 {
16615   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_ACTOR_ALIGN_FILL);
16616 
16617   return _clutter_actor_get_layout_info_or_defaults (self)->y_align;
16618 }
16619 
16620 static inline void
clutter_actor_set_margin_internal(ClutterActor * self,gfloat margin,GParamSpec * pspec)16621 clutter_actor_set_margin_internal (ClutterActor *self,
16622                                    gfloat        margin,
16623                                    GParamSpec   *pspec)
16624 {
16625   ClutterLayoutInfo *info;
16626 
16627   info = _clutter_actor_get_layout_info (self);
16628 
16629   if (pspec == obj_props[PROP_MARGIN_TOP])
16630     info->margin.top = margin;
16631   else if (pspec == obj_props[PROP_MARGIN_RIGHT])
16632     info->margin.right = margin;
16633   else if (pspec == obj_props[PROP_MARGIN_BOTTOM])
16634     info->margin.bottom = margin;
16635   else
16636     info->margin.left = margin;
16637 
16638   clutter_actor_queue_relayout (self);
16639   g_object_notify_by_pspec (G_OBJECT (self), pspec);
16640 }
16641 
16642 /**
16643  * clutter_actor_set_margin:
16644  * @self: a #ClutterActor
16645  * @margin: a #ClutterMargin
16646  *
16647  * Sets all the components of the margin of a #ClutterActor.
16648  *
16649  * Since: 1.10
16650  */
16651 void
clutter_actor_set_margin(ClutterActor * self,const ClutterMargin * margin)16652 clutter_actor_set_margin (ClutterActor        *self,
16653                           const ClutterMargin *margin)
16654 {
16655   ClutterLayoutInfo *info;
16656 
16657   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16658   g_return_if_fail (margin != NULL);
16659 
16660   info = _clutter_actor_get_layout_info (self);
16661 
16662   if (info->margin.top != margin->top)
16663     clutter_actor_set_margin_top (self, margin->top);
16664 
16665   if (info->margin.right != margin->right)
16666     clutter_actor_set_margin_right (self, margin->right);
16667 
16668   if (info->margin.bottom != margin->bottom)
16669     clutter_actor_set_margin_bottom (self, margin->bottom);
16670 
16671   if (info->margin.left != margin->left)
16672     clutter_actor_set_margin_left (self, margin->left);
16673 }
16674 
16675 /**
16676  * clutter_actor_get_margin:
16677  * @self: a #ClutterActor
16678  * @margin: (out caller-allocates): return location for a #ClutterMargin
16679  *
16680  * Retrieves all the components of the margin of a #ClutterActor.
16681  *
16682  * Since: 1.10
16683  */
16684 void
clutter_actor_get_margin(ClutterActor * self,ClutterMargin * margin)16685 clutter_actor_get_margin (ClutterActor  *self,
16686                           ClutterMargin *margin)
16687 {
16688   const ClutterLayoutInfo *info;
16689 
16690   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16691   g_return_if_fail (margin != NULL);
16692 
16693   info = _clutter_actor_get_layout_info_or_defaults (self);
16694 
16695   *margin = info->margin;
16696 }
16697 
16698 /**
16699  * clutter_actor_set_margin_top:
16700  * @self: a #ClutterActor
16701  * @margin: the top margin
16702  *
16703  * Sets the margin from the top of a #ClutterActor.
16704  *
16705  * The #ClutterActor:margin-top property is animatable.
16706  *
16707  * Since: 1.10
16708  */
16709 void
clutter_actor_set_margin_top(ClutterActor * self,gfloat margin)16710 clutter_actor_set_margin_top (ClutterActor *self,
16711                               gfloat        margin)
16712 {
16713   const ClutterLayoutInfo *info;
16714 
16715   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16716   g_return_if_fail (margin >= 0.f);
16717 
16718   info = _clutter_actor_get_layout_info_or_defaults (self);
16719 
16720   if (info->margin.top == margin)
16721     return;
16722 
16723   _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_TOP],
16724                                     info->margin.top,
16725                                     margin);
16726 }
16727 
16728 /**
16729  * clutter_actor_get_margin_top:
16730  * @self: a #ClutterActor
16731  *
16732  * Retrieves the top margin of a #ClutterActor.
16733  *
16734  * Return value: the top margin
16735  *
16736  * Since: 1.10
16737  */
16738 gfloat
clutter_actor_get_margin_top(ClutterActor * self)16739 clutter_actor_get_margin_top (ClutterActor *self)
16740 {
16741   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
16742 
16743   return _clutter_actor_get_layout_info_or_defaults (self)->margin.top;
16744 }
16745 
16746 /**
16747  * clutter_actor_set_margin_bottom:
16748  * @self: a #ClutterActor
16749  * @margin: the bottom margin
16750  *
16751  * Sets the margin from the bottom of a #ClutterActor.
16752  *
16753  * The #ClutterActor:margin-bottom property is animatable.
16754  *
16755  * Since: 1.10
16756  */
16757 void
clutter_actor_set_margin_bottom(ClutterActor * self,gfloat margin)16758 clutter_actor_set_margin_bottom (ClutterActor *self,
16759                                  gfloat        margin)
16760 {
16761   const ClutterLayoutInfo *info;
16762 
16763   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16764   g_return_if_fail (margin >= 0.f);
16765 
16766   info = _clutter_actor_get_layout_info_or_defaults (self);
16767 
16768   if (info->margin.bottom == margin)
16769     return;
16770 
16771   _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_BOTTOM],
16772                                     info->margin.bottom,
16773                                     margin);
16774 }
16775 
16776 /**
16777  * clutter_actor_get_margin_bottom:
16778  * @self: a #ClutterActor
16779  *
16780  * Retrieves the bottom margin of a #ClutterActor.
16781  *
16782  * Return value: the bottom margin
16783  *
16784  * Since: 1.10
16785  */
16786 gfloat
clutter_actor_get_margin_bottom(ClutterActor * self)16787 clutter_actor_get_margin_bottom (ClutterActor *self)
16788 {
16789   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
16790 
16791   return _clutter_actor_get_layout_info_or_defaults (self)->margin.bottom;
16792 }
16793 
16794 /**
16795  * clutter_actor_set_margin_left:
16796  * @self: a #ClutterActor
16797  * @margin: the left margin
16798  *
16799  * Sets the margin from the left of a #ClutterActor.
16800  *
16801  * The #ClutterActor:margin-left property is animatable.
16802  *
16803  * Since: 1.10
16804  */
16805 void
clutter_actor_set_margin_left(ClutterActor * self,gfloat margin)16806 clutter_actor_set_margin_left (ClutterActor *self,
16807                                gfloat        margin)
16808 {
16809   const ClutterLayoutInfo *info;
16810 
16811   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16812   g_return_if_fail (margin >= 0.f);
16813 
16814   info = _clutter_actor_get_layout_info_or_defaults (self);
16815 
16816   if (info->margin.left == margin)
16817     return;
16818 
16819   _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_LEFT],
16820                                     info->margin.left,
16821                                     margin);
16822 }
16823 
16824 /**
16825  * clutter_actor_get_margin_left:
16826  * @self: a #ClutterActor
16827  *
16828  * Retrieves the left margin of a #ClutterActor.
16829  *
16830  * Return value: the left margin
16831  *
16832  * Since: 1.10
16833  */
16834 gfloat
clutter_actor_get_margin_left(ClutterActor * self)16835 clutter_actor_get_margin_left (ClutterActor *self)
16836 {
16837   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
16838 
16839   return _clutter_actor_get_layout_info_or_defaults (self)->margin.left;
16840 }
16841 
16842 /**
16843  * clutter_actor_set_margin_right:
16844  * @self: a #ClutterActor
16845  * @margin: the right margin
16846  *
16847  * Sets the margin from the right of a #ClutterActor.
16848  *
16849  * The #ClutterActor:margin-right property is animatable.
16850  *
16851  * Since: 1.10
16852  */
16853 void
clutter_actor_set_margin_right(ClutterActor * self,gfloat margin)16854 clutter_actor_set_margin_right (ClutterActor *self,
16855                                 gfloat        margin)
16856 {
16857   const ClutterLayoutInfo *info;
16858 
16859   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16860   g_return_if_fail (margin >= 0.f);
16861 
16862   info = _clutter_actor_get_layout_info_or_defaults (self);
16863 
16864   if (info->margin.right == margin)
16865     return;
16866 
16867   _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_RIGHT],
16868                                     info->margin.right,
16869                                     margin);
16870 }
16871 
16872 /**
16873  * clutter_actor_get_margin_right:
16874  * @self: a #ClutterActor
16875  *
16876  * Retrieves the right margin of a #ClutterActor.
16877  *
16878  * Return value: the right margin
16879  *
16880  * Since: 1.10
16881  */
16882 gfloat
clutter_actor_get_margin_right(ClutterActor * self)16883 clutter_actor_get_margin_right (ClutterActor *self)
16884 {
16885   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
16886 
16887   return _clutter_actor_get_layout_info_or_defaults (self)->margin.right;
16888 }
16889 
16890 static inline void
clutter_actor_set_background_color_internal(ClutterActor * self,const ClutterColor * color)16891 clutter_actor_set_background_color_internal (ClutterActor *self,
16892                                              const ClutterColor *color)
16893 {
16894   ClutterActorPrivate *priv = self->priv;
16895   GObject *obj;
16896 
16897   if (priv->bg_color_set && clutter_color_equal (color, &priv->bg_color))
16898     return;
16899 
16900   obj = G_OBJECT (self);
16901 
16902   priv->bg_color = *color;
16903   priv->bg_color_set = TRUE;
16904 
16905   clutter_actor_queue_redraw (self);
16906 
16907   g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR_SET]);
16908   g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR]);
16909 }
16910 
16911 /**
16912  * clutter_actor_set_background_color:
16913  * @self: a #ClutterActor
16914  * @color: (allow-none): a #ClutterColor, or %NULL to unset a previously
16915  *  set color
16916  *
16917  * Sets the background color of a #ClutterActor.
16918  *
16919  * The background color will be used to cover the whole allocation of the
16920  * actor. The default background color of an actor is transparent.
16921  *
16922  * To check whether an actor has a background color, you can use the
16923  * #ClutterActor:background-color-set actor property.
16924  *
16925  * The #ClutterActor:background-color property is animatable.
16926  *
16927  * Since: 1.10
16928  */
16929 void
clutter_actor_set_background_color(ClutterActor * self,const ClutterColor * color)16930 clutter_actor_set_background_color (ClutterActor       *self,
16931                                     const ClutterColor *color)
16932 {
16933   ClutterActorPrivate *priv;
16934 
16935   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16936 
16937   priv = self->priv;
16938 
16939   if (color == NULL)
16940     {
16941       GObject *obj = G_OBJECT (self);
16942 
16943       priv->bg_color_set = FALSE;
16944 
16945       clutter_actor_queue_redraw (self);
16946 
16947       g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR_SET]);
16948     }
16949   else
16950     _clutter_actor_create_transition (self,
16951                                       obj_props[PROP_BACKGROUND_COLOR],
16952                                       &priv->bg_color,
16953                                       color);
16954 }
16955 
16956 /**
16957  * clutter_actor_get_background_color:
16958  * @self: a #ClutterActor
16959  * @color: (out caller-allocates): return location for a #ClutterColor
16960  *
16961  * Retrieves the color set using clutter_actor_set_background_color().
16962  *
16963  * Since: 1.10
16964  */
16965 void
clutter_actor_get_background_color(ClutterActor * self,ClutterColor * color)16966 clutter_actor_get_background_color (ClutterActor *self,
16967                                     ClutterColor *color)
16968 {
16969   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16970   g_return_if_fail (color != NULL);
16971 
16972   *color = self->priv->bg_color;
16973 }
16974 
16975 /**
16976  * clutter_actor_get_previous_sibling:
16977  * @self: a #ClutterActor
16978  *
16979  * Retrieves the sibling of @self that comes before it in the list
16980  * of children of @self's parent.
16981  *
16982  * The returned pointer is only valid until the scene graph changes; it
16983  * is not safe to modify the list of children of @self while iterating
16984  * it.
16985  *
16986  * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
16987  *
16988  * Since: 1.10
16989  */
16990 ClutterActor *
clutter_actor_get_previous_sibling(ClutterActor * self)16991 clutter_actor_get_previous_sibling (ClutterActor *self)
16992 {
16993   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
16994 
16995   return self->priv->prev_sibling;
16996 }
16997 
16998 /**
16999  * clutter_actor_get_next_sibling:
17000  * @self: a #ClutterActor
17001  *
17002  * Retrieves the sibling of @self that comes after it in the list
17003  * of children of @self's parent.
17004  *
17005  * The returned pointer is only valid until the scene graph changes; it
17006  * is not safe to modify the list of children of @self while iterating
17007  * it.
17008  *
17009  * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
17010  *
17011  * Since: 1.10
17012  */
17013 ClutterActor *
clutter_actor_get_next_sibling(ClutterActor * self)17014 clutter_actor_get_next_sibling (ClutterActor *self)
17015 {
17016   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17017 
17018   return self->priv->next_sibling;
17019 }
17020 
17021 /**
17022  * clutter_actor_get_first_child:
17023  * @self: a #ClutterActor
17024  *
17025  * Retrieves the first child of @self.
17026  *
17027  * The returned pointer is only valid until the scene graph changes; it
17028  * is not safe to modify the list of children of @self while iterating
17029  * it.
17030  *
17031  * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
17032  *
17033  * Since: 1.10
17034  */
17035 ClutterActor *
clutter_actor_get_first_child(ClutterActor * self)17036 clutter_actor_get_first_child (ClutterActor *self)
17037 {
17038   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17039 
17040   return self->priv->first_child;
17041 }
17042 
17043 /**
17044  * clutter_actor_get_last_child:
17045  * @self: a #ClutterActor
17046  *
17047  * Retrieves the last child of @self.
17048  *
17049  * The returned pointer is only valid until the scene graph changes; it
17050  * is not safe to modify the list of children of @self while iterating
17051  * it.
17052  *
17053  * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
17054  *
17055  * Since: 1.10
17056  */
17057 ClutterActor *
clutter_actor_get_last_child(ClutterActor * self)17058 clutter_actor_get_last_child (ClutterActor *self)
17059 {
17060   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17061 
17062   return self->priv->last_child;
17063 }
17064 
17065 /* easy way to have properly named fields instead of the dummy ones
17066  * we use in the public structure
17067  */
17068 typedef struct _RealActorIter
17069 {
17070   ClutterActor *root;           /* dummy1 */
17071   ClutterActor *current;        /* dummy2 */
17072   gpointer padding_1;           /* dummy3 */
17073   gint age;                     /* dummy4 */
17074   gpointer padding_2;           /* dummy5 */
17075 } RealActorIter;
17076 
17077 /**
17078  * clutter_actor_iter_init:
17079  * @iter: a #ClutterActorIter
17080  * @root: a #ClutterActor
17081  *
17082  * Initializes a #ClutterActorIter, which can then be used to iterate
17083  * efficiently over a section of the scene graph, and associates it
17084  * with @root.
17085  *
17086  * Modifying the scene graph section that contains @root will invalidate
17087  * the iterator.
17088  *
17089  * |[<!-- language="C" -->
17090  *   ClutterActorIter iter;
17091  *   ClutterActor *child;
17092  *
17093  *   clutter_actor_iter_init (&iter, container);
17094  *   while (clutter_actor_iter_next (&iter, &child))
17095  *     {
17096  *       // do something with child
17097  *     }
17098  * ]|
17099  *
17100  * Since: 1.10
17101  */
17102 void
clutter_actor_iter_init(ClutterActorIter * iter,ClutterActor * root)17103 clutter_actor_iter_init (ClutterActorIter *iter,
17104                          ClutterActor     *root)
17105 {
17106   RealActorIter *ri = (RealActorIter *) iter;
17107 
17108   g_return_if_fail (iter != NULL);
17109   g_return_if_fail (CLUTTER_IS_ACTOR (root));
17110 
17111   ri->root = root;
17112   ri->current = NULL;
17113   ri->age = root->priv->age;
17114 }
17115 
17116 /**
17117  * clutter_actor_iter_is_valid:
17118  * @iter: a #ClutterActorIter
17119  *
17120  * Checks whether a #ClutterActorIter is still valid.
17121  *
17122  * An iterator is considered valid if it has been initialized, and
17123  * if the #ClutterActor that it refers to hasn't been modified after
17124  * the initialization.
17125  *
17126  * Return value: %TRUE if the iterator is valid, and %FALSE otherwise
17127  *
17128  * Since: 1.12
17129  */
17130 gboolean
clutter_actor_iter_is_valid(const ClutterActorIter * iter)17131 clutter_actor_iter_is_valid (const ClutterActorIter *iter)
17132 {
17133   RealActorIter *ri = (RealActorIter *) iter;
17134 
17135   g_return_val_if_fail (iter != NULL, FALSE);
17136 
17137   if (ri->root == NULL)
17138     return FALSE;
17139 
17140   return ri->root->priv->age == ri->age;
17141 }
17142 
17143 /**
17144  * clutter_actor_iter_next:
17145  * @iter: a #ClutterActorIter
17146  * @child: (out) (transfer none): return location for a #ClutterActor
17147  *
17148  * Advances the @iter and retrieves the next child of the root #ClutterActor
17149  * that was used to initialize the #ClutterActorIterator.
17150  *
17151  * If the iterator can advance, this function returns %TRUE and sets the
17152  * @child argument.
17153  *
17154  * If the iterator cannot advance, this function returns %FALSE, and
17155  * the contents of @child are undefined.
17156  *
17157  * Return value: %TRUE if the iterator could advance, and %FALSE otherwise.
17158  *
17159  * Since: 1.10
17160  */
17161 gboolean
clutter_actor_iter_next(ClutterActorIter * iter,ClutterActor ** child)17162 clutter_actor_iter_next (ClutterActorIter  *iter,
17163                          ClutterActor     **child)
17164 {
17165   RealActorIter *ri = (RealActorIter *) iter;
17166 
17167   g_return_val_if_fail (iter != NULL, FALSE);
17168   g_return_val_if_fail (ri->root != NULL, FALSE);
17169 #ifndef G_DISABLE_ASSERT
17170   g_return_val_if_fail (ri->age == ri->root->priv->age, FALSE);
17171 #endif
17172 
17173   if (ri->current == NULL)
17174     ri->current = ri->root->priv->first_child;
17175   else
17176     ri->current = ri->current->priv->next_sibling;
17177 
17178   if (child != NULL)
17179     *child = ri->current;
17180 
17181   return ri->current != NULL;
17182 }
17183 
17184 /**
17185  * clutter_actor_iter_prev:
17186  * @iter: a #ClutterActorIter
17187  * @child: (out) (transfer none): return location for a #ClutterActor
17188  *
17189  * Advances the @iter and retrieves the previous child of the root
17190  * #ClutterActor that was used to initialize the #ClutterActorIterator.
17191  *
17192  * If the iterator can advance, this function returns %TRUE and sets the
17193  * @child argument.
17194  *
17195  * If the iterator cannot advance, this function returns %FALSE, and
17196  * the contents of @child are undefined.
17197  *
17198  * Return value: %TRUE if the iterator could advance, and %FALSE otherwise.
17199  *
17200  * Since: 1.10
17201  */
17202 gboolean
clutter_actor_iter_prev(ClutterActorIter * iter,ClutterActor ** child)17203 clutter_actor_iter_prev (ClutterActorIter  *iter,
17204                          ClutterActor     **child)
17205 {
17206   RealActorIter *ri = (RealActorIter *) iter;
17207 
17208   g_return_val_if_fail (iter != NULL, FALSE);
17209   g_return_val_if_fail (ri->root != NULL, FALSE);
17210 #ifndef G_DISABLE_ASSERT
17211   g_return_val_if_fail (ri->age == ri->root->priv->age, FALSE);
17212 #endif
17213 
17214   if (ri->current == NULL)
17215     ri->current = ri->root->priv->last_child;
17216   else
17217     ri->current = ri->current->priv->prev_sibling;
17218 
17219   if (child != NULL)
17220     *child = ri->current;
17221 
17222   return ri->current != NULL;
17223 }
17224 
17225 /**
17226  * clutter_actor_iter_remove:
17227  * @iter: a #ClutterActorIter
17228  *
17229  * Safely removes the #ClutterActor currently pointer to by the iterator
17230  * from its parent.
17231  *
17232  * This function can only be called after clutter_actor_iter_next() or
17233  * clutter_actor_iter_prev() returned %TRUE, and cannot be called more
17234  * than once for the same actor.
17235  *
17236  * This function will call clutter_actor_remove_child() internally.
17237  *
17238  * Since: 1.10
17239  */
17240 void
clutter_actor_iter_remove(ClutterActorIter * iter)17241 clutter_actor_iter_remove (ClutterActorIter *iter)
17242 {
17243   RealActorIter *ri = (RealActorIter *) iter;
17244   ClutterActor *cur;
17245 
17246   g_return_if_fail (iter != NULL);
17247   g_return_if_fail (ri->root != NULL);
17248 #ifndef G_DISABLE_ASSERT
17249   g_return_if_fail (ri->age == ri->root->priv->age);
17250 #endif
17251   g_return_if_fail (ri->current != NULL);
17252 
17253   cur = ri->current;
17254 
17255   if (cur != NULL)
17256     {
17257       ri->current = cur->priv->prev_sibling;
17258 
17259       clutter_actor_remove_child_internal (ri->root, cur,
17260                                            REMOVE_CHILD_DEFAULT_FLAGS);
17261 
17262       ri->age += 1;
17263     }
17264 }
17265 
17266 /**
17267  * clutter_actor_iter_destroy:
17268  * @iter: a #ClutterActorIter
17269  *
17270  * Safely destroys the #ClutterActor currently pointer to by the iterator
17271  * from its parent.
17272  *
17273  * This function can only be called after clutter_actor_iter_next() or
17274  * clutter_actor_iter_prev() returned %TRUE, and cannot be called more
17275  * than once for the same actor.
17276  *
17277  * This function will call clutter_actor_destroy() internally.
17278  *
17279  * Since: 1.10
17280  */
17281 void
clutter_actor_iter_destroy(ClutterActorIter * iter)17282 clutter_actor_iter_destroy (ClutterActorIter *iter)
17283 {
17284   RealActorIter *ri = (RealActorIter *) iter;
17285   ClutterActor *cur;
17286 
17287   g_return_if_fail (iter != NULL);
17288   g_return_if_fail (ri->root != NULL);
17289 #ifndef G_DISABLE_ASSERT
17290   g_return_if_fail (ri->age == ri->root->priv->age);
17291 #endif
17292   g_return_if_fail (ri->current != NULL);
17293 
17294   cur = ri->current;
17295 
17296   if (cur != NULL)
17297     {
17298       ri->current = cur->priv->prev_sibling;
17299 
17300       clutter_actor_destroy (cur);
17301 
17302       ri->age += 1;
17303     }
17304 }
17305 
17306 static const ClutterAnimationInfo default_animation_info = {
17307   NULL,         /* states */
17308   NULL,         /* cur_state */
17309   NULL,         /* transitions */
17310 };
17311 
17312 static void
clutter_animation_info_free(gpointer data)17313 clutter_animation_info_free (gpointer data)
17314 {
17315   if (data != NULL)
17316     {
17317       ClutterAnimationInfo *info = data;
17318 
17319       if (info->transitions != NULL)
17320         g_hash_table_unref (info->transitions);
17321 
17322       if (info->states != NULL)
17323         g_array_unref (info->states);
17324 
17325       g_free (info);
17326     }
17327 }
17328 
17329 const ClutterAnimationInfo *
_clutter_actor_get_animation_info_or_defaults(ClutterActor * self)17330 _clutter_actor_get_animation_info_or_defaults (ClutterActor *self)
17331 {
17332   const ClutterAnimationInfo *res;
17333   GObject *obj = G_OBJECT (self);
17334 
17335   res = g_object_get_qdata (obj, quark_actor_animation_info);
17336   if (res != NULL)
17337     return res;
17338 
17339   return &default_animation_info;
17340 }
17341 
17342 ClutterAnimationInfo *
_clutter_actor_get_animation_info(ClutterActor * self)17343 _clutter_actor_get_animation_info (ClutterActor *self)
17344 {
17345   GObject *obj = G_OBJECT (self);
17346   ClutterAnimationInfo *res;
17347 
17348   res = g_object_get_qdata (obj, quark_actor_animation_info);
17349   if (res == NULL)
17350     {
17351       res = g_new0 (ClutterAnimationInfo, 1);
17352 
17353       *res = default_animation_info;
17354 
17355       g_object_set_qdata_full (obj, quark_actor_animation_info,
17356                                res,
17357                                clutter_animation_info_free);
17358     }
17359 
17360   return res;
17361 }
17362 
17363 static void
transition_closure_free(gpointer data)17364 transition_closure_free (gpointer data)
17365 {
17366   if (G_LIKELY (data != NULL))
17367     {
17368       TransitionClosure *clos = data;
17369       ClutterTimeline *timeline;
17370 
17371       timeline = CLUTTER_TIMELINE (clos->transition);
17372 
17373       /* we disconnect the signal handler before stopping the timeline,
17374        * so that we don't end up inside on_transition_stopped() from
17375        * a call to g_hash_table_remove().
17376        */
17377       g_clear_signal_handler (&clos->completed_id, clos->transition);
17378 
17379       if (clutter_timeline_is_playing (timeline))
17380         clutter_timeline_stop (timeline);
17381       else if (clutter_timeline_get_delay (timeline) > 0)
17382         clutter_timeline_cancel_delay (timeline);
17383 
17384       /* remove the reference added in add_transition_internal() */
17385       g_object_unref (clos->transition);
17386 
17387       g_free (clos->name);
17388 
17389       g_free (clos);
17390     }
17391 }
17392 
17393 static void
on_transition_stopped(ClutterTransition * transition,gboolean is_finished,TransitionClosure * clos)17394 on_transition_stopped (ClutterTransition *transition,
17395                        gboolean           is_finished,
17396                        TransitionClosure *clos)
17397 {
17398   ClutterActor *actor = clos->actor;
17399   ClutterAnimationInfo *info;
17400   GQuark t_quark;
17401   gchar *t_name;
17402 
17403   if (clos->name == NULL)
17404     return;
17405 
17406   /* reset the caches used by animations */
17407   clutter_actor_store_content_box (actor, NULL);
17408 
17409   info = _clutter_actor_get_animation_info (actor);
17410 
17411   /* we need copies because we emit the signal after the
17412    * TransitionClosure data structure has been freed
17413    */
17414   t_quark = g_quark_from_string (clos->name);
17415   t_name = g_strdup (clos->name);
17416 
17417   if (clutter_transition_get_remove_on_complete (transition))
17418     {
17419       /* this is safe, because the timeline has now stopped,
17420        * so we won't recurse; the reference on the Animatable
17421        * will be dropped by the ::stopped signal closure in
17422        * ClutterTransition, which is RUN_LAST, and thus will
17423        * be called after this handler
17424        */
17425       g_hash_table_remove (info->transitions, clos->name);
17426     }
17427 
17428   /* we emit the ::transition-stopped after removing the
17429    * transition, so that we can chain up new transitions
17430    * without interfering with the one that just finished
17431    */
17432   g_signal_emit (actor, actor_signals[TRANSITION_STOPPED], t_quark,
17433                  t_name,
17434                  is_finished);
17435 
17436   g_free (t_name);
17437 
17438   /* if it's the last transition then we clean up */
17439   if (g_hash_table_size (info->transitions) == 0)
17440     {
17441       g_hash_table_unref (info->transitions);
17442       info->transitions = NULL;
17443 
17444       CLUTTER_NOTE (ANIMATION, "Transitions for '%s' completed",
17445                     _clutter_actor_get_debug_name (actor));
17446 
17447       g_signal_emit (actor, actor_signals[TRANSITIONS_COMPLETED], 0);
17448     }
17449 }
17450 
17451 static void
clutter_actor_add_transition_internal(ClutterActor * self,const gchar * name,ClutterTransition * transition)17452 clutter_actor_add_transition_internal (ClutterActor *self,
17453                                        const gchar  *name,
17454                                        ClutterTransition *transition)
17455 {
17456   ClutterTimeline *timeline;
17457   TransitionClosure *clos;
17458   ClutterAnimationInfo *info;
17459 
17460   info = _clutter_actor_get_animation_info (self);
17461 
17462   if (info->transitions == NULL)
17463     info->transitions = g_hash_table_new_full (g_str_hash, g_str_equal,
17464                                                NULL,
17465                                                transition_closure_free);
17466 
17467   if (g_hash_table_lookup (info->transitions, name) != NULL)
17468     {
17469       g_warning ("A transition with name '%s' already exists for "
17470                  "the actor '%s'",
17471                  name,
17472                  _clutter_actor_get_debug_name (self));
17473       return;
17474     }
17475 
17476   clutter_transition_set_animatable (transition, CLUTTER_ANIMATABLE (self));
17477 
17478   timeline = CLUTTER_TIMELINE (transition);
17479 
17480   clos = g_new0 (TransitionClosure, 1);
17481   clos->actor = self;
17482   clos->transition = g_object_ref (transition);
17483   clos->name = g_strdup (name);
17484   clos->completed_id = g_signal_connect (timeline, "stopped",
17485                                          G_CALLBACK (on_transition_stopped),
17486                                          clos);
17487 
17488   CLUTTER_NOTE (ANIMATION,
17489                 "Adding transition '%s' [%p] to actor '%s'",
17490                 clos->name,
17491                 clos->transition,
17492                 _clutter_actor_get_debug_name (self));
17493 
17494   g_hash_table_insert (info->transitions, clos->name, clos);
17495   clutter_timeline_start (timeline);
17496 }
17497 
17498 static gboolean
should_skip_implicit_transition(ClutterActor * self,GParamSpec * pspec)17499 should_skip_implicit_transition (ClutterActor *self,
17500                                  GParamSpec   *pspec)
17501 {
17502   ClutterActorPrivate *priv = self->priv;
17503   const ClutterAnimationInfo *info;
17504 
17505   /* this function is called from _clutter_actor_create_transition() which
17506    * calls _clutter_actor_get_animation_info() first, so we're guaranteed
17507    * to have the correct ClutterAnimationInfo pointer
17508    */
17509   info = _clutter_actor_get_animation_info_or_defaults (self);
17510 
17511   /* if the easing state has a non-zero duration we always want an
17512    * implicit transition to occur
17513    */
17514   if (info->cur_state->easing_duration == 0)
17515     return TRUE;
17516 
17517   /* on the other hand, if the actor hasn't been allocated yet, we want to
17518    * skip all transitions on the :allocation, to avoid actors "flying in"
17519    * into their new position and size
17520    */
17521   if (pspec == obj_props[PROP_ALLOCATION] &&
17522       !clutter_actor_box_is_initialized (&priv->allocation))
17523     return TRUE;
17524 
17525   /* if the actor is not mapped and is not part of a branch of the scene
17526    * graph that is being cloned, then we always skip implicit transitions
17527    * on the account of the fact that the actor is not going to be visible
17528    * when those transitions happen
17529    */
17530   if (!CLUTTER_ACTOR_IS_MAPPED (self) &&
17531       !clutter_actor_has_mapped_clones (self))
17532     return TRUE;
17533 
17534   return FALSE;
17535 }
17536 
17537 /*< private >*
17538  * _clutter_actor_create_transition:
17539  * @actor: a #ClutterActor
17540  * @pspec: the property used for the transition
17541  * @...: initial and final state
17542  *
17543  * Creates a #ClutterTransition for the property represented by @pspec.
17544  *
17545  * Return value: a #ClutterTransition
17546  */
17547 ClutterTransition *
_clutter_actor_create_transition(ClutterActor * actor,GParamSpec * pspec,...)17548 _clutter_actor_create_transition (ClutterActor *actor,
17549                                   GParamSpec   *pspec,
17550                                   ...)
17551 {
17552   ClutterTimeline *timeline;
17553   ClutterInterval *interval;
17554   ClutterAnimationInfo *info;
17555   ClutterTransition *res = NULL;
17556   gboolean call_restore = FALSE;
17557   TransitionClosure *clos;
17558   va_list var_args;
17559   g_auto (GValue) initial = G_VALUE_INIT;
17560   g_auto (GValue) final = G_VALUE_INIT;
17561   GType ptype;
17562   char *error;
17563 
17564   g_assert (pspec != NULL);
17565   g_assert ((pspec->flags & CLUTTER_PARAM_ANIMATABLE) != 0);
17566 
17567   info = _clutter_actor_get_animation_info (actor);
17568 
17569   /* XXX - this will go away in 2.0
17570    *
17571    * if no state has been pushed, we assume that the easing state is
17572    * in "compatibility mode": all transitions have a duration of 0
17573    * msecs, which means that they happen immediately. in Clutter 2.0
17574    * this will turn into a g_assert(info->states != NULL), as every
17575    * actor will start with a predefined easing state
17576    */
17577   if (info->states == NULL)
17578     {
17579       clutter_actor_save_easing_state (actor);
17580       clutter_actor_set_easing_duration (actor, 0);
17581       call_restore = TRUE;
17582     }
17583 
17584   if (info->transitions == NULL)
17585     info->transitions = g_hash_table_new_full (g_str_hash, g_str_equal,
17586                                                NULL,
17587                                                transition_closure_free);
17588 
17589   va_start (var_args, pspec);
17590 
17591   ptype = G_PARAM_SPEC_VALUE_TYPE (pspec);
17592 
17593   G_VALUE_COLLECT_INIT (&initial, ptype,
17594                         var_args, 0,
17595                         &error);
17596   if (error != NULL)
17597     {
17598       g_critical ("%s: %s", G_STRLOC, error);
17599       g_free (error);
17600       goto out;
17601     }
17602 
17603   G_VALUE_COLLECT_INIT (&final, ptype,
17604                         var_args, 0,
17605                         &error);
17606   if (error != NULL)
17607     {
17608       g_critical ("%s: %s", G_STRLOC, error);
17609       g_free (error);
17610       goto out;
17611     }
17612 
17613   if (should_skip_implicit_transition (actor, pspec))
17614     {
17615       CLUTTER_NOTE (ANIMATION, "Skipping implicit transition for '%s::%s'",
17616                     _clutter_actor_get_debug_name (actor),
17617                     pspec->name);
17618 
17619       /* remove a transition, if one exists */
17620       clutter_actor_remove_transition (actor, pspec->name);
17621 
17622       /* we don't go through the Animatable interface because we
17623        * already know we got here through an animatable property.
17624        */
17625       clutter_actor_set_animatable_property (actor,
17626                                              pspec->param_id,
17627                                              &final,
17628                                              pspec);
17629 
17630       goto out;
17631     }
17632 
17633   clos = g_hash_table_lookup (info->transitions, pspec->name);
17634   if (clos == NULL)
17635     {
17636       res = clutter_property_transition_new (pspec->name);
17637 
17638       clutter_transition_set_remove_on_complete (res, TRUE);
17639 
17640       interval = clutter_interval_new_with_values (ptype, &initial, &final);
17641       clutter_transition_set_interval (res, interval);
17642 
17643       timeline = CLUTTER_TIMELINE (res);
17644       clutter_timeline_set_delay (timeline, info->cur_state->easing_delay);
17645       clutter_timeline_set_duration (timeline, info->cur_state->easing_duration);
17646       clutter_timeline_set_progress_mode (timeline, info->cur_state->easing_mode);
17647 
17648 #ifdef CLUTTER_ENABLE_DEBUG
17649       if (CLUTTER_HAS_DEBUG (ANIMATION))
17650         {
17651           gchar *initial_v, *final_v;
17652 
17653           initial_v = g_strdup_value_contents (&initial);
17654           final_v = g_strdup_value_contents (&final);
17655 
17656           CLUTTER_NOTE (ANIMATION,
17657                         "Created transition for %s:%s "
17658                         "(len:%u, mode:%s, delay:%u) "
17659                         "initial:%s, final:%s",
17660                         _clutter_actor_get_debug_name (actor),
17661                         pspec->name,
17662                         info->cur_state->easing_duration,
17663                         clutter_get_easing_name_for_mode (info->cur_state->easing_mode),
17664                         info->cur_state->easing_delay,
17665                         initial_v, final_v);
17666 
17667           g_free (initial_v);
17668           g_free (final_v);
17669         }
17670 #endif /* CLUTTER_ENABLE_DEBUG */
17671 
17672       /* this will start the transition as well */
17673       clutter_actor_add_transition_internal (actor, pspec->name, res);
17674 
17675       /* the actor now owns the transition */
17676       g_object_unref (res);
17677     }
17678   else
17679     {
17680       ClutterAnimationMode cur_mode;
17681       guint cur_duration;
17682 
17683       CLUTTER_NOTE (ANIMATION, "Existing transition for %s:%s",
17684                     _clutter_actor_get_debug_name (actor),
17685                     pspec->name);
17686 
17687       timeline = CLUTTER_TIMELINE (clos->transition);
17688 
17689       cur_duration = clutter_timeline_get_duration (timeline);
17690       if (cur_duration != info->cur_state->easing_duration)
17691         clutter_timeline_set_duration (timeline, info->cur_state->easing_duration);
17692 
17693       cur_mode = clutter_timeline_get_progress_mode (timeline);
17694       if (cur_mode != info->cur_state->easing_mode)
17695         clutter_timeline_set_progress_mode (timeline, info->cur_state->easing_mode);
17696 
17697       clutter_timeline_rewind (timeline);
17698 
17699       interval = clutter_transition_get_interval (clos->transition);
17700       clutter_interval_set_initial_value (interval, &initial);
17701       clutter_interval_set_final_value (interval, &final);
17702 
17703       res = clos->transition;
17704     }
17705 
17706 out:
17707   if (call_restore)
17708     clutter_actor_restore_easing_state (actor);
17709 
17710   va_end (var_args);
17711 
17712   return res;
17713 }
17714 
17715 /**
17716  * clutter_actor_add_transition:
17717  * @self: a #ClutterActor
17718  * @name: the name of the transition to add
17719  * @transition: the #ClutterTransition to add
17720  *
17721  * Adds a @transition to the #ClutterActor's list of animations.
17722  *
17723  * The @name string is a per-actor unique identifier of the @transition: only
17724  * one #ClutterTransition can be associated to the specified @name.
17725  *
17726  * The @transition will be started once added.
17727  *
17728  * This function will take a reference on the @transition.
17729  *
17730  * This function is usually called implicitly when modifying an animatable
17731  * property.
17732  *
17733  * Since: 1.10
17734  */
17735 void
clutter_actor_add_transition(ClutterActor * self,const char * name,ClutterTransition * transition)17736 clutter_actor_add_transition (ClutterActor      *self,
17737                               const char        *name,
17738                               ClutterTransition *transition)
17739 {
17740   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17741   g_return_if_fail (name != NULL);
17742   g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
17743 
17744   clutter_actor_add_transition_internal (self, name, transition);
17745 }
17746 
17747 /**
17748  * clutter_actor_remove_transition:
17749  * @self: a #ClutterActor
17750  * @name: the name of the transition to remove
17751  *
17752  * Removes the transition stored inside a #ClutterActor using @name
17753  * identifier.
17754  *
17755  * If the transition is currently in progress, it will be stopped.
17756  *
17757  * This function releases the reference acquired when the transition
17758  * was added to the #ClutterActor.
17759  *
17760  * Since: 1.10
17761  */
17762 void
clutter_actor_remove_transition(ClutterActor * self,const char * name)17763 clutter_actor_remove_transition (ClutterActor *self,
17764                                  const char   *name)
17765 {
17766   const ClutterAnimationInfo *info;
17767   TransitionClosure *clos;
17768   gboolean was_playing;
17769   GQuark t_quark;
17770   gchar *t_name;
17771 
17772   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17773   g_return_if_fail (name != NULL);
17774 
17775   info = _clutter_actor_get_animation_info_or_defaults (self);
17776 
17777   if (info->transitions == NULL)
17778     return;
17779 
17780   clos = g_hash_table_lookup (info->transitions, name);
17781   if (clos == NULL)
17782     return;
17783 
17784   was_playing =
17785     clutter_timeline_is_playing (CLUTTER_TIMELINE (clos->transition));
17786   t_quark = g_quark_from_string (clos->name);
17787   t_name = g_strdup (clos->name);
17788 
17789   g_hash_table_remove (info->transitions, name);
17790 
17791   /* we want to maintain the invariant that ::transition-stopped is
17792    * emitted after the transition has been removed, to allow replacing
17793    * or chaining; removing the transition from the hash table will
17794    * stop it, but transition_closure_free() will disconnect the signal
17795    * handler we install in add_transition_internal(), to avoid loops
17796    * or segfaults.
17797    *
17798    * since we know already that a transition will stop once it's removed
17799    * from an actor, we can simply emit the ::transition-stopped here
17800    * ourselves, if the timeline was playing (if it wasn't, then the
17801    * signal was already emitted at least once).
17802    */
17803   if (was_playing)
17804     {
17805       g_signal_emit (self, actor_signals[TRANSITION_STOPPED],
17806                      t_quark,
17807                      t_name,
17808                      FALSE);
17809     }
17810 
17811   g_free (t_name);
17812 }
17813 
17814 /**
17815  * clutter_actor_remove_all_transitions:
17816  * @self: a #ClutterActor
17817  *
17818  * Removes all transitions associated to @self.
17819  *
17820  * Since: 1.10
17821  */
17822 void
clutter_actor_remove_all_transitions(ClutterActor * self)17823 clutter_actor_remove_all_transitions (ClutterActor *self)
17824 {
17825   const ClutterAnimationInfo *info;
17826 
17827   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17828 
17829   info = _clutter_actor_get_animation_info_or_defaults (self);
17830   if (info->transitions == NULL)
17831     return;
17832 
17833   g_hash_table_remove_all (info->transitions);
17834 }
17835 
17836 /**
17837  * clutter_actor_set_easing_duration:
17838  * @self: a #ClutterActor
17839  * @msecs: the duration of the easing, or %NULL
17840  *
17841  * Sets the duration of the tweening for animatable properties
17842  * of @self for the current easing state.
17843  *
17844  * Since: 1.10
17845  */
17846 void
clutter_actor_set_easing_duration(ClutterActor * self,guint msecs)17847 clutter_actor_set_easing_duration (ClutterActor *self,
17848                                    guint         msecs)
17849 {
17850   ClutterAnimationInfo *info;
17851 
17852   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17853 
17854   info = _clutter_actor_get_animation_info (self);
17855 
17856   if (info->cur_state == NULL)
17857     {
17858       g_warning ("You must call clutter_actor_save_easing_state() prior "
17859                  "to calling clutter_actor_set_easing_duration().");
17860       return;
17861     }
17862 
17863   if (info->cur_state->easing_duration != msecs)
17864     info->cur_state->easing_duration = msecs;
17865 }
17866 
17867 /**
17868  * clutter_actor_get_easing_duration:
17869  * @self: a #ClutterActor
17870  *
17871  * Retrieves the duration of the tweening for animatable
17872  * properties of @self for the current easing state.
17873  *
17874  * Return value: the duration of the tweening, in milliseconds
17875  *
17876  * Since: 1.10
17877  */
17878 guint
clutter_actor_get_easing_duration(ClutterActor * self)17879 clutter_actor_get_easing_duration (ClutterActor *self)
17880 {
17881   const ClutterAnimationInfo *info;
17882 
17883   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
17884 
17885   info = _clutter_actor_get_animation_info_or_defaults (self);
17886 
17887   if (info->cur_state != NULL)
17888     return info->cur_state->easing_duration;
17889 
17890   return 0;
17891 }
17892 
17893 /**
17894  * clutter_actor_set_easing_mode:
17895  * @self: a #ClutterActor
17896  * @mode: an easing mode, excluding %CLUTTER_CUSTOM_MODE
17897  *
17898  * Sets the easing mode for the tweening of animatable properties
17899  * of @self.
17900  *
17901  * Since: 1.10
17902  */
17903 void
clutter_actor_set_easing_mode(ClutterActor * self,ClutterAnimationMode mode)17904 clutter_actor_set_easing_mode (ClutterActor         *self,
17905                                ClutterAnimationMode  mode)
17906 {
17907   ClutterAnimationInfo *info;
17908 
17909   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17910   g_return_if_fail (mode != CLUTTER_CUSTOM_MODE);
17911   g_return_if_fail (mode < CLUTTER_ANIMATION_LAST);
17912 
17913   info = _clutter_actor_get_animation_info (self);
17914 
17915   if (info->cur_state == NULL)
17916     {
17917       g_warning ("You must call clutter_actor_save_easing_state() prior "
17918                  "to calling clutter_actor_set_easing_mode().");
17919       return;
17920     }
17921 
17922   if (info->cur_state->easing_mode != mode)
17923     info->cur_state->easing_mode = mode;
17924 }
17925 
17926 /**
17927  * clutter_actor_get_easing_mode:
17928  * @self: a #ClutterActor
17929  *
17930  * Retrieves the easing mode for the tweening of animatable properties
17931  * of @self for the current easing state.
17932  *
17933  * Return value: an easing mode
17934  *
17935  * Since: 1.10
17936  */
17937 ClutterAnimationMode
clutter_actor_get_easing_mode(ClutterActor * self)17938 clutter_actor_get_easing_mode (ClutterActor *self)
17939 {
17940   const ClutterAnimationInfo *info;
17941 
17942   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_EASE_OUT_CUBIC);
17943 
17944   info = _clutter_actor_get_animation_info_or_defaults (self);
17945 
17946   if (info->cur_state != NULL)
17947     return info->cur_state->easing_mode;
17948 
17949   return CLUTTER_EASE_OUT_CUBIC;
17950 }
17951 
17952 /**
17953  * clutter_actor_set_easing_delay:
17954  * @self: a #ClutterActor
17955  * @msecs: the delay before the start of the tweening, in milliseconds
17956  *
17957  * Sets the delay that should be applied before tweening animatable
17958  * properties.
17959  *
17960  * Since: 1.10
17961  */
17962 void
clutter_actor_set_easing_delay(ClutterActor * self,guint msecs)17963 clutter_actor_set_easing_delay (ClutterActor *self,
17964                                 guint         msecs)
17965 {
17966   ClutterAnimationInfo *info;
17967 
17968   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17969 
17970   info = _clutter_actor_get_animation_info (self);
17971 
17972   if (info->cur_state == NULL)
17973     {
17974       g_warning ("You must call clutter_actor_save_easing_state() prior "
17975                  "to calling clutter_actor_set_easing_delay().");
17976       return;
17977     }
17978 
17979   if (info->cur_state->easing_delay != msecs)
17980     info->cur_state->easing_delay = msecs;
17981 }
17982 
17983 /**
17984  * clutter_actor_get_easing_delay:
17985  * @self: a #ClutterActor
17986  *
17987  * Retrieves the delay that should be applied when tweening animatable
17988  * properties.
17989  *
17990  * Return value: a delay, in milliseconds
17991  *
17992  * Since: 1.10
17993  */
17994 guint
clutter_actor_get_easing_delay(ClutterActor * self)17995 clutter_actor_get_easing_delay (ClutterActor *self)
17996 {
17997   const ClutterAnimationInfo *info;
17998 
17999   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
18000 
18001   info = _clutter_actor_get_animation_info_or_defaults (self);
18002 
18003   if (info->cur_state != NULL)
18004     return info->cur_state->easing_delay;
18005 
18006   return 0;
18007 }
18008 
18009 /**
18010  * clutter_actor_get_transition:
18011  * @self: a #ClutterActor
18012  * @name: the name of the transition
18013  *
18014  * Retrieves the #ClutterTransition of a #ClutterActor by using the
18015  * transition @name.
18016  *
18017  * Transitions created for animatable properties use the name of the
18018  * property itself, for instance the code below:
18019  *
18020  * |[<!-- language="C" -->
18021  *   clutter_actor_set_easing_duration (actor, 1000);
18022  *   clutter_actor_set_rotation_angle (actor, CLUTTER_Y_AXIS, 360.0);
18023  *
18024  *   transition = clutter_actor_get_transition (actor, "rotation-angle-y");
18025  *   g_signal_connect (transition, "stopped",
18026  *                     G_CALLBACK (on_transition_stopped),
18027  *                     actor);
18028  * ]|
18029  *
18030  * will call the `on_transition_stopped` callback when the transition
18031  * is finished.
18032  *
18033  * If you just want to get notifications of the completion of a transition,
18034  * you should use the #ClutterActor::transition-stopped signal, using the
18035  * transition name as the signal detail.
18036  *
18037  * Return value: (transfer none): a #ClutterTransition, or %NULL is none
18038  *   was found to match the passed name; the returned instance is owned
18039  *   by Clutter and it should not be freed
18040  *
18041  * Since: 1.10
18042  */
18043 ClutterTransition *
clutter_actor_get_transition(ClutterActor * self,const char * name)18044 clutter_actor_get_transition (ClutterActor *self,
18045                               const char   *name)
18046 {
18047   TransitionClosure *clos;
18048   const ClutterAnimationInfo *info;
18049 
18050   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18051   g_return_val_if_fail (name != NULL, NULL);
18052 
18053   info = _clutter_actor_get_animation_info_or_defaults (self);
18054   if (info->transitions == NULL)
18055     return NULL;
18056 
18057   clos = g_hash_table_lookup (info->transitions, name);
18058   if (clos == NULL)
18059     return NULL;
18060 
18061   return clos->transition;
18062 }
18063 
18064 /**
18065  * clutter_actor_has_transitions: (skip)
18066  */
18067 gboolean
clutter_actor_has_transitions(ClutterActor * self)18068 clutter_actor_has_transitions (ClutterActor *self)
18069 {
18070   const ClutterAnimationInfo *info;
18071 
18072   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
18073 
18074   info = _clutter_actor_get_animation_info_or_defaults (self);
18075   if (info->transitions == NULL)
18076     return FALSE;
18077 
18078   return g_hash_table_size (info->transitions) > 0;
18079 }
18080 
18081 /**
18082  * clutter_actor_save_easing_state:
18083  * @self: a #ClutterActor
18084  *
18085  * Saves the current easing state for animatable properties, and creates
18086  * a new state with the default values for easing mode and duration.
18087  *
18088  * New transitions created after calling this function will inherit the
18089  * duration, easing mode, and delay of the new easing state; this also
18090  * applies to transitions modified in flight.
18091  *
18092  * Since: 1.10
18093  */
18094 void
clutter_actor_save_easing_state(ClutterActor * self)18095 clutter_actor_save_easing_state (ClutterActor *self)
18096 {
18097   ClutterAnimationInfo *info;
18098   AState new_state;
18099 
18100   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18101 
18102   info = _clutter_actor_get_animation_info (self);
18103 
18104   if (info->states == NULL)
18105     info->states = g_array_new (FALSE, FALSE, sizeof (AState));
18106 
18107   new_state.easing_mode = CLUTTER_EASE_OUT_CUBIC;
18108   new_state.easing_duration = 250;
18109   new_state.easing_delay = 0;
18110 
18111   g_array_append_val (info->states, new_state);
18112 
18113   info->cur_state = &g_array_index (info->states, AState, info->states->len - 1);
18114 }
18115 
18116 /**
18117  * clutter_actor_restore_easing_state:
18118  * @self: a #ClutterActor
18119  *
18120  * Restores the easing state as it was prior to a call to
18121  * clutter_actor_save_easing_state().
18122  *
18123  * Since: 1.10
18124  */
18125 void
clutter_actor_restore_easing_state(ClutterActor * self)18126 clutter_actor_restore_easing_state (ClutterActor *self)
18127 {
18128   ClutterAnimationInfo *info;
18129 
18130   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18131 
18132   info = _clutter_actor_get_animation_info (self);
18133 
18134   if (info->states == NULL)
18135     {
18136       g_critical ("The function clutter_actor_restore_easing_state() has "
18137                   "been called without a previous call to "
18138                   "clutter_actor_save_easing_state().");
18139       return;
18140     }
18141 
18142   g_array_remove_index (info->states, info->states->len - 1);
18143 
18144   if (info->states->len > 0)
18145     info->cur_state = &g_array_index (info->states, AState, info->states->len - 1);
18146   else
18147     {
18148       g_array_unref (info->states);
18149       info->states = NULL;
18150       info->cur_state = NULL;
18151     }
18152 }
18153 
18154 /**
18155  * clutter_actor_set_content:
18156  * @self: a #ClutterActor
18157  * @content: (allow-none): a #ClutterContent, or %NULL
18158  *
18159  * Sets the contents of a #ClutterActor.
18160  *
18161  * Since: 1.10
18162  */
18163 void
clutter_actor_set_content(ClutterActor * self,ClutterContent * content)18164 clutter_actor_set_content (ClutterActor   *self,
18165                            ClutterContent *content)
18166 {
18167   ClutterActorPrivate *priv;
18168 
18169   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18170   g_return_if_fail (content == NULL || CLUTTER_IS_CONTENT (content));
18171 
18172   priv = self->priv;
18173 
18174   if (priv->content == content)
18175     return;
18176 
18177   if (priv->content != NULL)
18178     {
18179       _clutter_content_detached (priv->content, self);
18180       g_clear_object (&priv->content);
18181     }
18182 
18183   priv->content = content;
18184 
18185   if (priv->content != NULL)
18186     {
18187       g_object_ref (priv->content);
18188       _clutter_content_attached (priv->content, self);
18189     }
18190 
18191   /* if the actor's preferred size is the content's preferred size,
18192    * then we need to conditionally queue a relayout here...
18193    */
18194   if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
18195     _clutter_actor_queue_only_relayout (self);
18196 
18197   clutter_actor_queue_redraw (self);
18198 
18199   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT]);
18200 
18201   /* if the content gravity is not resize-fill, and the new content has a
18202    * different preferred size than the previous one, then the content box
18203    * may have been changed. since we compute that lazily, we just notify
18204    * here, and let whomever watches :content-box do whatever they need to
18205    * do.
18206    */
18207   if (priv->content_gravity != CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
18208     {
18209       if (priv->content_box_valid)
18210         {
18211           ClutterActorBox from_box, to_box;
18212 
18213           clutter_actor_get_content_box (self, &from_box);
18214 
18215           /* invalidate the cached content box */
18216           priv->content_box_valid = FALSE;
18217           clutter_actor_get_content_box (self, &to_box);
18218 
18219           if (!clutter_actor_box_equal (&from_box, &to_box))
18220             _clutter_actor_create_transition (self, obj_props[PROP_CONTENT_BOX],
18221                                               &from_box,
18222                                               &to_box);
18223         }
18224 
18225       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
18226    }
18227 }
18228 
18229 /**
18230  * clutter_actor_get_content:
18231  * @self: a #ClutterActor
18232  *
18233  * Retrieves the contents of @self.
18234  *
18235  * Return value: (transfer none): a pointer to the #ClutterContent instance,
18236  *   or %NULL if none was set
18237  *
18238  * Since: 1.10
18239  */
18240 ClutterContent *
clutter_actor_get_content(ClutterActor * self)18241 clutter_actor_get_content (ClutterActor *self)
18242 {
18243   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18244 
18245   return self->priv->content;
18246 }
18247 
18248 /**
18249  * clutter_actor_set_content_gravity:
18250  * @self: a #ClutterActor
18251  * @gravity: the #ClutterContentGravity
18252  *
18253  * Sets the gravity of the #ClutterContent used by @self.
18254  *
18255  * See the description of the #ClutterActor:content-gravity property for
18256  * more information.
18257  *
18258  * The #ClutterActor:content-gravity property is animatable.
18259  *
18260  * Since: 1.10
18261  */
18262 void
clutter_actor_set_content_gravity(ClutterActor * self,ClutterContentGravity gravity)18263 clutter_actor_set_content_gravity (ClutterActor *self,
18264                                    ClutterContentGravity  gravity)
18265 {
18266   ClutterActorPrivate *priv;
18267   ClutterActorBox from_box, to_box;
18268 
18269   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18270 
18271   priv = self->priv;
18272 
18273   if (priv->content_gravity == gravity)
18274     return;
18275 
18276   priv->content_box_valid = FALSE;
18277 
18278   clutter_actor_get_content_box (self, &from_box);
18279 
18280   priv->content_gravity = gravity;
18281 
18282   clutter_actor_get_content_box (self, &to_box);
18283 
18284   _clutter_actor_create_transition (self, obj_props[PROP_CONTENT_BOX],
18285                                     &from_box,
18286                                     &to_box);
18287 
18288   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_GRAVITY]);
18289 }
18290 
18291 /**
18292  * clutter_actor_get_content_gravity:
18293  * @self: a #ClutterActor
18294  *
18295  * Retrieves the content gravity as set using
18296  * clutter_actor_set_content_gravity().
18297  *
18298  * Return value: the content gravity
18299  *
18300  * Since: 1.10
18301  */
18302 ClutterContentGravity
clutter_actor_get_content_gravity(ClutterActor * self)18303 clutter_actor_get_content_gravity (ClutterActor *self)
18304 {
18305   g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
18306                         CLUTTER_CONTENT_GRAVITY_RESIZE_FILL);
18307 
18308   return self->priv->content_gravity;
18309 }
18310 
18311 /**
18312  * clutter_actor_get_content_box:
18313  * @self: a #ClutterActor
18314  * @box: (out caller-allocates): the return location for the bounding
18315  *   box for the #ClutterContent
18316  *
18317  * Retrieves the bounding box for the #ClutterContent of @self.
18318  *
18319  * The bounding box is relative to the actor's allocation.
18320  *
18321  * If no #ClutterContent is set for @self, or if @self has not been
18322  * allocated yet, then the result is undefined.
18323  *
18324  * The content box is guaranteed to be, at most, as big as the allocation
18325  * of the #ClutterActor.
18326  *
18327  * If the #ClutterContent used by the actor has a preferred size, then
18328  * it is possible to modify the content box by using the
18329  * #ClutterActor:content-gravity property.
18330  *
18331  * Since: 1.10
18332  */
18333 void
clutter_actor_get_content_box(ClutterActor * self,ClutterActorBox * box)18334 clutter_actor_get_content_box (ClutterActor    *self,
18335                                ClutterActorBox *box)
18336 {
18337   ClutterActorPrivate *priv;
18338   gfloat content_w, content_h;
18339   gfloat alloc_w, alloc_h;
18340 
18341   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18342   g_return_if_fail (box != NULL);
18343 
18344   priv = self->priv;
18345 
18346   box->x1 = 0.f;
18347   box->y1 = 0.f;
18348   box->x2 = priv->allocation.x2 - priv->allocation.x1;
18349   box->y2 = priv->allocation.y2 - priv->allocation.y1;
18350 
18351   if (priv->content_box_valid)
18352     {
18353       *box = priv->content_box;
18354       return;
18355     }
18356 
18357   /* no need to do any more work */
18358   if (priv->content_gravity == CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
18359     return;
18360 
18361   if (priv->content == NULL)
18362     return;
18363 
18364   /* if the content does not have a preferred size then there is
18365    * no point in computing the content box
18366    */
18367   if (!clutter_content_get_preferred_size (priv->content,
18368                                            &content_w,
18369                                            &content_h))
18370     return;
18371 
18372   alloc_w = box->x2;
18373   alloc_h = box->y2;
18374 
18375   switch (priv->content_gravity)
18376     {
18377     case CLUTTER_CONTENT_GRAVITY_TOP_LEFT:
18378       box->x2 = box->x1 + MIN (content_w, alloc_w);
18379       box->y2 = box->y1 + MIN (content_h, alloc_h);
18380       break;
18381 
18382     case CLUTTER_CONTENT_GRAVITY_TOP:
18383       if (alloc_w > content_w)
18384         {
18385           box->x1 += ceilf ((alloc_w - content_w) / 2.0);
18386           box->x2 = box->x1 + content_w;
18387         }
18388       box->y2 = box->y1 + MIN (content_h, alloc_h);
18389       break;
18390 
18391     case CLUTTER_CONTENT_GRAVITY_TOP_RIGHT:
18392       if (alloc_w > content_w)
18393         {
18394           box->x1 += (alloc_w - content_w);
18395           box->x2 = box->x1 + content_w;
18396         }
18397       box->y2 = box->y1 + MIN (content_h, alloc_h);
18398       break;
18399 
18400     case CLUTTER_CONTENT_GRAVITY_LEFT:
18401       box->x2 = box->x1 + MIN (content_w, alloc_w);
18402       if (alloc_h > content_h)
18403         {
18404           box->y1 += ceilf ((alloc_h - content_h) / 2.0);
18405           box->y2 = box->y1 + content_h;
18406         }
18407       break;
18408 
18409     case CLUTTER_CONTENT_GRAVITY_CENTER:
18410       if (alloc_w > content_w)
18411         {
18412           box->x1 += ceilf ((alloc_w - content_w) / 2.0);
18413           box->x2 = box->x1 + content_w;
18414         }
18415       if (alloc_h > content_h)
18416         {
18417           box->y1 += ceilf ((alloc_h - content_h) / 2.0);
18418           box->y2 = box->y1 + content_h;
18419         }
18420       break;
18421 
18422     case CLUTTER_CONTENT_GRAVITY_RIGHT:
18423       if (alloc_w > content_w)
18424         {
18425           box->x1 += (alloc_w - content_w);
18426           box->x2 = box->x1 + content_w;
18427         }
18428       if (alloc_h > content_h)
18429         {
18430           box->y1 += ceilf ((alloc_h - content_h) / 2.0);
18431           box->y2 = box->y1 + content_h;
18432         }
18433       break;
18434 
18435     case CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT:
18436       box->x2 = box->x1 + MIN (content_w, alloc_w);
18437       if (alloc_h > content_h)
18438         {
18439           box->y1 += (alloc_h - content_h);
18440           box->y2 = box->y1 + content_h;
18441         }
18442       break;
18443 
18444     case CLUTTER_CONTENT_GRAVITY_BOTTOM:
18445       if (alloc_w > content_w)
18446         {
18447           box->x1 += ceilf ((alloc_w - content_w) / 2.0);
18448           box->x2 = box->x1 + content_w;
18449         }
18450       if (alloc_h > content_h)
18451         {
18452           box->y1 += (alloc_h - content_h);
18453           box->y2 = box->y1 + content_h;
18454         }
18455       break;
18456 
18457     case CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT:
18458       if (alloc_w > content_w)
18459         {
18460           box->x1 += (alloc_w - content_w);
18461           box->x2 = box->x1 + content_w;
18462         }
18463       if (alloc_h > content_h)
18464         {
18465           box->y1 += (alloc_h - content_h);
18466           box->y2 = box->y1 + content_h;
18467         }
18468       break;
18469 
18470     case CLUTTER_CONTENT_GRAVITY_RESIZE_FILL:
18471       g_assert_not_reached ();
18472       break;
18473 
18474     case CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT:
18475       {
18476         double r_c = content_w / content_h;
18477 
18478         if ((alloc_w / r_c) > alloc_h)
18479           {
18480             box->y1 = 0.f;
18481             box->y2 = alloc_h;
18482 
18483             box->x1 = (alloc_w - (alloc_h * r_c)) / 2.0f;
18484             box->x2 = box->x1 + (alloc_h * r_c);
18485           }
18486         else
18487           {
18488             box->x1 = 0.f;
18489             box->x2 = alloc_w;
18490 
18491             box->y1 = (alloc_h - (alloc_w / r_c)) / 2.0f;
18492             box->y2 = box->y1 + (alloc_w / r_c);
18493           }
18494 
18495         CLUTTER_NOTE (LAYOUT,
18496                       "r_c: %.3f, r_a: %.3f\t"
18497                       "a: [%.2fx%.2f], c: [%.2fx%.2f]\t"
18498                       "b: [%.2f, %.2f, %.2f, %.2f]",
18499                       r_c, alloc_w / alloc_h,
18500                       alloc_w, alloc_h,
18501                       content_w, content_h,
18502                       box->x1, box->y1, box->x2, box->y2);
18503       }
18504       break;
18505     }
18506 }
18507 
18508 /**
18509  * clutter_actor_set_content_scaling_filters:
18510  * @self: a #ClutterActor
18511  * @min_filter: the minification filter for the content
18512  * @mag_filter: the magnification filter for the content
18513  *
18514  * Sets the minification and magnification filter to be applied when
18515  * scaling the #ClutterActor:content of a #ClutterActor.
18516  *
18517  * The #ClutterActor:minification-filter will be used when reducing
18518  * the size of the content; the #ClutterActor:magnification-filter
18519  * will be used when increasing the size of the content.
18520  *
18521  * Since: 1.10
18522  */
18523 void
clutter_actor_set_content_scaling_filters(ClutterActor * self,ClutterScalingFilter min_filter,ClutterScalingFilter mag_filter)18524 clutter_actor_set_content_scaling_filters (ClutterActor         *self,
18525                                            ClutterScalingFilter  min_filter,
18526                                            ClutterScalingFilter  mag_filter)
18527 {
18528   ClutterActorPrivate *priv;
18529   gboolean changed;
18530   GObject *obj;
18531 
18532   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18533 
18534   priv = self->priv;
18535   obj = G_OBJECT (self);
18536 
18537   g_object_freeze_notify (obj);
18538 
18539   changed = FALSE;
18540 
18541   if (priv->min_filter != min_filter)
18542     {
18543       priv->min_filter = min_filter;
18544       changed = TRUE;
18545 
18546       g_object_notify_by_pspec (obj, obj_props[PROP_MINIFICATION_FILTER]);
18547     }
18548 
18549   if (priv->mag_filter != mag_filter)
18550     {
18551       priv->mag_filter = mag_filter;
18552       changed = TRUE;
18553 
18554       g_object_notify_by_pspec (obj, obj_props[PROP_MAGNIFICATION_FILTER]);
18555     }
18556 
18557   if (changed)
18558     clutter_actor_queue_redraw (self);
18559 
18560   g_object_thaw_notify (obj);
18561 }
18562 
18563 /**
18564  * clutter_actor_get_content_scaling_filters:
18565  * @self: a #ClutterActor
18566  * @min_filter: (out) (allow-none): return location for the minification
18567  *   filter, or %NULL
18568  * @mag_filter: (out) (allow-none): return location for the magnification
18569  *   filter, or %NULL
18570  *
18571  * Retrieves the values set using clutter_actor_set_content_scaling_filters().
18572  *
18573  * Since: 1.10
18574  */
18575 void
clutter_actor_get_content_scaling_filters(ClutterActor * self,ClutterScalingFilter * min_filter,ClutterScalingFilter * mag_filter)18576 clutter_actor_get_content_scaling_filters (ClutterActor         *self,
18577                                            ClutterScalingFilter *min_filter,
18578                                            ClutterScalingFilter *mag_filter)
18579 {
18580   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18581 
18582   if (min_filter != NULL)
18583     *min_filter = self->priv->min_filter;
18584 
18585   if (mag_filter != NULL)
18586     *mag_filter = self->priv->mag_filter;
18587 }
18588 
18589 /*
18590  * clutter_actor_queue_compute_expand:
18591  * @self: a #ClutterActor
18592  *
18593  * Invalidates the needs_x_expand and needs_y_expand flags on @self
18594  * and its parents up to the top-level actor.
18595  *
18596  * This function also queues a relayout if anything changed.
18597  */
18598 static inline void
clutter_actor_queue_compute_expand(ClutterActor * self)18599 clutter_actor_queue_compute_expand (ClutterActor *self)
18600 {
18601   ClutterActor *parent;
18602   gboolean changed;
18603 
18604   if (self->priv->needs_compute_expand)
18605     return;
18606 
18607   changed = FALSE;
18608   parent = self;
18609   while (parent != NULL)
18610     {
18611       if (!parent->priv->needs_compute_expand)
18612         {
18613           parent->priv->needs_compute_expand = TRUE;
18614           changed = TRUE;
18615         }
18616 
18617       parent = parent->priv->parent;
18618     }
18619 
18620   if (changed)
18621     clutter_actor_queue_relayout (self);
18622 }
18623 
18624 /**
18625  * clutter_actor_set_x_expand:
18626  * @self: a #ClutterActor
18627  * @expand: whether the actor should expand horizontally
18628  *
18629  * Sets whether a #ClutterActor should expand horizontally; this means
18630  * that layout manager should allocate extra space for the actor, if
18631  * possible.
18632  *
18633  * Setting an actor to expand will also make all its parent expand, so
18634  * that it's possible to build an actor tree and only set this flag on
18635  * its leaves and not on every single actor.
18636  *
18637  * Since: 1.12
18638  */
18639 void
clutter_actor_set_x_expand(ClutterActor * self,gboolean expand)18640 clutter_actor_set_x_expand (ClutterActor *self,
18641                             gboolean      expand)
18642 {
18643   ClutterLayoutInfo *info;
18644 
18645   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18646 
18647   expand = !!expand;
18648 
18649   info = _clutter_actor_get_layout_info (self);
18650   if (info->x_expand != expand)
18651     {
18652       info->x_expand = expand;
18653 
18654       self->priv->x_expand_set = TRUE;
18655 
18656       clutter_actor_queue_compute_expand (self);
18657 
18658       g_object_notify_by_pspec (G_OBJECT (self),
18659                                 obj_props[PROP_X_EXPAND]);
18660     }
18661 }
18662 
18663 /**
18664  * clutter_actor_get_x_expand:
18665  * @self: a #ClutterActor
18666  *
18667  * Retrieves the value set with clutter_actor_set_x_expand().
18668  *
18669  * See also: clutter_actor_needs_expand()
18670  *
18671  * Return value: %TRUE if the actor has been set to expand
18672  *
18673  * Since: 1.12
18674  */
18675 gboolean
clutter_actor_get_x_expand(ClutterActor * self)18676 clutter_actor_get_x_expand (ClutterActor *self)
18677 {
18678   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
18679 
18680   return _clutter_actor_get_layout_info_or_defaults (self)->x_expand;
18681 }
18682 
18683 /**
18684  * clutter_actor_set_y_expand:
18685  * @self: a #ClutterActor
18686  * @expand: whether the actor should expand vertically
18687  *
18688  * Sets whether a #ClutterActor should expand horizontally; this means
18689  * that layout manager should allocate extra space for the actor, if
18690  * possible.
18691  *
18692  * Setting an actor to expand will also make all its parent expand, so
18693  * that it's possible to build an actor tree and only set this flag on
18694  * its leaves and not on every single actor.
18695  *
18696  * Since: 1.12
18697  */
18698 void
clutter_actor_set_y_expand(ClutterActor * self,gboolean expand)18699 clutter_actor_set_y_expand (ClutterActor *self,
18700                             gboolean      expand)
18701 {
18702   ClutterLayoutInfo *info;
18703 
18704   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18705 
18706   expand = !!expand;
18707 
18708   info = _clutter_actor_get_layout_info (self);
18709   if (info->y_expand != expand)
18710     {
18711       info->y_expand = expand;
18712 
18713       self->priv->y_expand_set = TRUE;
18714 
18715       clutter_actor_queue_compute_expand (self);
18716 
18717       g_object_notify_by_pspec (G_OBJECT (self),
18718                                 obj_props[PROP_Y_EXPAND]);
18719     }
18720 }
18721 
18722 /**
18723  * clutter_actor_get_y_expand:
18724  * @self: a #ClutterActor
18725  *
18726  * Retrieves the value set with clutter_actor_set_y_expand().
18727  *
18728  * See also: clutter_actor_needs_expand()
18729  *
18730  * Return value: %TRUE if the actor has been set to expand
18731  *
18732  * Since: 1.12
18733  */
18734 gboolean
clutter_actor_get_y_expand(ClutterActor * self)18735 clutter_actor_get_y_expand (ClutterActor *self)
18736 {
18737   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
18738 
18739   return _clutter_actor_get_layout_info_or_defaults (self)->y_expand;
18740 }
18741 
18742 static void
clutter_actor_compute_expand_recursive(ClutterActor * self,gboolean * x_expand_p,gboolean * y_expand_p)18743 clutter_actor_compute_expand_recursive (ClutterActor *self,
18744                                         gboolean     *x_expand_p,
18745                                         gboolean     *y_expand_p)
18746 {
18747   ClutterActorIter iter;
18748   ClutterActor *child;
18749   gboolean x_expand, y_expand;
18750 
18751   x_expand = y_expand = FALSE;
18752 
18753   /* note that we don't recurse into children if we're already set to expand;
18754    * this avoids traversing the whole actor tree, even if it may lead to some
18755    * child left with the needs_compute_expand flag set.
18756    */
18757   clutter_actor_iter_init (&iter, self);
18758   while (clutter_actor_iter_next (&iter, &child))
18759     {
18760       x_expand = x_expand ||
18761         clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL);
18762 
18763       y_expand = y_expand ||
18764         clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL);
18765     }
18766 
18767   *x_expand_p = x_expand;
18768   *y_expand_p = y_expand;
18769 }
18770 
18771 static void
clutter_actor_compute_expand(ClutterActor * self)18772 clutter_actor_compute_expand (ClutterActor *self)
18773 {
18774   if (self->priv->needs_compute_expand)
18775     {
18776       const ClutterLayoutInfo *info;
18777       gboolean x_expand, y_expand;
18778 
18779       info = _clutter_actor_get_layout_info_or_defaults (self);
18780 
18781       if (self->priv->x_expand_set)
18782         x_expand = info->x_expand;
18783       else
18784         x_expand = FALSE;
18785 
18786       if (self->priv->y_expand_set)
18787         y_expand = info->y_expand;
18788       else
18789         y_expand = FALSE;
18790 
18791       /* we don't need to recurse down to the children if the
18792        * actor has been forcibly set to expand
18793        */
18794       if (!(self->priv->x_expand_set && self->priv->y_expand_set))
18795         {
18796           if (self->priv->n_children != 0)
18797             {
18798               gboolean *x_expand_p, *y_expand_p;
18799               gboolean ignored = FALSE;
18800 
18801               x_expand_p = self->priv->x_expand_set ? &ignored : &x_expand;
18802               y_expand_p = self->priv->y_expand_set ? &ignored : &y_expand;
18803 
18804               clutter_actor_compute_expand_recursive (self,
18805                                                       x_expand_p,
18806                                                       y_expand_p);
18807             }
18808         }
18809 
18810       self->priv->needs_compute_expand = FALSE;
18811       self->priv->needs_x_expand = (x_expand != FALSE);
18812       self->priv->needs_y_expand = (y_expand != FALSE);
18813     }
18814 }
18815 
18816 /**
18817  * clutter_actor_needs_expand:
18818  * @self: a #ClutterActor
18819  * @orientation: the direction of expansion
18820  *
18821  * Checks whether an actor, or any of its children, is set to expand
18822  * horizontally or vertically.
18823  *
18824  * This function should only be called by layout managers that can
18825  * assign extra space to their children.
18826  *
18827  * If you want to know whether the actor was explicitly set to expand,
18828  * use clutter_actor_get_x_expand() or clutter_actor_get_y_expand().
18829  *
18830  * Return value: %TRUE if the actor should expand
18831  *
18832  * Since: 1.12
18833  */
18834 gboolean
clutter_actor_needs_expand(ClutterActor * self,ClutterOrientation orientation)18835 clutter_actor_needs_expand (ClutterActor       *self,
18836                             ClutterOrientation  orientation)
18837 {
18838   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
18839 
18840   if (!CLUTTER_ACTOR_IS_VISIBLE (self))
18841     return FALSE;
18842 
18843   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
18844     return FALSE;
18845 
18846   clutter_actor_compute_expand (self);
18847 
18848   switch (orientation)
18849     {
18850     case CLUTTER_ORIENTATION_HORIZONTAL:
18851       return self->priv->needs_x_expand;
18852 
18853     case CLUTTER_ORIENTATION_VERTICAL:
18854       return self->priv->needs_y_expand;
18855     }
18856 
18857   return FALSE;
18858 }
18859 
18860 /**
18861  * clutter_actor_set_content_repeat:
18862  * @self: a #ClutterActor
18863  * @repeat: the repeat policy
18864  *
18865  * Sets the policy for repeating the #ClutterActor:content of a
18866  * #ClutterActor. The behaviour is deferred to the #ClutterContent
18867  * implementation.
18868  *
18869  * Since: 1.12
18870  */
18871 void
clutter_actor_set_content_repeat(ClutterActor * self,ClutterContentRepeat repeat)18872 clutter_actor_set_content_repeat (ClutterActor         *self,
18873                                   ClutterContentRepeat  repeat)
18874 {
18875   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18876 
18877   if (self->priv->content_repeat == repeat)
18878     return;
18879 
18880   self->priv->content_repeat = repeat;
18881 
18882   clutter_actor_queue_redraw (self);
18883 }
18884 
18885 /**
18886  * clutter_actor_get_content_repeat:
18887  * @self: a #ClutterActor
18888  *
18889  * Retrieves the repeat policy for a #ClutterActor set by
18890  * clutter_actor_set_content_repeat().
18891  *
18892  * Return value: the content repeat policy
18893  *
18894  * Since: 1.12
18895  */
18896 ClutterContentRepeat
clutter_actor_get_content_repeat(ClutterActor * self)18897 clutter_actor_get_content_repeat (ClutterActor *self)
18898 {
18899   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_REPEAT_NONE);
18900 
18901   return self->priv->content_repeat;
18902 }
18903 
18904 void
_clutter_actor_handle_event(ClutterActor * self,const ClutterEvent * event)18905 _clutter_actor_handle_event (ClutterActor       *self,
18906                              const ClutterEvent *event)
18907 {
18908   GPtrArray *event_tree;
18909   ClutterActor *iter;
18910   gboolean is_key_event;
18911   gint i = 0;
18912 
18913   /* XXX - for historical reasons that are now lost in the mists of time,
18914    * key events are delivered regardless of whether an actor is set as
18915    * reactive; this should be changed for 2.0.
18916    */
18917   is_key_event = event->type == CLUTTER_KEY_PRESS ||
18918                  event->type == CLUTTER_KEY_RELEASE;
18919 
18920   event_tree = g_ptr_array_sized_new (64);
18921   g_ptr_array_set_free_func (event_tree, (GDestroyNotify) g_object_unref);
18922 
18923   /* build the list of of emitters for the event */
18924   iter = self;
18925   while (iter != NULL)
18926     {
18927       ClutterActor *parent = iter->priv->parent;
18928 
18929       if (CLUTTER_ACTOR_IS_REACTIVE (iter) || /* an actor must be reactive */
18930           parent == NULL ||                       /* unless it's the stage */
18931           is_key_event)                          /* or this is a key event */
18932         {
18933           /* keep a reference on the actor, so that it remains valid
18934            * for the duration of the signal emission
18935            */
18936           g_ptr_array_add (event_tree, g_object_ref (iter));
18937         }
18938 
18939       iter = parent;
18940     }
18941 
18942   /* Capture: from top-level downwards */
18943   for (i = event_tree->len - 1; i >= 0; i--)
18944     if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, TRUE))
18945       goto done;
18946 
18947   /* Bubble: from source upwards */
18948   for (i = 0; i < event_tree->len; i++)
18949     if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, FALSE))
18950       goto done;
18951 
18952 done:
18953   g_ptr_array_free (event_tree, TRUE);
18954 }
18955 
18956 static void
clutter_actor_set_child_transform_internal(ClutterActor * self,const graphene_matrix_t * transform)18957 clutter_actor_set_child_transform_internal (ClutterActor            *self,
18958                                             const graphene_matrix_t *transform)
18959 {
18960   ClutterTransformInfo *info = _clutter_actor_get_transform_info (self);
18961   ClutterActorIter iter;
18962   ClutterActor *child;
18963   GObject *obj;
18964   gboolean was_set = info->child_transform_set;
18965 
18966   graphene_matrix_init_from_matrix (&info->child_transform, transform);
18967 
18968   /* if it's the identity matrix, we need to toggle the boolean flag */
18969   info->child_transform_set = !graphene_matrix_is_identity (transform);
18970 
18971   /* we need to reset the transform_valid flag on each child */
18972   clutter_actor_iter_init (&iter, self);
18973   while (clutter_actor_iter_next (&iter, &child))
18974     transform_changed (child);
18975 
18976   clutter_actor_queue_redraw (self);
18977 
18978   obj = G_OBJECT (self);
18979   g_object_notify_by_pspec (obj, obj_props[PROP_CHILD_TRANSFORM]);
18980 
18981   if (was_set != info->child_transform_set)
18982     g_object_notify_by_pspec (obj, obj_props[PROP_CHILD_TRANSFORM_SET]);
18983 }
18984 
18985 /**
18986  * clutter_actor_set_child_transform:
18987  * @self: a #ClutterActor
18988  * @transform: (allow-none): a #graphene_matrix_t, or %NULL
18989  *
18990  * Sets the transformation matrix to be applied to all the children
18991  * of @self prior to their own transformations. The default child
18992  * transformation is the identity matrix.
18993  *
18994  * If @transform is %NULL, the child transform will be unset.
18995  *
18996  * The #ClutterActor:child-transform property is animatable.
18997  *
18998  * Since: 1.12
18999  */
19000 void
clutter_actor_set_child_transform(ClutterActor * self,const graphene_matrix_t * transform)19001 clutter_actor_set_child_transform (ClutterActor            *self,
19002                                    const graphene_matrix_t *transform)
19003 {
19004   const ClutterTransformInfo *info;
19005   graphene_matrix_t new_transform;
19006 
19007   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19008 
19009   info = _clutter_actor_get_transform_info_or_defaults (self);
19010 
19011   if (transform != NULL)
19012     graphene_matrix_init_from_matrix (&new_transform, transform);
19013   else
19014     graphene_matrix_init_identity (&new_transform);
19015 
19016   _clutter_actor_create_transition (self, obj_props[PROP_CHILD_TRANSFORM],
19017                                     &info->child_transform,
19018                                     &new_transform);
19019 }
19020 
19021 /**
19022  * clutter_actor_get_child_transform:
19023  * @self: a #ClutterActor
19024  * @transform: (out caller-allocates): a #graphene_matrix_t
19025  *
19026  * Retrieves the child transformation matrix set using
19027  * clutter_actor_set_child_transform(); if none is currently set,
19028  * the @transform matrix will be initialized to the identity matrix.
19029  *
19030  * Since: 1.12
19031  */
19032 void
clutter_actor_get_child_transform(ClutterActor * self,graphene_matrix_t * transform)19033 clutter_actor_get_child_transform (ClutterActor      *self,
19034                                    graphene_matrix_t *transform)
19035 {
19036   const ClutterTransformInfo *info;
19037 
19038   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19039   g_return_if_fail (transform != NULL);
19040 
19041   info = _clutter_actor_get_transform_info_or_defaults (self);
19042 
19043   if (info->child_transform_set)
19044     graphene_matrix_init_from_matrix (transform, &info->child_transform);
19045   else
19046     graphene_matrix_init_identity (transform);
19047 }
19048 
19049 static void
clutter_actor_push_in_cloned_branch(ClutterActor * self,gulong count)19050 clutter_actor_push_in_cloned_branch (ClutterActor *self,
19051                                      gulong        count)
19052 {
19053   ClutterActor *iter;
19054 
19055   for (iter = self->priv->first_child;
19056        iter != NULL;
19057        iter = iter->priv->next_sibling)
19058     clutter_actor_push_in_cloned_branch (iter, count);
19059 
19060   self->priv->in_cloned_branch += count;
19061 }
19062 
19063 static void
clutter_actor_pop_in_cloned_branch(ClutterActor * self,gulong count)19064 clutter_actor_pop_in_cloned_branch (ClutterActor *self,
19065                                     gulong        count)
19066 {
19067   ClutterActor *iter;
19068 
19069   self->priv->in_cloned_branch -= count;
19070 
19071   for (iter = self->priv->first_child;
19072        iter != NULL;
19073        iter = iter->priv->next_sibling)
19074     clutter_actor_pop_in_cloned_branch (iter, count);
19075 }
19076 
19077 void
_clutter_actor_attach_clone(ClutterActor * actor,ClutterActor * clone)19078 _clutter_actor_attach_clone (ClutterActor *actor,
19079                              ClutterActor *clone)
19080 {
19081   ClutterActorPrivate *priv = actor->priv;
19082 
19083   g_assert (clone != NULL);
19084 
19085   if (priv->clones == NULL)
19086     priv->clones = g_hash_table_new (NULL, NULL);
19087 
19088   g_hash_table_add (priv->clones, clone);
19089 
19090   clutter_actor_push_in_cloned_branch (actor, 1);
19091 }
19092 
19093 void
_clutter_actor_detach_clone(ClutterActor * actor,ClutterActor * clone)19094 _clutter_actor_detach_clone (ClutterActor *actor,
19095                              ClutterActor *clone)
19096 {
19097   ClutterActorPrivate *priv = actor->priv;
19098 
19099   g_assert (clone != NULL);
19100 
19101   if (priv->clones == NULL ||
19102       g_hash_table_lookup (priv->clones, clone) == NULL)
19103     return;
19104 
19105   clutter_actor_pop_in_cloned_branch (actor, 1);
19106 
19107   g_hash_table_remove (priv->clones, clone);
19108 
19109   if (g_hash_table_size (priv->clones) == 0)
19110     {
19111       g_hash_table_unref (priv->clones);
19112       priv->clones = NULL;
19113     }
19114 }
19115 
19116 /**
19117  * clutter_actor_has_mapped_clones:
19118  * @self: a #ClutterActor
19119  *
19120  * Returns whether a #ClutterActor or any parent actors have mapped clones
19121  * that are clone-painting @self.
19122  *
19123  * Returns: %TRUE if the actor has mapped clones, %FALSE otherwise
19124  */
19125 gboolean
clutter_actor_has_mapped_clones(ClutterActor * self)19126 clutter_actor_has_mapped_clones (ClutterActor *self)
19127 {
19128   ClutterActor *actor;
19129   GHashTableIter iter;
19130   gpointer key;
19131 
19132   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
19133 
19134   if (self->priv->in_cloned_branch == 0)
19135     return FALSE;
19136 
19137   for (actor = self; actor; actor = actor->priv->parent)
19138     {
19139       if (actor->priv->clones)
19140         {
19141           g_hash_table_iter_init (&iter, actor->priv->clones);
19142           while (g_hash_table_iter_next (&iter, &key, NULL))
19143             {
19144               if (CLUTTER_ACTOR_IS_MAPPED (key))
19145                 return TRUE;
19146             }
19147         }
19148 
19149       /* Clones will force-show their own source actor but not children of
19150        * it, so if we're hidden and an actor up the hierarchy has a clone,
19151        * we won't be visible.
19152        */
19153       if (!CLUTTER_ACTOR_IS_VISIBLE (actor))
19154         return FALSE;
19155     }
19156 
19157   return FALSE;
19158 }
19159 
19160 static void
push_in_paint_unmapped_branch(ClutterActor * self,guint count)19161 push_in_paint_unmapped_branch (ClutterActor *self,
19162                                guint         count)
19163 {
19164   ClutterActor *iter;
19165 
19166   for (iter = self->priv->first_child;
19167        iter != NULL;
19168        iter = iter->priv->next_sibling)
19169     push_in_paint_unmapped_branch (iter, count);
19170 
19171   self->priv->unmapped_paint_branch_counter += count;
19172 }
19173 
19174 static void
pop_in_paint_unmapped_branch(ClutterActor * self,guint count)19175 pop_in_paint_unmapped_branch (ClutterActor *self,
19176                               guint         count)
19177 {
19178   ClutterActor *iter;
19179 
19180   self->priv->unmapped_paint_branch_counter -= count;
19181 
19182   for (iter = self->priv->first_child;
19183        iter != NULL;
19184        iter = iter->priv->next_sibling)
19185     pop_in_paint_unmapped_branch (iter, count);
19186 }
19187 
19188 static void
clutter_actor_child_model__items_changed(GListModel * model,guint position,guint removed,guint added,gpointer user_data)19189 clutter_actor_child_model__items_changed (GListModel *model,
19190                                           guint       position,
19191                                           guint       removed,
19192                                           guint       added,
19193                                           gpointer    user_data)
19194 {
19195   ClutterActor *parent = user_data;
19196   ClutterActorPrivate *priv = parent->priv;
19197   guint i;
19198 
19199   while (removed--)
19200     {
19201       ClutterActor *child = clutter_actor_get_child_at_index (parent, position);
19202       clutter_actor_destroy (child);
19203     }
19204 
19205   for (i = 0; i < added; i++)
19206     {
19207       GObject *item = g_list_model_get_item (model, position + i);
19208       ClutterActor *child = priv->create_child_func (item, priv->create_child_data);
19209 
19210       /* The actor returned by the function can have a floating reference,
19211        * if the implementation is in pure C, or have a full reference, usually
19212        * the case for language bindings. To avoid leaking references, we
19213        * try to assume ownership of the instance, and release the reference
19214        * at the end unconditionally, leaving the only reference to the actor
19215        * itself.
19216        */
19217       if (g_object_is_floating (child))
19218         g_object_ref_sink (child);
19219 
19220       clutter_actor_insert_child_at_index (parent, child, position + i);
19221 
19222       g_object_unref (child);
19223       g_object_unref (item);
19224     }
19225 }
19226 
19227 /**
19228  * clutter_actor_bind_model:
19229  * @self: a #ClutterActor
19230  * @model: (nullable): a #GListModel
19231  * @create_child_func: a function that creates #ClutterActor instances
19232  *   from the contents of the @model
19233  * @user_data: user data passed to @create_child_func
19234  * @notify: function called when unsetting the @model
19235  *
19236  * Binds a #GListModel to a #ClutterActor.
19237  *
19238  * If the #ClutterActor was already bound to a #GListModel, the previous
19239  * binding is destroyed.
19240  *
19241  * The existing children of #ClutterActor are destroyed when setting a
19242  * model, and new children are created and added, representing the contents
19243  * of the @model. The #ClutterActor is updated whenever the @model changes.
19244  * If @model is %NULL, the #ClutterActor is left empty.
19245  *
19246  * When a #ClutterActor is bound to a model, adding and removing children
19247  * directly is undefined behaviour.
19248  *
19249  * Since: 1.24
19250  */
19251 void
clutter_actor_bind_model(ClutterActor * self,GListModel * model,ClutterActorCreateChildFunc create_child_func,gpointer user_data,GDestroyNotify notify)19252 clutter_actor_bind_model (ClutterActor                *self,
19253                           GListModel                  *model,
19254                           ClutterActorCreateChildFunc  create_child_func,
19255                           gpointer                     user_data,
19256                           GDestroyNotify               notify)
19257 {
19258   ClutterActorPrivate *priv = clutter_actor_get_instance_private (self);
19259 
19260   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19261   g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
19262   g_return_if_fail (model == NULL || create_child_func != NULL);
19263 
19264   if (priv->child_model != NULL)
19265     {
19266       if (priv->create_child_notify != NULL)
19267         priv->create_child_notify (priv->create_child_data);
19268 
19269       g_signal_handlers_disconnect_by_func (priv->child_model,
19270                                             clutter_actor_child_model__items_changed,
19271                                             self);
19272       g_clear_object (&priv->child_model);
19273       priv->create_child_func = NULL;
19274       priv->create_child_data = NULL;
19275       priv->create_child_notify = NULL;
19276     }
19277 
19278   clutter_actor_destroy_all_children (self);
19279 
19280   if (model == NULL)
19281     return;
19282 
19283   priv->child_model = g_object_ref (model);
19284   priv->create_child_func = create_child_func;
19285   priv->create_child_data = user_data;
19286   priv->create_child_notify = notify;
19287 
19288   g_signal_connect (priv->child_model, "items-changed",
19289                     G_CALLBACK (clutter_actor_child_model__items_changed),
19290                     self);
19291 
19292   clutter_actor_child_model__items_changed (priv->child_model,
19293                                             0,
19294                                             0,
19295                                             g_list_model_get_n_items (priv->child_model),
19296                                             self);
19297 }
19298 
19299 typedef struct {
19300   GType child_type;
19301   GArray *props;
19302 } BindClosure;
19303 
19304 typedef struct {
19305   const char *model_property;
19306   const char *child_property;
19307   GBindingFlags flags;
19308 } BindProperty;
19309 
19310 static void
bind_closure_free(gpointer data_)19311 bind_closure_free (gpointer data_)
19312 {
19313   BindClosure *data = data_;
19314 
19315   if (data == NULL)
19316     return;
19317 
19318   g_array_unref (data->props);
19319   g_free (data);
19320 }
19321 
19322 static ClutterActor *
bind_child_with_properties(gpointer item,gpointer data_)19323 bind_child_with_properties (gpointer item,
19324                             gpointer data_)
19325 {
19326   BindClosure *data = data_;
19327   ClutterActor *res;
19328   guint i;
19329 
19330   res = g_object_new (data->child_type, NULL);
19331 
19332   for (i = 0; i < data->props->len; i++)
19333     {
19334       const BindProperty *prop = &g_array_index (data->props, BindProperty, i);
19335 
19336       g_object_bind_property (item, prop->model_property,
19337                               res, prop->child_property,
19338                               prop->flags);
19339     }
19340 
19341   return res;
19342 }
19343 
19344 /**
19345  * clutter_actor_bind_model_with_properties:
19346  * @self: a #ClutterActor
19347  * @model: a #GListModel
19348  * @child_type: the type of #ClutterActor to use when creating
19349  *   children mapping to items inside the @model
19350  * @first_model_property: the first property of @model to bind
19351  * @...: tuples of property names on the @model, on the child, and the
19352  *   #GBindingFlags used to bind them, terminated by %NULL
19353  *
19354  * Binds a #GListModel to a #ClutterActor.
19355  *
19356  * Unlike clutter_actor_bind_model(), this function automatically creates
19357  * a child #ClutterActor of type @child_type, and binds properties on the
19358  * items inside the @model to the corresponding properties on the child,
19359  * for instance:
19360  *
19361  * |[<!-- language="C" -->
19362  *   clutter_actor_bind_model_with_properties (actor, model,
19363  *                                             MY_TYPE_CHILD_VIEW,
19364  *                                             "label", "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
19365  *                                             "icon", "image", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
19366  *                                             "selected", "selected", G_BINDING_BIDIRECTIONAL,
19367  *                                             "active", "active", G_BINDING_BIDIRECTIONAL,
19368  *                                             NULL);
19369  * ]|
19370  *
19371  * is the equivalent of calling clutter_actor_bind_model() with a
19372  * #ClutterActorCreateChildFunc of:
19373  *
19374  * |[<!-- language="C" -->
19375  *   ClutterActor *res = g_object_new (MY_TYPE_CHILD_VIEW, NULL);
19376  *
19377  *   g_object_bind_property (item, "label", res, "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
19378  *   g_object_bind_property (item, "icon", res, "image", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
19379  *   g_object_bind_property (item, "selected", res, "selected", G_BINDING_BIDIRECTIONAL);
19380  *   g_object_bind_property (item, "active", res, "active", G_BINDING_BIDIRECTIONAL);
19381  *
19382  *   return res;
19383  * ]|
19384  *
19385  * If the #ClutterActor was already bound to a #GListModel, the previous
19386  * binding is destroyed.
19387  *
19388  * When a #ClutterActor is bound to a model, adding and removing children
19389  * directly is undefined behaviour.
19390  *
19391  * See also: clutter_actor_bind_model()
19392  *
19393  * Since: 1.24
19394  */
19395 void
clutter_actor_bind_model_with_properties(ClutterActor * self,GListModel * model,GType child_type,const char * first_model_property,...)19396 clutter_actor_bind_model_with_properties (ClutterActor *self,
19397                                           GListModel   *model,
19398                                           GType         child_type,
19399                                           const char   *first_model_property,
19400                                           ...)
19401 {
19402   va_list args;
19403   BindClosure *clos;
19404   const char *model_property;
19405 
19406   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19407   g_return_if_fail (G_IS_LIST_MODEL (model));
19408   g_return_if_fail (g_type_is_a (child_type, CLUTTER_TYPE_ACTOR));
19409 
19410   clos = g_new0 (BindClosure, 1);
19411   clos->child_type = child_type;
19412   clos->props = g_array_new (FALSE, FALSE, sizeof (BindProperty));
19413 
19414   va_start (args, first_model_property);
19415   model_property = first_model_property;
19416   while (model_property != NULL)
19417     {
19418       const char *child_property = va_arg (args, char *);
19419       GBindingFlags binding_flags = va_arg (args, guint);
19420       BindProperty bind;
19421 
19422       bind.model_property = g_intern_string (model_property);
19423       bind.child_property = g_intern_string (child_property);
19424       bind.flags = binding_flags;
19425 
19426       g_array_append_val (clos->props, bind);
19427 
19428       model_property = va_arg (args, char *);
19429     }
19430   va_end (args);
19431 
19432   clutter_actor_bind_model (self, model, bind_child_with_properties, clos, bind_closure_free);
19433 }
19434 
19435 /*< private >
19436  * clutter_actor_create_texture_paint_node:
19437  * @self: a #ClutterActor
19438  * @texture: a #CoglTexture
19439  *
19440  * Creates a #ClutterPaintNode initialized using the state of the
19441  * given #ClutterActor, ready to be used inside the implementation
19442  * of the #ClutterActorClass.paint_node virtual function.
19443  *
19444  * The returned paint node has the geometry set to the size of the
19445  * #ClutterActor:content-box property; it uses the filters specified
19446  * in the #ClutterActor:minification-filter and #ClutterActor:magnification-filter
19447  * properties; and respects the #ClutterActor:content-repeat property.
19448  *
19449  * Returns: (transfer full): The newly created #ClutterPaintNode
19450  *
19451  * Since: 1.24
19452  */
19453 ClutterPaintNode *
clutter_actor_create_texture_paint_node(ClutterActor * self,CoglTexture * texture)19454 clutter_actor_create_texture_paint_node (ClutterActor *self,
19455                                          CoglTexture  *texture)
19456 {
19457   ClutterActorPrivate *priv = clutter_actor_get_instance_private (self);
19458   ClutterPaintNode *node;
19459   ClutterActorBox box;
19460   ClutterColor color;
19461 
19462   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
19463   g_return_val_if_fail (texture != NULL, NULL);
19464 
19465   clutter_actor_get_content_box (self, &box);
19466 
19467   /* ClutterTextureNode will premultiply the blend color, so we
19468    * want it to be white with the paint opacity
19469    */
19470   color.red = 255;
19471   color.green = 255;
19472   color.blue = 255;
19473   color.alpha = clutter_actor_get_paint_opacity_internal (self);
19474 
19475   node = clutter_texture_node_new (texture, &color, priv->min_filter, priv->mag_filter);
19476   clutter_paint_node_set_static_name (node, "Texture");
19477 
19478   if (priv->content_repeat == CLUTTER_REPEAT_NONE)
19479     clutter_paint_node_add_rectangle (node, &box);
19480   else
19481     {
19482       float t_w = 1.f, t_h = 1.f;
19483 
19484       if ((priv->content_repeat & CLUTTER_REPEAT_X_AXIS) != FALSE)
19485         t_w = (box.x2 - box.x1) / cogl_texture_get_width (texture);
19486 
19487       if ((priv->content_repeat & CLUTTER_REPEAT_Y_AXIS) != FALSE)
19488         t_h = (box.y2 - box.y1) / cogl_texture_get_height (texture);
19489 
19490       clutter_paint_node_add_texture_rectangle (node, &box,
19491                                                 0.f, 0.f,
19492                                                 t_w, t_h);
19493     }
19494 
19495   return node;
19496 }
19497 
19498 gboolean
clutter_actor_has_accessible(ClutterActor * actor)19499 clutter_actor_has_accessible (ClutterActor *actor)
19500 {
19501   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
19502 
19503   if (CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible)
19504     return CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible (actor);
19505 
19506   return TRUE;
19507 }
19508 
19509 void
clutter_actor_queue_immediate_relayout(ClutterActor * self)19510 clutter_actor_queue_immediate_relayout (ClutterActor *self)
19511 {
19512   ClutterStage *stage;
19513 
19514   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19515 
19516   clutter_actor_queue_relayout (self);
19517 
19518   stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
19519   if (stage)
19520     clutter_stage_set_actor_needs_immediate_relayout (stage);
19521 }
19522 
19523 /**
19524  * clutter_actor_invalidate_transform:
19525  * @self: A #ClutterActor
19526  *
19527  * Invalidate the cached transformation matrix of @self.
19528  * This is needed for implementations overriding the apply_transform()
19529  * vfunc and has to be called if the matrix returned by apply_transform()
19530  * would change.
19531  */
19532 void
clutter_actor_invalidate_transform(ClutterActor * self)19533 clutter_actor_invalidate_transform (ClutterActor *self)
19534 {
19535   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19536 
19537   transform_changed (self);
19538 }
19539 
19540 /**
19541  * clutter_actor_invalidate_paint_volume:
19542  * @self: A #ClutterActor
19543  *
19544  * Invalidates the cached paint volume of @self. This is needed for
19545  * implementations overriding the #ClutterActorClass.get_paint_volume()
19546  * virtual function and has to be called every time the paint volume
19547  * returned by that function would change.
19548  */
19549 void
clutter_actor_invalidate_paint_volume(ClutterActor * self)19550 clutter_actor_invalidate_paint_volume (ClutterActor *self)
19551 {
19552   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19553 
19554   queue_update_paint_volume (self);
19555 }
19556 
19557 gboolean
clutter_actor_get_redraw_clip(ClutterActor * self,ClutterPaintVolume * dst_old_pv,ClutterPaintVolume * dst_new_pv)19558 clutter_actor_get_redraw_clip (ClutterActor       *self,
19559                                ClutterPaintVolume *dst_old_pv,
19560                                ClutterPaintVolume *dst_new_pv)
19561 {
19562   ClutterActorPrivate *priv = self->priv;
19563   ClutterPaintVolume *paint_volume;
19564 
19565   paint_volume = _clutter_actor_get_paint_volume_mutable (self);
19566   if (!paint_volume || !priv->last_paint_volume_valid)
19567     return FALSE;
19568 
19569   _clutter_paint_volume_set_from_volume (dst_old_pv, &priv->last_paint_volume);
19570   _clutter_paint_volume_set_from_volume (dst_new_pv, paint_volume);
19571 
19572   return TRUE;
19573 }
19574