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 #ClutterActor:anchor-x and #ClutterActor:anchor-y point.
47  *  9. negative translation by the actor's #ClutterActor:pivot-point
48  *
49  * ## Modifying an actor's geometry ## {#clutter-actor-geometry}
50  *
51  * Each actor has a bounding box, called #ClutterActor:allocation
52  * which is either set by its parent or explicitly through the
53  * clutter_actor_set_position() and clutter_actor_set_size() methods.
54  * Each actor also has an implicit preferred size.
55  *
56  * An actor’s preferred size can be defined by any subclass by
57  * overriding the #ClutterActorClass.get_preferred_width() and the
58  * #ClutterActorClass.get_preferred_height() virtual functions, or it can
59  * be explicitly set by using clutter_actor_set_width() and
60  * clutter_actor_set_height().
61  *
62  * An actor’s position can be set explicitly by using
63  * clutter_actor_set_x() and clutter_actor_set_y(); the coordinates are
64  * relative to the origin of the actor’s parent.
65  *
66  * ## Managing actor children ## {#clutter-actor-children}
67  *
68  * Each actor can have multiple children, by calling
69  * clutter_actor_add_child() to add a new child actor, and
70  * clutter_actor_remove_child() to remove an existing child. #ClutterActor
71  * will hold a reference on each child actor, which will be released when
72  * the child is removed from its parent, or destroyed using
73  * clutter_actor_destroy().
74  *
75  * |[<!-- language="C" -->
76  *  ClutterActor *actor = clutter_actor_new ();
77  *
78  *  // set the bounding box of the actor
79  *  clutter_actor_set_position (actor, 0, 0);
80  *  clutter_actor_set_size (actor, 480, 640);
81  *
82  *  // set the background color of the actor
83  *  clutter_actor_set_background_color (actor, CLUTTER_COLOR_Orange);
84  *
85  *  // set the bounding box of the child, relative to the parent
86  *  ClutterActor *child = clutter_actor_new ();
87  *  clutter_actor_set_position (child, 20, 20);
88  *  clutter_actor_set_size (child, 80, 240);
89  *
90  *  // set the background color of the child
91  *  clutter_actor_set_background_color (child, CLUTTER_COLOR_Blue);
92  *
93  *  // add the child to the actor
94  *  clutter_actor_add_child (actor, child);
95  * ]|
96  *
97  * Children can be inserted at a given index, or above and below
98  * another child actor. The order of insertion determines the order of the
99  * children when iterating over them. Iterating over children is performed
100  * by using clutter_actor_get_first_child(), clutter_actor_get_previous_sibling(),
101  * clutter_actor_get_next_sibling(), and clutter_actor_get_last_child(). It is
102  * also possible to retrieve a list of children by using
103  * clutter_actor_get_children(), as well as retrieving a specific child at a
104  * given index by using clutter_actor_get_child_at_index().
105  *
106  * If you need to track additions of children to a #ClutterActor, use
107  * the #ClutterContainer::actor-added signal; similarly, to track removals
108  * of children from a ClutterActor, use the #ClutterContainer::actor-removed
109  * signal.
110  *
111  * See [basic-actor.c](https://git.gnome.org/browse/clutter/tree/examples/basic-actor.c?h=clutter-1.18).
112  *
113  * ## Painting an actor ## {#clutter-actor-painting}
114  *
115  * There are three ways to paint an actor:
116  *
117  *  - set a delegate #ClutterContent as the value for the #ClutterActor:content property of the actor
118  *  - subclass #ClutterActor and override the #ClutterActorClass.paint_node() virtual function
119  *  - subclass #ClutterActor and override the #ClutterActorClass.paint() virtual function.
120  *
121  * A #ClutterContent is a delegate object that takes over the painting
122  * operations of one, or more actors. The #ClutterContent painting will
123  * be performed on top of the #ClutterActor:background-color of the actor,
124  * and before calling the actor's own implementation of the
125  * #ClutterActorClass.paint_node() virtual function.
126  *
127  * |[<!-- language="C" -->
128  * ClutterActor *actor = clutter_actor_new ();
129  *
130  * // set the bounding box
131  * clutter_actor_set_position (actor, 50, 50);
132  * clutter_actor_set_size (actor, 100, 100);
133  *
134  * // set the content; the image_content variable is set elsewhere
135  * clutter_actor_set_content (actor, image_content);
136  * ]|
137  *
138  * The #ClutterActorClass.paint_node() virtual function is invoked whenever
139  * an actor needs to be painted. The implementation of the virtual function
140  * must only paint the contents of the actor itself, and not the contents of
141  * its children, if the actor has any.
142  *
143  * The #ClutterPaintNode passed to the virtual function is the local root of
144  * the render tree; any node added to it will be rendered at the correct
145  * position, as defined by the actor's #ClutterActor:allocation.
146  *
147  * |[<!-- language="C" -->
148  * static void
149  * my_actor_paint_node (ClutterActor     *actor,
150  *                      ClutterPaintNode *root)
151  * {
152  *   ClutterPaintNode *node;
153  *   ClutterActorBox box;
154  *
155  *   // where the content of the actor should be painted
156  *   clutter_actor_get_allocation_box (actor, &box);
157  *
158  *   // the cogl_texture variable is set elsewhere
159  *   node = clutter_texture_node_new (cogl_texture, CLUTTER_COLOR_White,
160  *                                    CLUTTER_SCALING_FILTER_TRILINEAR,
161  *                                    CLUTTER_SCALING_FILTER_LINEAR);
162  *
163  *   // paint the content of the node using the allocation
164  *   clutter_paint_node_add_rectangle (node, &box);
165  *
166  *   // add the node, and transfer ownership
167  *   clutter_paint_node_add_child (root, node);
168  *   clutter_paint_node_unref (node);
169  * }
170  *
171  * The #ClutterActorClass.paint() virtual function is invoked when the
172  * #ClutterActor::paint signal is emitted, and after the other signal
173  * handlers have been invoked. Overriding the paint virtual function
174  * gives total control to the paint sequence of the actor itself,
175  * including the children of the actor, if any.
176  *
177  * It is strongly discouraged to override the #ClutterActorClass.paint()
178  * virtual function, as well as connecting to the #ClutterActor::paint
179  * signal. These hooks into the paint sequence are considered legacy, and
180  * will be removed when the Clutter API changes.
181  *
182  * ## Handling events on an actor ## {#clutter-actor-event-handling}
183  *
184  * A #ClutterActor can receive and handle input device events, for
185  * instance pointer events and key events, as long as its
186  * #ClutterActor:reactive property is set to %TRUE.
187  *
188  * Once an actor has been determined to be the source of an event,
189  * Clutter will traverse the scene graph from the top-level actor towards the
190  * event source, emitting the #ClutterActor::captured-event signal on each
191  * ancestor until it reaches the source; this phase is also called
192  * the "capture" phase. If the event propagation was not stopped, the graph
193  * is walked backwards, from the source actor to the top-level, and the
194  * #ClutterActor::event signal is emitted, alongside eventual event-specific
195  * signals like #ClutterActor::button-press-event or #ClutterActor::motion-event;
196  * this phase is also called the "bubble" phase.
197  *
198  * At any point of the signal emission, signal handlers can stop the propagation
199  * through the scene graph by returning %CLUTTER_EVENT_STOP; otherwise, they can
200  * continue the propagation by returning %CLUTTER_EVENT_PROPAGATE.
201  *
202  * ## Animation ## {#clutter-actor-animation}
203  *
204  * Animation is a core concept of modern user interfaces; Clutter provides a
205  * complete and powerful animation framework that automatically tweens the
206  * actor's state without requiring direct, frame by frame manipulation from
207  * your application code. You have two models at your disposal:
208  *
209  *  - an implicit animation model
210  *  - an explicit animation model
211  *
212  * The implicit animation model of Clutter assumes that all the
213  * changes in an actor state should be gradual and asynchronous; Clutter
214  * will automatically transition an actor's property change between the
215  * current state and the desired one without manual intervention, if the
216  * property is defined to be animatable in its documentation.
217  *
218  * By default, in the 1.0 API series, the transition happens with a duration
219  * of zero milliseconds, and the implicit animation is an opt in feature to
220  * retain backwards compatibility.
221  *
222  * Implicit animations depend on the current easing state; in order to use
223  * the default easing state for an actor you should call the
224  * clutter_actor_save_easing_state() function:
225  *
226  * |[<!-- language="C" -->
227  * // assume that the actor is currently positioned at (100, 100)
228  *
229  * // store the current easing state and reset the new easing state to
230  * // its default values
231  * clutter_actor_save_easing_state (actor);
232  *
233  * // change the actor's position
234  * clutter_actor_set_position (actor, 500, 500);
235  *
236  * // restore the previously saved easing state
237  * clutter_actor_restore_easing_state (actor);
238  * ]|
239  *
240  * The example above will trigger an implicit animation of the
241  * actor between its current position to a new position.
242  *
243  * Implicit animations use a default duration of 250 milliseconds,
244  * and a default easing mode of %CLUTTER_EASE_OUT_CUBIC, unless you call
245  * clutter_actor_set_easing_mode() and clutter_actor_set_easing_duration()
246  * after changing the easing state of the actor.
247  *
248  * It is possible to animate multiple properties of an actor
249  * at the same time, and you can animate multiple actors at the same
250  * time as well, for instance:
251  *
252  * |[<!-- language="C" -->
253  * clutter_actor_save_easing_state (actor);
254  *
255  * // animate the actor's opacity and depth
256  * clutter_actor_set_opacity (actor, 0);
257  * clutter_actor_set_depth (actor, -100);
258  *
259  * clutter_actor_restore_easing_state (actor);
260  *
261  * clutter_actor_save_easing_state (another_actor);
262  *
263  * // animate another actor's opacity
264  * clutter_actor_set_opacity (another_actor, 255);
265  * clutter_actor_set_depth (another_actor, 100);
266  *
267  * clutter_actor_restore_easing_state (another_actor);
268  * ]|
269  *
270  * Changing the easing state will affect all the following property
271  * transitions, but will not affect existing transitions.
272  *
273  * It is important to note that if you modify the state on an
274  * animatable property while a transition is in flight, the transition's
275  * final value will be updated, as well as its duration and progress
276  * mode by using the current easing state; for instance, in the following
277  * example:
278  *
279  * |[<!-- language="C" -->
280  * clutter_actor_save_easing_state (actor);
281  * clutter_actor_set_easing_duration (actor, 1000);
282  * clutter_actor_set_x (actor, 200);
283  * clutter_actor_restore_easing_state (actor);
284  *
285  * clutter_actor_save_easing_state (actor);
286  * clutter_actor_set_easing_duration (actor, 500);
287  * clutter_actor_set_x (actor, 100);
288  * clutter_actor_restore_easing_state (actor);
289  * ]|
290  *
291  * the first call to clutter_actor_set_x() will begin a transition
292  * of the #ClutterActor:x property from the current value to the value of
293  * 200 over a duration of one second; the second call to clutter_actor_set_x()
294  * will change the transition's final value to 100 and the duration to 500
295  * milliseconds.
296  *
297  * It is possible to receive a notification of the completion of an
298  * implicit transition by using the #ClutterActor::transition-stopped
299  * signal, decorated with the name of the property. In case you want to
300  * know when all the currently in flight transitions are complete, use
301  * the #ClutterActor::transitions-completed signal instead.
302  *
303  * It is possible to retrieve the #ClutterTransition used by the
304  * animatable properties by using clutter_actor_get_transition() and using
305  * the property name as the transition name.
306  *
307  * The explicit animation model supported by Clutter requires that
308  * you create a #ClutterTransition object, and optionally set the initial
309  * and final values. The transition will not start unless you add it to the
310  * #ClutterActor.
311  *
312  * |[<!-- language="C" -->
313  * ClutterTransition *transition;
314  *
315  * transition = clutter_property_transition_new ("opacity");
316  * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 3000);
317  * clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), 2);
318  * clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE);
319  * clutter_transition_set_from (transition, G_TYPE_UINT, 255);
320  * clutter_transition_set_to (transition, G_TYPE_UINT, 0);
321  *
322  * clutter_actor_add_transition (actor, "animate-opacity", transition);
323  * ]|
324  *
325  * The example above will animate the #ClutterActor:opacity property
326  * of an actor between fully opaque and fully transparent, and back, over
327  * a span of 3 seconds. The animation does not begin until it is added to
328  * the actor.
329  *
330  * The explicit animation API applies to all #GObject properties,
331  * as well as the custom properties defined through the #ClutterAnimatable
332  * interface, regardless of whether they are defined as implicitly
333  * animatable or not.
334  *
335  * The explicit animation API should also be used when using custom
336  * animatable properties for #ClutterAction, #ClutterConstraint, and
337  * #ClutterEffect instances associated to an actor; see the section on
338  * custom animatable properties below for an example.
339  *
340  * Finally, explicit animations are useful for creating animations
341  * that run continuously, for instance:
342  *
343  * |[<!-- language="C" -->
344  * // this animation will pulse the actor's opacity continuously
345  * ClutterTransition *transition;
346  * ClutterInterval *interval;
347  *
348  * transition = clutter_property_transition_new ("opacity");
349  *
350  * // we want to animate the opacity between 0 and 255
351  * clutter_transition_set_from (transition, G_TYPE_UINT, 0);
352  * clutter_transition_set_to (transition, G_TYPE_UINT, 255);
353  *
354  * // over a one second duration, running an infinite amount of times
355  * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 1000);
356  * clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), -1);
357  *
358  * // we want to fade in and out, so we need to auto-reverse the transition
359  * clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE);
360  *
361  * // and we want to use an easing function that eases both in and out
362  * clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
363  *                                     CLUTTER_EASE_IN_OUT_CUBIC);
364  *
365  * // add the transition to the desired actor to start it
366  * clutter_actor_add_transition (actor, "opacityAnimation", transition);
367  * ]|
368  *
369  * ## Implementing an actor ## {#clutter-actor-implementing}
370  *
371  * Careful consideration should be given when deciding to implement
372  * a #ClutterActor sub-class. It is generally recommended to implement a
373  * sub-class of #ClutterActor only for actors that should be used as leaf
374  * nodes of a scene graph.
375  *
376  * If your actor should be painted in a custom way, you should
377  * override the #ClutterActor::paint signal class handler. You can either
378  * opt to chain up to the parent class implementation or decide to fully
379  * override the default paint implementation; Clutter will set up the
380  * transformations and clip regions prior to emitting the #ClutterActor::paint
381  * signal.
382  *
383  * By overriding the #ClutterActorClass.get_preferred_width() and
384  * #ClutterActorClass.get_preferred_height() virtual functions it is
385  * possible to change or provide the preferred size of an actor; similarly,
386  * by overriding the #ClutterActorClass.allocate() virtual function it is
387  * possible to control the layout of the children of an actor. Make sure to
388  * always chain up to the parent implementation of the
389  * #ClutterActorClass.allocate() virtual function.
390  *
391  * In general, it is strongly encouraged to use delegation and composition
392  * instead of direct subclassing.
393  *
394  * ## ClutterActor custom properties for ClutterScript ## {#clutter-actor-custom-script}
395  *
396  * #ClutterActor defines a custom "rotation" property which allows a short-hand
397  * description of the rotations to be applied to an actor.
398  *
399  * The syntax of the "rotation" property is the following:
400  *
401  * |[
402  * "rotation" : [ { "<axis>" : [ <angle>, [ <center-point> ] ] } ]
403  * ]|
404  *
405  * where:
406  *
407  *  - axis is the name of an enumeration value of type #ClutterRotateAxis
408  *  - angle is a floating point value representing the rotation angle on the given axis in degrees
409  *  - center-point is an optional array, and if present it must contain the center of rotation as described by two coordinates:
410  *    - Y and Z for "x-axis"
411  *    - X and Z for "y-axis"
412  *    - X and Y for "z-axis".
413  *
414  * #ClutterActor also defines a scriptable "margin" property which follows the CSS "margin" shorthand.
415  *
416  * |[
417  *   // 4 values
418  *   "margin" : [ top, right, bottom, left ]
419  *   // 3 values
420  *   "margin" : [ top, left/right, bottom ]
421  *   // 2 values
422  *   "margin" : [ top/bottom, left/right ]
423  *   // 1 value
424  *   "margin" : [ top/right/bottom/left ]
425  * ]|
426  *
427  * #ClutterActor will also parse every positional and dimensional
428  * property defined as a string through clutter_units_from_string(); you
429  * should read the documentation for the #ClutterUnits parser format for
430  * the valid units and syntax.
431  *
432  * ## Custom animatable properties
433  *
434  * #ClutterActor allows accessing properties of #ClutterAction,
435  * #ClutterEffect, and #ClutterConstraint instances associated to an actor
436  * instance for animation purposes.
437  *
438  * In order to access a specific #ClutterAction or a #ClutterConstraint
439  * property it is necessary to set the #ClutterActorMeta:name property on the
440  * given action or constraint.
441  *
442  * The property can be accessed using the following syntax:
443  *
444  * |[
445  *   @<section>.<meta-name>.<property-name>
446  * ]|
447  *
448  *  - the initial `@` is mandatory
449  *  - the `section` fragment can be one between "actions", "constraints" and "effects"
450  *  - the `meta-name` fragment is the name of the action, effect, or constraint, as
451  *    specified by the #ClutterActorMeta:name property of #ClutterActorMeta
452  *  - the `property-name` fragment is the name of the action, effect, or constraint
453  *    property to be animated.
454  *
455  * The example below animates a #ClutterBindConstraint applied to an actor
456  * using an explicit transition. The `rect` actor has a binding constraint
457  * on the `origin` actor, and in its initial state is overlapping the actor
458  * to which is bound to.
459  *
460  * |[<!-- language="C" -->
461  * constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_X, 0.0);
462  * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-x");
463  * clutter_actor_add_constraint (rect, constraint);
464  *
465  * constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_Y, 0.0);
466  * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-y");
467  * clutter_actor_add_constraint (rect, constraint);
468  *
469  * clutter_actor_set_reactive (origin, TRUE);
470  *
471  * g_signal_connect (origin, "button-press-event",
472  *                   G_CALLBACK (on_button_press),
473  *                   rect);
474  * ]|
475  *
476  * On button press, the rectangle "slides" from behind the actor to
477  * which is bound to, using the #ClutterBindConstraint:offset property to
478  * achieve the effect:
479  *
480  * |[<!-- language="C" -->
481  * gboolean
482  * on_button_press (ClutterActor *origin,
483  *                  ClutterEvent *event,
484  *                  ClutterActor *rect)
485  * {
486  *   ClutterTransition *transition;
487  *
488  *   // the offset that we want to apply; this will make the actor
489  *   // slide in from behind the origin and rest at the right of
490  *   // the origin, plus a padding value
491  *   float new_offset = clutter_actor_get_width (origin) + h_padding;
492  *
493  *   // the property we wish to animate; the "@constraints" section
494  *   // tells Clutter to check inside the constraints associated
495  *   // with the actor; the "bind-x" section is the name of the
496  *   // constraint; and the "offset" is the name of the property
497  *   // on the constraint
498  *   const char *prop = "@constraints.bind-x.offset";
499  *
500  *   // create a new transition for the given property
501  *   transition = clutter_property_transition_new (prop);
502  *
503  *   // set the easing mode and duration
504  *   clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
505  *                                       CLUTTER_EASE_OUT_CUBIC);
506  *   clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 500);
507  *
508  *   // create the interval with the initial and final values
509  *   clutter_transition_set_from (transition, G_TYPE_FLOAT, 0.f);
510  *   clutter_transition_set_to (transition, G_TYPE_FLOAT, new_offset);
511  *
512  *   // add the transition to the actor; this causes the animation
513  *   // to start. the name "offsetAnimation" can be used to retrieve
514  *   // the transition later
515  *   clutter_actor_add_transition (rect, "offsetAnimation", transition);
516  *
517  *   // we handled the event
518  *   return CLUTTER_EVENT_STOP;
519  * }
520  * ]|
521  */
522 
523 /**
524  * CLUTTER_ACTOR_IS_MAPPED:
525  * @a: a #ClutterActor
526  *
527  * Evaluates to %TRUE if the %CLUTTER_ACTOR_MAPPED flag is set.
528  *
529  * The mapped state is set when the actor is visible and all its parents up
530  * to a top-level (e.g. a #ClutterStage) are visible, realized, and mapped.
531  *
532  * This check can be used to see if an actor is going to be painted, as only
533  * actors with the %CLUTTER_ACTOR_MAPPED flag set are going to be painted.
534  *
535  * The %CLUTTER_ACTOR_MAPPED flag is managed by Clutter itself, and it should
536  * not be checked directly; instead, the recommended usage is to connect a
537  * handler on the #GObject::notify signal for the #ClutterActor:mapped
538  * property of #ClutterActor, and check the presence of
539  * the %CLUTTER_ACTOR_MAPPED flag on state changes.
540  *
541  * It is also important to note that Clutter may delay the changes of
542  * the %CLUTTER_ACTOR_MAPPED flag on top-levels due to backend-specific
543  * limitations, or during the reparenting of an actor, to optimize
544  * unnecessary (and potentially expensive) state changes.
545  *
546  * Since: 0.2
547  *
548  * Deprecated: 1.24: Use clutter_actor_is_mapped() or the #ClutterActor:mapped
549  *   property instead of this macro.
550  */
551 
552 /**
553  * CLUTTER_ACTOR_IS_REALIZED:
554  * @a: a #ClutterActor
555  *
556  * Evaluates to %TRUE if the %CLUTTER_ACTOR_REALIZED flag is set.
557  *
558  * The realized state has an actor-dependant interpretation. If an
559  * actor wants to delay allocating resources until it is attached to a
560  * stage, it may use the realize state to do so. However it is
561  * perfectly acceptable for an actor to allocate Cogl resources before
562  * being realized because there is only one drawing context used by Clutter
563  * so any resources will work on any stage.  If an actor is mapped it
564  * must also be realized, but an actor can be realized and unmapped
565  * (this is so hiding an actor temporarily doesn't do an expensive
566  * unrealize/realize).
567  *
568  * To be realized an actor must be inside a stage, and all its parents
569  * must be realized.
570  *
571  * Since: 0.2
572  *
573  * Deprecated: 1.24: Use clutter_actor_is_realized() or the #ClutterActor:realized
574  *   property instead of this macro.
575  */
576 
577 /**
578  * CLUTTER_ACTOR_IS_VISIBLE:
579  * @a: a #ClutterActor
580  *
581  * Evaluates to %TRUE if the actor has been shown, %FALSE if it's hidden.
582  * Equivalent to the ClutterActor::visible object property.
583  *
584  * Note that an actor is only painted onscreen if it's mapped, which
585  * means it's visible, and all its parents are visible, and one of the
586  * parents is a toplevel stage; see also %CLUTTER_ACTOR_IS_MAPPED.
587  *
588  * Since: 0.2
589  *
590  * Deprecated: 1.24: Use clutter_actor_is_visible() or the #ClutterActor:visible
591  *   property instead of this macro.
592  */
593 
594 /**
595  * CLUTTER_ACTOR_IS_REACTIVE:
596  * @a: a #ClutterActor
597  *
598  * Evaluates to %TRUE if the %CLUTTER_ACTOR_REACTIVE flag is set.
599  *
600  * Only reactive actors will receive event-related signals.
601  *
602  * Since: 0.6
603  *
604  * Deprecated: 1.24: Use clutter_actor_get_reactive() or the
605  *   #ClutterActor:reactive property instead of this macro.
606  */
607 
608 #include "config.h"
609 
610 #include <math.h>
611 
612 #include <gobject/gvaluecollector.h>
613 
614 #include <cogl/cogl.h>
615 
616 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
617 #define CLUTTER_ENABLE_EXPERIMENTAL_API
618 
619 #include "clutter-actor-private.h"
620 
621 #include "clutter-action.h"
622 #include "clutter-actor-meta-private.h"
623 #include "clutter-animatable.h"
624 #include "clutter-color-static.h"
625 #include "clutter-color.h"
626 #include "clutter-constraint-private.h"
627 #include "clutter-container.h"
628 #include "clutter-content-private.h"
629 #include "clutter-debug.h"
630 #include "clutter-easing.h"
631 #include "clutter-effect-private.h"
632 #include "clutter-enum-types.h"
633 #include "clutter-fixed-layout.h"
634 #include "clutter-flatten-effect.h"
635 #include "clutter-interval.h"
636 #include "clutter-main.h"
637 #include "clutter-marshal.h"
638 #include "clutter-paint-nodes.h"
639 #include "clutter-paint-node-private.h"
640 #include "clutter-paint-volume-private.h"
641 #include "clutter-private.h"
642 #include "clutter-property-transition.h"
643 #include "clutter-scriptable.h"
644 #include "clutter-script-private.h"
645 #include "clutter-stage-private.h"
646 #include "clutter-timeline.h"
647 #include "clutter-transition.h"
648 #include "clutter-units.h"
649 
650 #include "deprecated/clutter-actor.h"
651 #include "deprecated/clutter-behaviour.h"
652 #include "deprecated/clutter-container.h"
653 
654 /* Internal enum used to control mapped state update.  This is a hint
655  * which indicates when to do something other than just enforce
656  * invariants.
657  */
658 typedef enum {
659   MAP_STATE_CHECK,           /* just enforce invariants. */
660   MAP_STATE_MAKE_UNREALIZED, /* force unrealize, ignoring invariants,
661                               * used when about to unparent.
662                               */
663   MAP_STATE_MAKE_MAPPED,     /* set mapped, error if invariants not met;
664                               * used to set mapped on toplevels.
665                               */
666   MAP_STATE_MAKE_UNMAPPED    /* set unmapped, even if parent is mapped,
667                               * used just before unmapping parent.
668                               */
669 } MapStateChange;
670 
671 /* 3 entries should be a good compromise, few layout managers
672  * will ask for 3 different preferred size in each allocation cycle */
673 #define N_CACHED_SIZE_REQUESTS 3
674 
675 struct _ClutterActorPrivate
676 {
677   /* request mode */
678   ClutterRequestMode request_mode;
679 
680   /* our cached size requests for different width / height */
681   SizeRequest width_requests[N_CACHED_SIZE_REQUESTS];
682   SizeRequest height_requests[N_CACHED_SIZE_REQUESTS];
683 
684   /* An age of 0 means the entry is not set */
685   guint cached_height_age;
686   guint cached_width_age;
687 
688   /* the bounding box of the actor, relative to the parent's
689    * allocation
690    */
691   ClutterActorBox allocation;
692   ClutterAllocationFlags allocation_flags;
693 
694   /* clip, in actor coordinates */
695   ClutterRect clip;
696 
697   /* the cached transformation matrix; see apply_transform() */
698   CoglMatrix transform;
699 
700   guint8 opacity;
701   gint opacity_override;
702 
703   ClutterOffscreenRedirect offscreen_redirect;
704 
705   /* This is an internal effect used to implement the
706      offscreen-redirect property */
707   ClutterEffect *flatten_effect;
708 
709   /* scene graph */
710   ClutterActor *parent;
711   ClutterActor *prev_sibling;
712   ClutterActor *next_sibling;
713   ClutterActor *first_child;
714   ClutterActor *last_child;
715 
716   gint n_children;
717 
718   /* tracks whenever the children of an actor are changed; the
719    * age is incremented by 1 whenever an actor is added or
720    * removed. the age is not incremented when the first or the
721    * last child pointers are changed, or when grandchildren of
722    * an actor are changed.
723    */
724   gint age;
725 
726   gchar *name; /* a non-unique name, used for debugging */
727 
728   gint32 pick_id; /* per-stage unique id, used for picking */
729 
730   /* a back-pointer to the Pango context that we can use
731    * to create pre-configured PangoLayout
732    */
733   PangoContext *pango_context;
734 
735   /* the text direction configured for this child - either by
736    * application code, or by the actor's parent
737    */
738   ClutterTextDirection text_direction;
739 
740   /* a counter used to toggle the CLUTTER_INTERNAL_CHILD flag */
741   gint internal_child;
742 
743   /* meta classes */
744   ClutterMetaGroup *actions;
745   ClutterMetaGroup *constraints;
746   ClutterMetaGroup *effects;
747 
748   /* delegate object used to allocate the children of this actor */
749   ClutterLayoutManager *layout_manager;
750 
751   /* delegate object used to paint the contents of this actor */
752   ClutterContent *content;
753 
754   ClutterActorBox content_box;
755   ClutterContentGravity content_gravity;
756   ClutterScalingFilter min_filter;
757   ClutterScalingFilter mag_filter;
758   ClutterContentRepeat content_repeat;
759 
760   /* used when painting, to update the paint volume */
761   ClutterEffect *current_effect;
762 
763   /* This is used to store an effect which needs to be redrawn. A
764      redraw can be queued to start from a particular effect. This is
765      used by parametrised effects that can cache an image of the
766      actor. If a parameter of the effect changes then it only needs to
767      redraw the cached image, not the actual actor. The pointer is
768      only valid if is_dirty == TRUE. If the pointer is NULL then the
769      whole actor is dirty. */
770   ClutterEffect *effect_to_redraw;
771 
772   /* This is used when painting effects to implement the
773      clutter_actor_continue_paint() function. It points to the node in
774      the list of effects that is next in the chain */
775   const GList *next_effect_to_paint;
776 
777   ClutterPaintVolume paint_volume;
778 
779   /* NB: This volume isn't relative to this actor, it is in eye
780    * coordinates so that it can remain valid after the actor changes.
781    */
782   ClutterPaintVolume last_paint_volume;
783 
784   ClutterStageQueueRedrawEntry *queue_redraw_entry;
785 
786   ClutterColor bg_color;
787 
788 #ifdef CLUTTER_ENABLE_DEBUG
789   /* a string used for debugging messages */
790   gchar *debug_name;
791 #endif
792 
793   /* a set of clones of the actor */
794   GHashTable *clones;
795 
796   /* whether the actor is inside a cloned branch; this
797    * value is propagated to all the actor's children
798    */
799   gulong in_cloned_branch;
800 
801   GListModel *child_model;
802   ClutterActorCreateChildFunc create_child_func;
803   gpointer create_child_data;
804   GDestroyNotify create_child_notify;
805 
806   /* bitfields: KEEP AT THE END */
807 
808   /* fixed position and sizes */
809   guint position_set                : 1;
810   guint min_width_set               : 1;
811   guint min_height_set              : 1;
812   guint natural_width_set           : 1;
813   guint natural_height_set          : 1;
814   /* cached request is invalid (implies allocation is too) */
815   guint needs_width_request         : 1;
816   /* cached request is invalid (implies allocation is too) */
817   guint needs_height_request        : 1;
818   /* cached allocation is invalid (request has changed, probably) */
819   guint needs_allocation            : 1;
820   guint show_on_set_parent          : 1;
821   guint has_clip                    : 1;
822   guint clip_to_allocation          : 1;
823   guint enable_model_view_transform : 1;
824   guint enable_paint_unmapped       : 1;
825   guint has_pointer                 : 1;
826   guint propagated_one_redraw       : 1;
827   guint paint_volume_valid          : 1;
828   guint last_paint_volume_valid     : 1;
829   guint in_clone_paint              : 1;
830   guint transform_valid             : 1;
831   /* This is TRUE if anything has queued a redraw since we were last
832      painted. In this case effect_to_redraw will point to an effect
833      the redraw was queued from or it will be NULL if the redraw was
834      queued without an effect. */
835   guint is_dirty                    : 1;
836   guint bg_color_set                : 1;
837   guint content_box_valid           : 1;
838   guint x_expand_set                : 1;
839   guint y_expand_set                : 1;
840   guint needs_compute_expand        : 1;
841   guint needs_x_expand              : 1;
842   guint needs_y_expand              : 1;
843 };
844 
845 enum
846 {
847   PROP_0,
848 
849   PROP_NAME,
850 
851   /* X, Y, WIDTH, HEIGHT are "do what I mean" properties;
852    * when set they force a size request, when gotten they
853    * get the allocation if the allocation is valid, and the
854    * request otherwise
855    */
856   PROP_X,
857   PROP_Y,
858   PROP_WIDTH,
859   PROP_HEIGHT,
860 
861   PROP_POSITION,
862   PROP_SIZE,
863 
864   /* Then the rest of these size-related properties are the "actual"
865    * underlying properties set or gotten by X, Y, WIDTH, HEIGHT
866    */
867   PROP_FIXED_X,
868   PROP_FIXED_Y,
869 
870   PROP_FIXED_POSITION_SET,
871 
872   PROP_MIN_WIDTH,
873   PROP_MIN_WIDTH_SET,
874 
875   PROP_MIN_HEIGHT,
876   PROP_MIN_HEIGHT_SET,
877 
878   PROP_NATURAL_WIDTH,
879   PROP_NATURAL_WIDTH_SET,
880 
881   PROP_NATURAL_HEIGHT,
882   PROP_NATURAL_HEIGHT_SET,
883 
884   PROP_REQUEST_MODE,
885 
886   /* Allocation properties are read-only */
887   PROP_ALLOCATION,
888 
889   PROP_DEPTH, /* XXX:2.0 remove */
890   PROP_Z_POSITION,
891 
892   PROP_CLIP, /* XXX:2.0 remove */
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   PROP_SCALE_CENTER_X, /* XXX:2.0 remove */
913   PROP_SCALE_CENTER_Y, /* XXX:2.0 remove */
914   PROP_SCALE_GRAVITY, /* XXX:2.0 remove */
915 
916   PROP_ROTATION_ANGLE_X, /* XXX:2.0 rename to rotation-x */
917   PROP_ROTATION_ANGLE_Y, /* XXX:2.0 rename to rotation-y */
918   PROP_ROTATION_ANGLE_Z, /* XXX:2.0 rename to rotation-z */
919   PROP_ROTATION_CENTER_X, /* XXX:2.0 remove */
920   PROP_ROTATION_CENTER_Y, /* XXX:2.0 remove */
921   PROP_ROTATION_CENTER_Z, /* XXX:2.0 remove */
922   /* This property only makes sense for the z rotation because the
923      others would depend on the actor having a size along the
924      z-axis */
925   PROP_ROTATION_CENTER_Z_GRAVITY, /* XXX:2.0 remove */
926 
927   PROP_ANCHOR_X, /* XXX:2.0 remove */
928   PROP_ANCHOR_Y, /* XXX:2.0 remove */
929   PROP_ANCHOR_GRAVITY, /*XXX:2.0 remove */
930 
931   PROP_TRANSLATION_X,
932   PROP_TRANSLATION_Y,
933   PROP_TRANSLATION_Z,
934 
935   PROP_TRANSFORM,
936   PROP_TRANSFORM_SET,
937   PROP_CHILD_TRANSFORM,
938   PROP_CHILD_TRANSFORM_SET,
939 
940   PROP_SHOW_ON_SET_PARENT, /*XXX:2.0 remove */
941 
942   PROP_TEXT_DIRECTION,
943   PROP_HAS_POINTER,
944 
945   PROP_ACTIONS,
946   PROP_CONSTRAINTS,
947   PROP_EFFECT,
948 
949   PROP_LAYOUT_MANAGER,
950 
951   PROP_X_EXPAND,
952   PROP_Y_EXPAND,
953   PROP_X_ALIGN,
954   PROP_Y_ALIGN,
955   PROP_MARGIN_TOP,
956   PROP_MARGIN_BOTTOM,
957   PROP_MARGIN_LEFT,
958   PROP_MARGIN_RIGHT,
959 
960   PROP_BACKGROUND_COLOR,
961   PROP_BACKGROUND_COLOR_SET,
962 
963   PROP_FIRST_CHILD,
964   PROP_LAST_CHILD,
965 
966   PROP_CONTENT,
967   PROP_CONTENT_GRAVITY,
968   PROP_CONTENT_BOX,
969   PROP_MINIFICATION_FILTER,
970   PROP_MAGNIFICATION_FILTER,
971   PROP_CONTENT_REPEAT,
972 
973   PROP_LAST
974 };
975 
976 static GParamSpec *obj_props[PROP_LAST];
977 
978 enum
979 {
980   SHOW,
981   HIDE,
982   DESTROY,
983   PARENT_SET,
984   KEY_FOCUS_IN,
985   KEY_FOCUS_OUT,
986   PAINT,
987   PICK,
988   REALIZE,
989   UNREALIZE,
990   QUEUE_REDRAW,
991   QUEUE_RELAYOUT,
992   EVENT,
993   CAPTURED_EVENT,
994   BUTTON_PRESS_EVENT,
995   BUTTON_RELEASE_EVENT,
996   SCROLL_EVENT,
997   KEY_PRESS_EVENT,
998   KEY_RELEASE_EVENT,
999   MOTION_EVENT,
1000   ENTER_EVENT,
1001   LEAVE_EVENT,
1002   ALLOCATION_CHANGED,
1003   TRANSITIONS_COMPLETED,
1004   TOUCH_EVENT,
1005   TRANSITION_STOPPED,
1006 
1007   LAST_SIGNAL
1008 };
1009 
1010 static guint actor_signals[LAST_SIGNAL] = { 0, };
1011 
1012 typedef struct _TransitionClosure
1013 {
1014   ClutterActor *actor;
1015   ClutterTransition *transition;
1016   gchar *name;
1017   gulong completed_id;
1018 } TransitionClosure;
1019 
1020 static void clutter_container_iface_init  (ClutterContainerIface  *iface);
1021 static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
1022 static void clutter_animatable_iface_init (ClutterAnimatableIface *iface);
1023 static void atk_implementor_iface_init    (AtkImplementorIface    *iface);
1024 
1025 /* These setters are all static for now, maybe they should be in the
1026  * public API, but they are perhaps obscure enough to leave only as
1027  * properties
1028  */
1029 static void clutter_actor_set_min_width          (ClutterActor *self,
1030                                                   gfloat        min_width);
1031 static void clutter_actor_set_min_height         (ClutterActor *self,
1032                                                   gfloat        min_height);
1033 static void clutter_actor_set_natural_width      (ClutterActor *self,
1034                                                   gfloat        natural_width);
1035 static void clutter_actor_set_natural_height     (ClutterActor *self,
1036                                                   gfloat        natural_height);
1037 static void clutter_actor_set_min_width_set      (ClutterActor *self,
1038                                                   gboolean      use_min_width);
1039 static void clutter_actor_set_min_height_set     (ClutterActor *self,
1040                                                   gboolean      use_min_height);
1041 static void clutter_actor_set_natural_width_set  (ClutterActor *self,
1042                                                   gboolean  use_natural_width);
1043 static void clutter_actor_set_natural_height_set (ClutterActor *self,
1044                                                   gboolean  use_natural_height);
1045 static void clutter_actor_update_map_state       (ClutterActor  *self,
1046                                                   MapStateChange change);
1047 static void clutter_actor_unrealize_not_hiding   (ClutterActor *self);
1048 
1049 /* Helper routines for managing anchor coords */
1050 static void clutter_anchor_coord_get_units (ClutterActor      *self,
1051                                             const AnchorCoord *coord,
1052                                             gfloat            *x,
1053                                             gfloat            *y,
1054                                             gfloat            *z);
1055 static void clutter_anchor_coord_set_units (AnchorCoord       *coord,
1056                                             gfloat             x,
1057                                             gfloat             y,
1058                                             gfloat             z);
1059 
1060 static ClutterGravity clutter_anchor_coord_get_gravity (const AnchorCoord *coord);
1061 static void           clutter_anchor_coord_set_gravity (AnchorCoord       *coord,
1062                                                         ClutterGravity     gravity);
1063 
1064 static gboolean clutter_anchor_coord_is_zero (const AnchorCoord *coord);
1065 
1066 static void _clutter_actor_get_relative_transformation_matrix (ClutterActor *self,
1067                                                                ClutterActor *ancestor,
1068                                                                CoglMatrix *matrix);
1069 
1070 static ClutterPaintVolume *_clutter_actor_get_paint_volume_mutable (ClutterActor *self);
1071 
1072 static guint8   clutter_actor_get_paint_opacity_internal        (ClutterActor *self);
1073 
1074 static inline void clutter_actor_set_background_color_internal (ClutterActor *self,
1075                                                                 const ClutterColor *color);
1076 
1077 static void on_layout_manager_changed (ClutterLayoutManager *manager,
1078                                        ClutterActor         *self);
1079 
1080 static inline void clutter_actor_queue_compute_expand (ClutterActor *self);
1081 
1082 static inline void clutter_actor_set_margin_internal (ClutterActor *self,
1083                                                       gfloat        margin,
1084                                                       GParamSpec   *pspec);
1085 
1086 static void clutter_actor_set_transform_internal (ClutterActor        *self,
1087                                                   const ClutterMatrix *transform);
1088 static void clutter_actor_set_child_transform_internal (ClutterActor        *self,
1089                                                         const ClutterMatrix *transform);
1090 
1091 static void     clutter_actor_realize_internal          (ClutterActor *self);
1092 static void     clutter_actor_unrealize_internal        (ClutterActor *self);
1093 
1094 /* Helper macro which translates by the anchor coord, applies the
1095    given transformation and then translates back */
1096 #define TRANSFORM_ABOUT_ANCHOR_COORD(a,m,c,_transform)  G_STMT_START { \
1097   gfloat _tx, _ty, _tz;                                                \
1098   clutter_anchor_coord_get_units ((a), (c), &_tx, &_ty, &_tz);         \
1099   cogl_matrix_translate ((m), _tx, _ty, _tz);                          \
1100   { _transform; }                                                      \
1101   cogl_matrix_translate ((m), -_tx, -_ty, -_tz);        } G_STMT_END
1102 
1103 static GQuark quark_shader_data = 0;
1104 static GQuark quark_actor_layout_info = 0;
1105 static GQuark quark_actor_transform_info = 0;
1106 static GQuark quark_actor_animation_info = 0;
1107 
1108 G_DEFINE_TYPE_WITH_CODE (ClutterActor,
1109                          clutter_actor,
1110                          G_TYPE_INITIALLY_UNOWNED,
1111                          G_ADD_PRIVATE (ClutterActor)
1112                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
1113                                                 clutter_container_iface_init)
1114                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
1115                                                 clutter_scriptable_iface_init)
1116                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE,
1117                                                 clutter_animatable_iface_init)
1118                          G_IMPLEMENT_INTERFACE (ATK_TYPE_IMPLEMENTOR,
1119                                                 atk_implementor_iface_init));
1120 
1121 /*< private >
1122  * clutter_actor_get_debug_name:
1123  * @actor: a #ClutterActor
1124  *
1125  * Retrieves a printable name of @actor for debugging messages
1126  *
1127  * Return value: a string with a printable name
1128  */
1129 const gchar *
_clutter_actor_get_debug_name(ClutterActor * actor)1130 _clutter_actor_get_debug_name (ClutterActor *actor)
1131 {
1132   ClutterActorPrivate *priv = actor->priv;
1133   const gchar *retval;
1134 
1135 #ifdef CLUTTER_ENABLE_DEBUG
1136   if (G_UNLIKELY (priv->debug_name == NULL))
1137     {
1138       priv->debug_name = g_strdup_printf ("<%s>[<%s>:%p]",
1139                                           priv->name != NULL ? priv->name
1140                                                              : "unnamed",
1141                                           G_OBJECT_TYPE_NAME (actor),
1142                                           actor);
1143     }
1144 
1145   retval = priv->debug_name;
1146 #else
1147   retval = priv->name != NULL
1148          ? priv->name
1149          : G_OBJECT_TYPE_NAME (actor);
1150 #endif
1151 
1152   return retval;
1153 }
1154 
1155 #ifdef CLUTTER_ENABLE_DEBUG
1156 /* XXX - this is for debugging only, remove once working (or leave
1157  * in only in some debug mode). Should leave it for a little while
1158  * until we're confident in the new map/realize/visible handling.
1159  */
1160 static inline void
clutter_actor_verify_map_state(ClutterActor * self)1161 clutter_actor_verify_map_state (ClutterActor *self)
1162 {
1163   ClutterActorPrivate *priv = self->priv;
1164 
1165   if (CLUTTER_ACTOR_IS_REALIZED (self))
1166     {
1167       /* all bets are off during reparent when we're potentially realized,
1168        * but should not be according to invariants
1169        */
1170       if (!CLUTTER_ACTOR_IN_REPARENT (self))
1171         {
1172           if (priv->parent == NULL)
1173             {
1174               if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
1175                 {
1176                 }
1177               else
1178                 g_warning ("Realized non-toplevel actor '%s' should "
1179                            "have a parent",
1180                            _clutter_actor_get_debug_name (self));
1181             }
1182           else if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent))
1183             {
1184               g_warning ("Realized actor %s has an unrealized parent %s",
1185                          _clutter_actor_get_debug_name (self),
1186                          _clutter_actor_get_debug_name (priv->parent));
1187             }
1188         }
1189     }
1190 
1191   if (CLUTTER_ACTOR_IS_MAPPED (self))
1192     {
1193       if (!CLUTTER_ACTOR_IS_REALIZED (self))
1194         g_warning ("Actor '%s' is mapped but not realized",
1195                    _clutter_actor_get_debug_name (self));
1196 
1197       /* remaining bets are off during reparent when we're potentially
1198        * mapped, but should not be according to invariants
1199        */
1200       if (!CLUTTER_ACTOR_IN_REPARENT (self))
1201         {
1202           if (priv->parent == NULL)
1203             {
1204               if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
1205                 {
1206                   if (!CLUTTER_ACTOR_IS_VISIBLE (self) &&
1207                       !CLUTTER_ACTOR_IN_DESTRUCTION (self))
1208                     {
1209                       g_warning ("Toplevel actor '%s' is mapped "
1210                                  "but not visible",
1211                                  _clutter_actor_get_debug_name (self));
1212                     }
1213                 }
1214               else
1215                 {
1216                   g_warning ("Mapped actor '%s' should have a parent",
1217                              _clutter_actor_get_debug_name (self));
1218                 }
1219             }
1220           else
1221             {
1222               ClutterActor *iter = self;
1223 
1224               /* check for the enable_paint_unmapped flag on the actor
1225                * and parents; if the flag is enabled at any point of this
1226                * branch of the scene graph then all the later checks
1227                * become pointless
1228                */
1229               while (iter != NULL)
1230                 {
1231                   if (iter->priv->enable_paint_unmapped)
1232                     return;
1233 
1234                   iter = iter->priv->parent;
1235                 }
1236 
1237               if (!CLUTTER_ACTOR_IS_VISIBLE (priv->parent))
1238                 {
1239                   g_warning ("Actor '%s' should not be mapped if parent '%s'"
1240                              "is not visible",
1241                              _clutter_actor_get_debug_name (self),
1242                              _clutter_actor_get_debug_name (priv->parent));
1243                 }
1244 
1245               if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent))
1246                 {
1247                   g_warning ("Actor '%s' should not be mapped if parent '%s'"
1248                              "is not realized",
1249                              _clutter_actor_get_debug_name (self),
1250                              _clutter_actor_get_debug_name (priv->parent));
1251                 }
1252 
1253               if (!CLUTTER_ACTOR_IS_TOPLEVEL (priv->parent))
1254                 {
1255                   if (!CLUTTER_ACTOR_IS_MAPPED (priv->parent))
1256                     g_warning ("Actor '%s' is mapped but its non-toplevel "
1257                                "parent '%s' is not mapped",
1258                                _clutter_actor_get_debug_name (self),
1259                                _clutter_actor_get_debug_name (priv->parent));
1260                 }
1261             }
1262         }
1263     }
1264 }
1265 
1266 #endif /* CLUTTER_ENABLE_DEBUG */
1267 
1268 static void
clutter_actor_set_mapped(ClutterActor * self,gboolean mapped)1269 clutter_actor_set_mapped (ClutterActor *self,
1270                           gboolean      mapped)
1271 {
1272   if (CLUTTER_ACTOR_IS_MAPPED (self) == mapped)
1273     return;
1274 
1275   if (mapped)
1276     {
1277       CLUTTER_ACTOR_GET_CLASS (self)->map (self);
1278       g_assert (CLUTTER_ACTOR_IS_MAPPED (self));
1279     }
1280   else
1281     {
1282       CLUTTER_ACTOR_GET_CLASS (self)->unmap (self);
1283       g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
1284     }
1285 }
1286 
1287 /* this function updates the mapped and realized states according to
1288  * invariants, in the appropriate order.
1289  */
1290 static void
clutter_actor_update_map_state(ClutterActor * self,MapStateChange change)1291 clutter_actor_update_map_state (ClutterActor  *self,
1292                                 MapStateChange change)
1293 {
1294   gboolean was_mapped;
1295 
1296   was_mapped = CLUTTER_ACTOR_IS_MAPPED (self);
1297 
1298   if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
1299     {
1300       /* the mapped flag on top-level actors must be set by the
1301        * per-backend implementation because it might be asynchronous.
1302        *
1303        * That is, the MAPPED flag on toplevels currently tracks the X
1304        * server mapped-ness of the window, while the expected behavior
1305        * (if used to GTK) may be to track WM_STATE!=WithdrawnState.
1306        * This creates some weird complexity by breaking the invariant
1307        * that if we're visible and all ancestors shown then we are
1308        * also mapped - instead, we are mapped if all ancestors
1309        * _possibly excepting_ the stage are mapped. The stage
1310        * will map/unmap for example when it is minimized or
1311        * moved to another workspace.
1312        *
1313        * So, the only invariant on the stage is that if visible it
1314        * should be realized, and that it has to be visible to be
1315        * mapped.
1316        */
1317       if (CLUTTER_ACTOR_IS_VISIBLE (self))
1318         clutter_actor_realize (self);
1319 
1320       switch (change)
1321         {
1322         case MAP_STATE_CHECK:
1323           break;
1324 
1325         case MAP_STATE_MAKE_MAPPED:
1326           g_assert (!was_mapped);
1327           clutter_actor_set_mapped (self, TRUE);
1328           break;
1329 
1330         case MAP_STATE_MAKE_UNMAPPED:
1331           g_assert (was_mapped);
1332           clutter_actor_set_mapped (self, FALSE);
1333           break;
1334 
1335         case MAP_STATE_MAKE_UNREALIZED:
1336           /* we only use MAKE_UNREALIZED in unparent,
1337            * and unparenting a stage isn't possible.
1338            * If someone wants to just unrealize a stage
1339            * then clutter_actor_unrealize() doesn't
1340            * go through this codepath.
1341            */
1342           g_warning ("Trying to force unrealize stage is not allowed");
1343           break;
1344         }
1345 
1346       if (CLUTTER_ACTOR_IS_MAPPED (self) &&
1347           !CLUTTER_ACTOR_IS_VISIBLE (self) &&
1348           !CLUTTER_ACTOR_IN_DESTRUCTION (self))
1349         {
1350           g_warning ("Clutter toplevel of type '%s' is not visible, but "
1351                      "it is somehow still mapped",
1352                      _clutter_actor_get_debug_name (self));
1353         }
1354     }
1355   else
1356     {
1357       ClutterActorPrivate *priv = self->priv;
1358       ClutterActor *parent = priv->parent;
1359       gboolean should_be_mapped;
1360       gboolean may_be_realized;
1361       gboolean must_be_realized;
1362 
1363       should_be_mapped = FALSE;
1364       may_be_realized = TRUE;
1365       must_be_realized = FALSE;
1366 
1367       if (parent == NULL || change == MAP_STATE_MAKE_UNREALIZED)
1368         {
1369           may_be_realized = FALSE;
1370         }
1371       else
1372         {
1373           /* Maintain invariant that if parent is mapped, and we are
1374            * visible, then we are mapped ...  unless parent is a
1375            * stage, in which case we map regardless of parent's map
1376            * state but do require stage to be visible and realized.
1377            *
1378            * If parent is realized, that does not force us to be
1379            * realized; but if parent is unrealized, that does force
1380            * us to be unrealized.
1381            *
1382            * The reason we don't force children to realize with
1383            * parents is _clutter_actor_rerealize(); if we require that
1384            * a realized parent means children are realized, then to
1385            * unrealize an actor we would have to unrealize its
1386            * parents, which would end up meaning unrealizing and
1387            * hiding the entire stage. So we allow unrealizing a
1388            * child (as long as that child is not mapped) while that
1389            * child still has a realized parent.
1390            *
1391            * Also, if we unrealize from leaf nodes to root, and
1392            * realize from root to leaf, the invariants are never
1393            * violated if we allow children to be unrealized
1394            * while parents are realized.
1395            *
1396            * When unmapping, MAP_STATE_MAKE_UNMAPPED is specified
1397            * to force us to unmap, even though parent is still
1398            * mapped. This is because we're unmapping from leaf nodes
1399            * up to root nodes.
1400            */
1401           if (CLUTTER_ACTOR_IS_VISIBLE (self) &&
1402               change != MAP_STATE_MAKE_UNMAPPED)
1403             {
1404               gboolean parent_is_visible_realized_toplevel;
1405 
1406               parent_is_visible_realized_toplevel =
1407                 (CLUTTER_ACTOR_IS_TOPLEVEL (parent) &&
1408                  CLUTTER_ACTOR_IS_VISIBLE (parent) &&
1409                  CLUTTER_ACTOR_IS_REALIZED (parent));
1410 
1411               if (CLUTTER_ACTOR_IS_MAPPED (parent) ||
1412                   parent_is_visible_realized_toplevel)
1413                 {
1414                   must_be_realized = TRUE;
1415                   should_be_mapped = TRUE;
1416                 }
1417             }
1418 
1419           /* if the actor has been set to be painted even if unmapped
1420            * then we should map it and check for realization as well;
1421            * this is an override for the branch of the scene graph
1422            * which begins with this node
1423            */
1424           if (priv->enable_paint_unmapped)
1425             {
1426               should_be_mapped = TRUE;
1427               must_be_realized = TRUE;
1428             }
1429 
1430           if (!CLUTTER_ACTOR_IS_REALIZED (parent))
1431             may_be_realized = FALSE;
1432         }
1433 
1434       if (change == MAP_STATE_MAKE_MAPPED && !should_be_mapped)
1435         {
1436           if (parent == NULL)
1437             g_warning ("Attempting to map a child that does not "
1438                        "meet the necessary invariants: the actor '%s' "
1439                        "has no parent",
1440                        _clutter_actor_get_debug_name (self));
1441           else
1442             g_warning ("Attempting to map a child that does not "
1443                        "meet the necessary invariants: the actor '%s' "
1444                        "is parented to an unmapped actor '%s'",
1445                        _clutter_actor_get_debug_name (self),
1446                        _clutter_actor_get_debug_name (priv->parent));
1447         }
1448 
1449       /* If in reparent, we temporarily suspend unmap and unrealize.
1450        *
1451        * We want to go in the order "realize, map" and "unmap, unrealize"
1452        */
1453 
1454       /* Unmap */
1455       if (!should_be_mapped && !CLUTTER_ACTOR_IN_REPARENT (self))
1456         clutter_actor_set_mapped (self, FALSE);
1457 
1458       /* Realize */
1459       if (must_be_realized)
1460         clutter_actor_realize (self);
1461 
1462       /* if we must be realized then we may be, presumably */
1463       g_assert (!(must_be_realized && !may_be_realized));
1464 
1465       /* Unrealize */
1466       if (!may_be_realized && !CLUTTER_ACTOR_IN_REPARENT (self))
1467         clutter_actor_unrealize_not_hiding (self);
1468 
1469       /* Map */
1470       if (should_be_mapped)
1471         {
1472           if (!must_be_realized)
1473             g_warning ("Somehow we think actor '%s' should be mapped but "
1474                        "not realized, which isn't allowed",
1475                        _clutter_actor_get_debug_name (self));
1476 
1477           /* realization is allowed to fail (though I don't know what
1478            * an app is supposed to do about that - shouldn't it just
1479            * be a g_error? anyway, we have to avoid mapping if this
1480            * happens)
1481            */
1482           if (CLUTTER_ACTOR_IS_REALIZED (self))
1483             clutter_actor_set_mapped (self, TRUE);
1484         }
1485     }
1486 
1487 #ifdef CLUTTER_ENABLE_DEBUG
1488   /* check all invariants were kept */
1489   clutter_actor_verify_map_state (self);
1490 #endif
1491 }
1492 
1493 static void
clutter_actor_real_map(ClutterActor * self)1494 clutter_actor_real_map (ClutterActor *self)
1495 {
1496   ClutterActorPrivate *priv = self->priv;
1497   ClutterActor *stage, *iter;
1498 
1499   g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
1500 
1501   CLUTTER_NOTE (ACTOR, "Mapping actor '%s'",
1502                 _clutter_actor_get_debug_name (self));
1503 
1504   CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_MAPPED);
1505 
1506   stage = _clutter_actor_get_stage_internal (self);
1507   priv->pick_id = _clutter_stage_acquire_pick_id (CLUTTER_STAGE (stage), self);
1508 
1509   CLUTTER_NOTE (ACTOR, "Pick id '%d' for actor '%s'",
1510                 priv->pick_id,
1511                 _clutter_actor_get_debug_name (self));
1512 
1513   /* notify on parent mapped before potentially mapping
1514    * children, so apps see a top-down notification.
1515    */
1516   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]);
1517 
1518   for (iter = self->priv->first_child;
1519        iter != NULL;
1520        iter = iter->priv->next_sibling)
1521     {
1522       clutter_actor_map (iter);
1523     }
1524 }
1525 
1526 /**
1527  * clutter_actor_map:
1528  * @self: A #ClutterActor
1529  *
1530  * Sets the %CLUTTER_ACTOR_MAPPED flag on the actor and possibly maps
1531  * and realizes its children if they are visible. Does nothing if the
1532  * actor is not visible.
1533  *
1534  * Calling this function is strongly disencouraged: the default
1535  * implementation of #ClutterActorClass.map() will map all the children
1536  * of an actor when mapping its parent.
1537  *
1538  * When overriding map, it is mandatory to chain up to the parent
1539  * implementation.
1540  *
1541  * Since: 1.0
1542  */
1543 void
clutter_actor_map(ClutterActor * self)1544 clutter_actor_map (ClutterActor *self)
1545 {
1546   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1547 
1548   if (CLUTTER_ACTOR_IS_MAPPED (self))
1549     return;
1550 
1551   if (!CLUTTER_ACTOR_IS_VISIBLE (self))
1552     return;
1553 
1554   clutter_actor_update_map_state (self, MAP_STATE_MAKE_MAPPED);
1555 }
1556 
1557 /**
1558  * clutter_actor_is_mapped:
1559  * @self: a #ClutterActor
1560  *
1561  * Checks whether a #ClutterActor has been set as mapped.
1562  *
1563  * See also %CLUTTER_ACTOR_IS_MAPPED and #ClutterActor:mapped
1564  *
1565  * Returns: %TRUE if the actor is mapped
1566  *
1567  * Since: 1.24
1568  */
1569 gboolean
clutter_actor_is_mapped(ClutterActor * self)1570 clutter_actor_is_mapped (ClutterActor *self)
1571 {
1572   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
1573 
1574   return CLUTTER_ACTOR_IS_MAPPED (self);
1575 }
1576 
1577 static void
clutter_actor_real_unmap(ClutterActor * self)1578 clutter_actor_real_unmap (ClutterActor *self)
1579 {
1580   ClutterActorPrivate *priv = self->priv;
1581   ClutterActor *iter;
1582 
1583   g_assert (CLUTTER_ACTOR_IS_MAPPED (self));
1584 
1585   CLUTTER_NOTE (ACTOR, "Unmapping actor '%s'",
1586                 _clutter_actor_get_debug_name (self));
1587 
1588   for (iter = self->priv->first_child;
1589        iter != NULL;
1590        iter = iter->priv->next_sibling)
1591     {
1592       clutter_actor_unmap (iter);
1593     }
1594 
1595   CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_MAPPED);
1596 
1597   /* clear the contents of the last paint volume, so that hiding + moving +
1598    * showing will not result in the wrong area being repainted
1599    */
1600   _clutter_paint_volume_init_static (&priv->last_paint_volume, NULL);
1601   priv->last_paint_volume_valid = TRUE;
1602 
1603   /* notify on parent mapped after potentially unmapping
1604    * children, so apps see a bottom-up notification.
1605    */
1606   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]);
1607 
1608   /* relinquish keyboard focus if we were unmapped while owning it */
1609   if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
1610     {
1611       ClutterStage *stage;
1612 
1613       stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
1614 
1615       if (stage != NULL)
1616         _clutter_stage_release_pick_id (stage, priv->pick_id);
1617 
1618       priv->pick_id = -1;
1619 
1620       if (stage != NULL &&
1621           clutter_stage_get_key_focus (stage) == self)
1622         {
1623           clutter_stage_set_key_focus (stage, NULL);
1624         }
1625     }
1626 }
1627 
1628 /**
1629  * clutter_actor_unmap:
1630  * @self: A #ClutterActor
1631  *
1632  * Unsets the %CLUTTER_ACTOR_MAPPED flag on the actor and possibly
1633  * unmaps its children if they were mapped.
1634  *
1635  * Calling this function is not encouraged: the default #ClutterActor
1636  * implementation of #ClutterActorClass.unmap() will also unmap any
1637  * eventual children by default when their parent is unmapped.
1638  *
1639  * When overriding #ClutterActorClass.unmap(), it is mandatory to
1640  * chain up to the parent implementation.
1641  *
1642  * It is important to note that the implementation of the
1643  * #ClutterActorClass.unmap() virtual function may be called after
1644  * the #ClutterActorClass.destroy() or the #GObjectClass.dispose()
1645  * implementation, but it is guaranteed to be called before the
1646  * #GObjectClass.finalize() implementation.
1647  *
1648  * Since: 1.0
1649  */
1650 void
clutter_actor_unmap(ClutterActor * self)1651 clutter_actor_unmap (ClutterActor *self)
1652 {
1653   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1654 
1655   if (!CLUTTER_ACTOR_IS_MAPPED (self))
1656     return;
1657 
1658   clutter_actor_update_map_state (self, MAP_STATE_MAKE_UNMAPPED);
1659 }
1660 
1661 static void
clutter_actor_real_show(ClutterActor * self)1662 clutter_actor_real_show (ClutterActor *self)
1663 {
1664   ClutterActorPrivate *priv = self->priv;
1665 
1666   if (CLUTTER_ACTOR_IS_VISIBLE (self))
1667     return;
1668 
1669   CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_VISIBLE);
1670 
1671   /* we notify on the "visible" flag in the clutter_actor_show()
1672    * wrapper so the entire show signal emission completes first,
1673    * and the branch of the scene graph is in a stable state
1674    */
1675   clutter_actor_update_map_state (self, MAP_STATE_CHECK);
1676 
1677   /* we queue a relayout unless the actor is inside a
1678    * container that explicitly told us not to
1679    */
1680   if (priv->parent != NULL &&
1681       (!(priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT)))
1682     {
1683       /* While an actor is hidden the parent may not have
1684        * allocated/requested so we need to start from scratch
1685        * and avoid the short-circuiting in
1686        * clutter_actor_queue_relayout().
1687        */
1688       priv->needs_width_request  = FALSE;
1689       priv->needs_height_request = FALSE;
1690       priv->needs_allocation     = FALSE;
1691 
1692       clutter_actor_queue_relayout (self);
1693     }
1694 }
1695 
1696 static inline void
set_show_on_set_parent(ClutterActor * self,gboolean set_show)1697 set_show_on_set_parent (ClutterActor *self,
1698                         gboolean      set_show)
1699 {
1700   ClutterActorPrivate *priv = self->priv;
1701 
1702   set_show = !!set_show;
1703 
1704   if (priv->show_on_set_parent == set_show)
1705     return;
1706 
1707   if (priv->parent == NULL)
1708     {
1709       priv->show_on_set_parent = set_show;
1710       g_object_notify_by_pspec (G_OBJECT (self),
1711                                 obj_props[PROP_SHOW_ON_SET_PARENT]);
1712     }
1713 }
1714 
1715 /**
1716  * clutter_actor_show:
1717  * @self: A #ClutterActor
1718  *
1719  * Flags an actor to be displayed. An actor that isn't shown will not
1720  * be rendered on the stage.
1721  *
1722  * Actors are visible by default.
1723  *
1724  * If this function is called on an actor without a parent, the
1725  * #ClutterActor:show-on-set-parent will be set to %TRUE as a side
1726  * effect.
1727  */
1728 void
clutter_actor_show(ClutterActor * self)1729 clutter_actor_show (ClutterActor *self)
1730 {
1731   ClutterActorPrivate *priv;
1732 
1733   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1734 
1735   /* simple optimization */
1736   if (CLUTTER_ACTOR_IS_VISIBLE (self))
1737     {
1738       /* we still need to set the :show-on-set-parent property, in
1739        * case show() is called on an unparented actor
1740        */
1741       set_show_on_set_parent (self, TRUE);
1742       return;
1743     }
1744 
1745 #ifdef CLUTTER_ENABLE_DEBUG
1746   clutter_actor_verify_map_state (self);
1747 #endif
1748 
1749   priv = self->priv;
1750 
1751   g_object_freeze_notify (G_OBJECT (self));
1752 
1753   set_show_on_set_parent (self, TRUE);
1754 
1755   /* if we're showing a child that needs to expand, or may
1756    * expand, then we need to recompute the expand flags for
1757    * its parent as well
1758    */
1759   if (priv->needs_compute_expand ||
1760       priv->needs_x_expand ||
1761       priv->needs_y_expand)
1762     {
1763       clutter_actor_queue_compute_expand (self);
1764     }
1765 
1766   g_signal_emit (self, actor_signals[SHOW], 0);
1767   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]);
1768 
1769   if (priv->parent != NULL)
1770     clutter_actor_queue_redraw (priv->parent);
1771 
1772   g_object_thaw_notify (G_OBJECT (self));
1773 }
1774 
1775 /**
1776  * clutter_actor_is_visible:
1777  * @self: a #ClutterActor
1778  *
1779  * Checks whether an actor is marked as visible.
1780  *
1781  * See also %CLUTTER_ACTOR_IS_VISIBLE and #ClutterActor:visible.
1782  *
1783  * Returns: %TRUE if the actor visible
1784  *
1785  * Since: 1.24
1786  */
1787 gboolean
clutter_actor_is_visible(ClutterActor * self)1788 clutter_actor_is_visible (ClutterActor *self)
1789 {
1790   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
1791 
1792   return CLUTTER_ACTOR_IS_VISIBLE (self);
1793 }
1794 
1795 /**
1796  * clutter_actor_show_all:
1797  * @self: a #ClutterActor
1798  *
1799  * Calls clutter_actor_show() on all children of an actor (if any).
1800  *
1801  * Since: 0.2
1802  *
1803  * Deprecated: 1.10: Actors are visible by default
1804  */
1805 void
clutter_actor_show_all(ClutterActor * self)1806 clutter_actor_show_all (ClutterActor *self)
1807 {
1808   ClutterActorClass *klass;
1809 
1810   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1811 
1812   klass = CLUTTER_ACTOR_GET_CLASS (self);
1813   if (klass->show_all)
1814     klass->show_all (self);
1815 }
1816 
1817 static void
clutter_actor_real_hide(ClutterActor * self)1818 clutter_actor_real_hide (ClutterActor *self)
1819 {
1820   ClutterActorPrivate *priv = self->priv;
1821 
1822   if (!CLUTTER_ACTOR_IS_VISIBLE (self))
1823     return;
1824 
1825   CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_VISIBLE);
1826 
1827   /* we notify on the "visible" flag in the clutter_actor_hide()
1828    * wrapper so the entire hide signal emission completes first,
1829    * and the branch of the scene graph is in a stable state
1830    */
1831   clutter_actor_update_map_state (self, MAP_STATE_CHECK);
1832 
1833   /* we queue a relayout unless the actor is inside a
1834    * container that explicitly told us not to
1835    */
1836   if (priv->parent != NULL &&
1837       (!(priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT)))
1838     clutter_actor_queue_relayout (priv->parent);
1839 }
1840 
1841 /**
1842  * clutter_actor_hide:
1843  * @self: A #ClutterActor
1844  *
1845  * Flags an actor to be hidden. A hidden actor will not be
1846  * rendered on the stage.
1847  *
1848  * Actors are visible by default.
1849  *
1850  * If this function is called on an actor without a parent, the
1851  * #ClutterActor:show-on-set-parent property will be set to %FALSE
1852  * as a side-effect.
1853  */
1854 void
clutter_actor_hide(ClutterActor * self)1855 clutter_actor_hide (ClutterActor *self)
1856 {
1857   ClutterActorPrivate *priv;
1858 
1859   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1860 
1861   /* simple optimization */
1862   if (!CLUTTER_ACTOR_IS_VISIBLE (self))
1863     {
1864       /* we still need to set the :show-on-set-parent property, in
1865        * case hide() is called on an unparented actor
1866        */
1867       set_show_on_set_parent (self, FALSE);
1868       return;
1869     }
1870 
1871 #ifdef CLUTTER_ENABLE_DEBUG
1872   clutter_actor_verify_map_state (self);
1873 #endif
1874 
1875   priv = self->priv;
1876 
1877   g_object_freeze_notify (G_OBJECT (self));
1878 
1879   set_show_on_set_parent (self, FALSE);
1880 
1881   /* if we're hiding a child that needs to expand, or may
1882    * expand, then we need to recompute the expand flags for
1883    * its parent as well
1884    */
1885   if (priv->needs_compute_expand ||
1886       priv->needs_x_expand ||
1887       priv->needs_y_expand)
1888     {
1889       clutter_actor_queue_compute_expand (self);
1890     }
1891 
1892   g_signal_emit (self, actor_signals[HIDE], 0);
1893   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]);
1894 
1895   if (priv->parent != NULL)
1896     clutter_actor_queue_redraw (priv->parent);
1897 
1898   g_object_thaw_notify (G_OBJECT (self));
1899 }
1900 
1901 /**
1902  * clutter_actor_hide_all:
1903  * @self: a #ClutterActor
1904  *
1905  * Calls clutter_actor_hide() on all child actors (if any).
1906  *
1907  * Since: 0.2
1908  *
1909  * Deprecated: 1.10: Using clutter_actor_hide() on the actor will
1910  *   prevent its children from being painted as well.
1911  */
1912 void
clutter_actor_hide_all(ClutterActor * self)1913 clutter_actor_hide_all (ClutterActor *self)
1914 {
1915   ClutterActorClass *klass;
1916 
1917   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1918 
1919   klass = CLUTTER_ACTOR_GET_CLASS (self);
1920   if (klass->hide_all)
1921     klass->hide_all (self);
1922 }
1923 
1924 /**
1925  * clutter_actor_realize:
1926  * @self: A #ClutterActor
1927  *
1928  * Realization informs the actor that it is attached to a stage. It
1929  * can use this to allocate resources if it wanted to delay allocation
1930  * until it would be rendered. However it is perfectly acceptable for
1931  * an actor to create resources before being realized because Clutter
1932  * only ever has a single rendering context so that actor is free to
1933  * be moved from one stage to another.
1934  *
1935  * This function does nothing if the actor is already realized.
1936  *
1937  * Because a realized actor must have realized parent actors, calling
1938  * clutter_actor_realize() will also realize all parents of the actor.
1939  *
1940  * This function does not realize child actors, except in the special
1941  * case that realizing the stage, when the stage is visible, will
1942  * suddenly map (and thus realize) the children of the stage.
1943  *
1944  * Deprecated: 1.16: Actors are automatically realized, and nothing
1945  *   requires explicit realization.
1946  */
1947 void
clutter_actor_realize(ClutterActor * self)1948 clutter_actor_realize (ClutterActor *self)
1949 {
1950   g_return_if_fail (CLUTTER_IS_ACTOR (self));
1951 
1952   clutter_actor_realize_internal (self);
1953 }
1954 
1955 /**
1956  * clutter_actor_is_realized:
1957  * @self: a #ClutterActor
1958  *
1959  * Checks whether a #ClutterActor is realized.
1960  *
1961  * See also %CLUTTER_ACTOR_IS_REALIZED and #ClutterActor:realized.
1962  *
1963  * Returns: %TRUE if the actor is realized
1964  *
1965  * Since: 1.24
1966  */
1967 gboolean
clutter_actor_is_realized(ClutterActor * self)1968 clutter_actor_is_realized (ClutterActor *self)
1969 {
1970   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
1971 
1972   return CLUTTER_ACTOR_IS_REALIZED (self);
1973 }
1974 
1975 static void
clutter_actor_realize_internal(ClutterActor * self)1976 clutter_actor_realize_internal (ClutterActor *self)
1977 {
1978   ClutterActorPrivate *priv = self->priv;
1979 
1980 #ifdef CLUTTER_ENABLE_DEBUG
1981   clutter_actor_verify_map_state (self);
1982 #endif
1983 
1984   if (CLUTTER_ACTOR_IS_REALIZED (self))
1985     return;
1986 
1987   /* To be realized, our parent actors must be realized first.
1988    * This will only succeed if we're inside a toplevel.
1989    */
1990   if (priv->parent != NULL)
1991     clutter_actor_realize (priv->parent);
1992 
1993   if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
1994     {
1995       /* toplevels can be realized at any time */
1996     }
1997   else
1998     {
1999       /* "Fail" the realization if parent is missing or unrealized;
2000        * this should really be a g_warning() not some kind of runtime
2001        * failure; how can an app possibly recover? Instead it's a bug
2002        * in the app and the app should get an explanatory warning so
2003        * someone can fix it. But for now it's too hard to fix this
2004        * because e.g. ClutterTexture needs reworking.
2005        */
2006       if (priv->parent == NULL ||
2007           !CLUTTER_ACTOR_IS_REALIZED (priv->parent))
2008         return;
2009     }
2010 
2011   CLUTTER_NOTE (ACTOR, "Realizing actor '%s'", _clutter_actor_get_debug_name (self));
2012 
2013   CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
2014   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]);
2015 
2016   g_signal_emit (self, actor_signals[REALIZE], 0);
2017 
2018   /* Stage actor is allowed to unset the realized flag again in its
2019    * default signal handler, though that is a pathological situation.
2020    */
2021 
2022   /* If realization "failed" we'll have to update child state. */
2023   clutter_actor_update_map_state (self, MAP_STATE_CHECK);
2024 }
2025 
2026 static void
clutter_actor_real_unrealize(ClutterActor * self)2027 clutter_actor_real_unrealize (ClutterActor *self)
2028 {
2029   /* we must be unmapped (implying our children are also unmapped) */
2030   g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
2031 }
2032 
2033 /**
2034  * clutter_actor_unrealize:
2035  * @self: A #ClutterActor
2036  *
2037  * Unrealization informs the actor that it may be being destroyed or
2038  * moved to another stage. The actor may want to destroy any
2039  * underlying graphics resources at this point. However it is
2040  * perfectly acceptable for it to retain the resources until the actor
2041  * is destroyed because Clutter only ever uses a single rendering
2042  * context and all of the graphics resources are valid on any stage.
2043  *
2044  * Because mapped actors must be realized, actors may not be
2045  * unrealized if they are mapped. This function hides the actor to be
2046  * sure it isn't mapped, an application-visible side effect that you
2047  * may not be expecting.
2048  *
2049  * This function should not be called by application code.
2050  *
2051  * This function should not really be in the public API, because
2052  * there isn't a good reason to call it. ClutterActor will already
2053  * unrealize things for you when it's important to do so.
2054  *
2055  * If you were using clutter_actor_unrealize() in a dispose
2056  * implementation, then don't, just chain up to ClutterActor's
2057  * dispose.
2058  *
2059  * If you were using clutter_actor_unrealize() to implement
2060  * unrealizing children of your container, then don't, ClutterActor
2061  * will already take care of that.
2062  *
2063  * Deprecated: 1.16: Actors are automatically unrealized, and nothing
2064  *   requires explicit realization.
2065  */
2066 void
clutter_actor_unrealize(ClutterActor * self)2067 clutter_actor_unrealize (ClutterActor *self)
2068 {
2069   g_return_if_fail (CLUTTER_IS_ACTOR (self));
2070   g_return_if_fail (!CLUTTER_ACTOR_IS_MAPPED (self));
2071 
2072   clutter_actor_unrealize_internal (self);
2073 }
2074 
2075 /* If you were using clutter_actor_unrealize() to re-realize to
2076  * create your resources in a different way, then use
2077  * _clutter_actor_rerealize() (inside Clutter) or just call your
2078  * code that recreates your resources directly (outside Clutter).
2079  */
2080 static void
clutter_actor_unrealize_internal(ClutterActor * self)2081 clutter_actor_unrealize_internal (ClutterActor *self)
2082 {
2083 #ifdef CLUTTER_ENABLE_DEBUG
2084   clutter_actor_verify_map_state (self);
2085 #endif
2086 
2087   clutter_actor_hide (self);
2088 
2089   clutter_actor_unrealize_not_hiding (self);
2090 }
2091 
2092 static ClutterActorTraverseVisitFlags
unrealize_actor_before_children_cb(ClutterActor * self,int depth,void * user_data)2093 unrealize_actor_before_children_cb (ClutterActor *self,
2094                                     int depth,
2095                                     void *user_data)
2096 {
2097   /* If an actor is already unrealized we know its children have also
2098    * already been unrealized... */
2099   if (!CLUTTER_ACTOR_IS_REALIZED (self))
2100     return CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN;
2101 
2102   g_signal_emit (self, actor_signals[UNREALIZE], 0);
2103 
2104   return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
2105 }
2106 
2107 static ClutterActorTraverseVisitFlags
unrealize_actor_after_children_cb(ClutterActor * self,int depth,void * user_data)2108 unrealize_actor_after_children_cb (ClutterActor *self,
2109                                    int depth,
2110                                    void *user_data)
2111 {
2112   /* We want to unset the realized flag only _after_
2113    * child actors are unrealized, to maintain invariants.
2114    */
2115   CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
2116   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]);
2117   return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
2118 }
2119 
2120 /*
2121  * clutter_actor_unrealize_not_hiding:
2122  * @self: A #ClutterActor
2123  *
2124  * Unrealization informs the actor that it may be being destroyed or
2125  * moved to another stage. The actor may want to destroy any
2126  * underlying graphics resources at this point. However it is
2127  * perfectly acceptable for it to retain the resources until the actor
2128  * is destroyed because Clutter only ever uses a single rendering
2129  * context and all of the graphics resources are valid on any stage.
2130  *
2131  * Because mapped actors must be realized, actors may not be
2132  * unrealized if they are mapped. You must hide the actor or one of
2133  * its parents before attempting to unrealize.
2134  *
2135  * This function is separate from clutter_actor_unrealize() because it
2136  * does not automatically hide the actor.
2137  * Actors need not be hidden to be unrealized, they just need to
2138  * be unmapped. In fact we don't want to mess up the application's
2139  * setting of the "visible" flag, so hiding is very undesirable.
2140  *
2141  * clutter_actor_unrealize() does a clutter_actor_hide() just for
2142  * backward compatibility.
2143  */
2144 static void
clutter_actor_unrealize_not_hiding(ClutterActor * self)2145 clutter_actor_unrealize_not_hiding (ClutterActor *self)
2146 {
2147   _clutter_actor_traverse (self,
2148                            CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST,
2149                            unrealize_actor_before_children_cb,
2150                            unrealize_actor_after_children_cb,
2151                            NULL);
2152 }
2153 
2154 /*
2155  * _clutter_actor_rerealize:
2156  * @self: A #ClutterActor
2157  * @callback: Function to call while unrealized
2158  * @data: data for callback
2159  *
2160  * If an actor is already unrealized, this just calls the callback.
2161  *
2162  * If it is realized, it unrealizes temporarily, calls the callback,
2163  * and then re-realizes the actor.
2164  *
2165  * As a side effect, leaves all children of the actor unrealized if
2166  * the actor was realized but not showing.  This is because when we
2167  * unrealize the actor temporarily we must unrealize its children
2168  * (e.g. children of a stage can't be realized if stage window is
2169  * gone). And we aren't clever enough to save the realization state of
2170  * all children. In most cases this should not matter, because
2171  * the children will automatically realize when they next become mapped.
2172  */
2173 void
_clutter_actor_rerealize(ClutterActor * self,ClutterCallback callback,void * data)2174 _clutter_actor_rerealize (ClutterActor    *self,
2175                           ClutterCallback  callback,
2176                           void            *data)
2177 {
2178   gboolean was_mapped;
2179   gboolean was_showing;
2180   gboolean was_realized;
2181 
2182   g_return_if_fail (CLUTTER_IS_ACTOR (self));
2183 
2184 #ifdef CLUTTER_ENABLE_DEBUG
2185   clutter_actor_verify_map_state (self);
2186 #endif
2187 
2188   was_realized = CLUTTER_ACTOR_IS_REALIZED (self);
2189   was_mapped = CLUTTER_ACTOR_IS_MAPPED (self);
2190   was_showing = CLUTTER_ACTOR_IS_VISIBLE (self);
2191 
2192   /* Must be unmapped to unrealize. Note we only have to hide this
2193    * actor if it was mapped (if all parents were showing).  If actor
2194    * is merely visible (but not mapped), then that's fine, we can
2195    * leave it visible.
2196    */
2197   if (was_mapped)
2198     clutter_actor_hide (self);
2199 
2200   g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
2201 
2202   /* unrealize self and all children */
2203   clutter_actor_unrealize_not_hiding (self);
2204 
2205   if (callback != NULL)
2206     {
2207       (* callback) (self, data);
2208     }
2209 
2210   if (was_showing)
2211     clutter_actor_show (self); /* will realize only if mapping implies it */
2212   else if (was_realized)
2213     clutter_actor_realize (self); /* realize self and all parents */
2214 }
2215 
2216 static void
clutter_actor_real_pick(ClutterActor * self,const ClutterColor * color)2217 clutter_actor_real_pick (ClutterActor       *self,
2218 			 const ClutterColor *color)
2219 {
2220   /* the default implementation is just to paint a rectangle
2221    * with the same size of the actor using the passed color
2222    */
2223   if (clutter_actor_should_pick_paint (self))
2224     {
2225       ClutterActorBox box = { 0, };
2226       float width, height;
2227 
2228       clutter_actor_get_allocation_box (self, &box);
2229 
2230       width = box.x2 - box.x1;
2231       height = box.y2 - box.y1;
2232 
2233       cogl_set_source_color4ub (color->red,
2234                                 color->green,
2235                                 color->blue,
2236                                 color->alpha);
2237 
2238       cogl_rectangle (0, 0, width, height);
2239     }
2240 
2241   /* XXX - this thoroughly sucks, but we need to maintain compatibility
2242    * with existing container classes that override the pick() virtual
2243    * and chain up to the default implementation - otherwise we'll end up
2244    * painting our children twice.
2245    *
2246    * this has to go away for 2.0; hopefully along the pick() itself.
2247    */
2248   if (CLUTTER_ACTOR_GET_CLASS (self)->pick == clutter_actor_real_pick)
2249     {
2250       ClutterActor *iter;
2251 
2252       for (iter = self->priv->first_child;
2253            iter != NULL;
2254            iter = iter->priv->next_sibling)
2255         clutter_actor_paint (iter);
2256     }
2257 }
2258 
2259 /**
2260  * clutter_actor_should_pick_paint:
2261  * @self: A #ClutterActor
2262  *
2263  * Should be called inside the implementation of the
2264  * #ClutterActor::pick virtual function in order to check whether
2265  * the actor should paint itself in pick mode or not.
2266  *
2267  * This function should never be called directly by applications.
2268  *
2269  * Return value: %TRUE if the actor should paint its silhouette,
2270  *   %FALSE otherwise
2271  */
2272 gboolean
clutter_actor_should_pick_paint(ClutterActor * self)2273 clutter_actor_should_pick_paint (ClutterActor *self)
2274 {
2275   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
2276 
2277   if (CLUTTER_ACTOR_IS_MAPPED (self) &&
2278       (_clutter_context_get_pick_mode () == CLUTTER_PICK_ALL ||
2279        CLUTTER_ACTOR_IS_REACTIVE (self)))
2280     return TRUE;
2281 
2282   return FALSE;
2283 }
2284 
2285 static void
clutter_actor_real_get_preferred_width(ClutterActor * self,gfloat for_height,gfloat * min_width_p,gfloat * natural_width_p)2286 clutter_actor_real_get_preferred_width (ClutterActor *self,
2287                                         gfloat        for_height,
2288                                         gfloat       *min_width_p,
2289                                         gfloat       *natural_width_p)
2290 {
2291   ClutterActorPrivate *priv = self->priv;
2292 
2293   if (priv->n_children != 0 &&
2294       priv->layout_manager != NULL)
2295     {
2296       ClutterContainer *container = CLUTTER_CONTAINER (self);
2297 
2298       CLUTTER_NOTE (LAYOUT, "Querying the layout manager '%s'[%p] "
2299                     "for the preferred width",
2300                     G_OBJECT_TYPE_NAME (priv->layout_manager),
2301                     priv->layout_manager);
2302 
2303       clutter_layout_manager_get_preferred_width (priv->layout_manager,
2304                                                   container,
2305                                                   for_height,
2306                                                   min_width_p,
2307                                                   natural_width_p);
2308 
2309       return;
2310     }
2311 
2312   /* Default implementation is always 0x0, usually an actor
2313    * using this default is relying on someone to set the
2314    * request manually
2315    */
2316   CLUTTER_NOTE (LAYOUT, "Default preferred width: 0, 0");
2317 
2318   if (min_width_p)
2319     *min_width_p = 0;
2320 
2321   if (natural_width_p)
2322     *natural_width_p = 0;
2323 }
2324 
2325 static void
clutter_actor_real_get_preferred_height(ClutterActor * self,gfloat for_width,gfloat * min_height_p,gfloat * natural_height_p)2326 clutter_actor_real_get_preferred_height (ClutterActor *self,
2327                                          gfloat        for_width,
2328                                          gfloat       *min_height_p,
2329                                          gfloat       *natural_height_p)
2330 {
2331   ClutterActorPrivate *priv = self->priv;
2332 
2333   if (priv->n_children != 0 &&
2334       priv->layout_manager != NULL)
2335     {
2336       ClutterContainer *container = CLUTTER_CONTAINER (self);
2337 
2338       CLUTTER_NOTE (LAYOUT, "Querying the layout manager '%s'[%p] "
2339                     "for the preferred height",
2340                     G_OBJECT_TYPE_NAME (priv->layout_manager),
2341                     priv->layout_manager);
2342 
2343       clutter_layout_manager_get_preferred_height (priv->layout_manager,
2344                                                    container,
2345                                                    for_width,
2346                                                    min_height_p,
2347                                                    natural_height_p);
2348 
2349       return;
2350     }
2351   /* Default implementation is always 0x0, usually an actor
2352    * using this default is relying on someone to set the
2353    * request manually
2354    */
2355   CLUTTER_NOTE (LAYOUT, "Default preferred height: 0, 0");
2356 
2357   if (min_height_p)
2358     *min_height_p = 0;
2359 
2360   if (natural_height_p)
2361     *natural_height_p = 0;
2362 }
2363 
2364 static void
clutter_actor_store_old_geometry(ClutterActor * self,ClutterActorBox * box)2365 clutter_actor_store_old_geometry (ClutterActor    *self,
2366                                   ClutterActorBox *box)
2367 {
2368   *box = self->priv->allocation;
2369 }
2370 
2371 static inline void
clutter_actor_notify_if_geometry_changed(ClutterActor * self,const ClutterActorBox * old)2372 clutter_actor_notify_if_geometry_changed (ClutterActor          *self,
2373                                           const ClutterActorBox *old)
2374 {
2375   ClutterActorPrivate *priv = self->priv;
2376   GObject *obj = G_OBJECT (self);
2377 
2378   g_object_freeze_notify (obj);
2379 
2380   /* to avoid excessive requisition or allocation cycles we
2381    * use the cached values.
2382    *
2383    * - if we don't have an allocation we assume that we need
2384    *   to notify anyway
2385    * - if we don't have a width or a height request we notify
2386    *   width and height
2387    * - if we have a valid allocation then we check the old
2388    *   bounding box with the current allocation and we notify
2389    *   the changes
2390    */
2391   if (priv->needs_allocation)
2392     {
2393       g_object_notify_by_pspec (obj, obj_props[PROP_X]);
2394       g_object_notify_by_pspec (obj, obj_props[PROP_Y]);
2395       g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
2396       g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
2397       g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
2398       g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2399     }
2400   else if (priv->needs_width_request || priv->needs_height_request)
2401     {
2402       g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
2403       g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
2404       g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2405     }
2406   else
2407     {
2408       gfloat x, y;
2409       gfloat width, height;
2410 
2411       x = priv->allocation.x1;
2412       y = priv->allocation.y1;
2413       width = priv->allocation.x2 - priv->allocation.x1;
2414       height = priv->allocation.y2 - priv->allocation.y1;
2415 
2416       if (x != old->x1)
2417         {
2418           g_object_notify_by_pspec (obj, obj_props[PROP_X]);
2419           g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
2420         }
2421 
2422       if (y != old->y1)
2423         {
2424           g_object_notify_by_pspec (obj, obj_props[PROP_Y]);
2425           g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]);
2426         }
2427 
2428       if (width != (old->x2 - old->x1))
2429         {
2430           g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
2431           g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2432         }
2433 
2434       if (height != (old->y2 - old->y1))
2435         {
2436           g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
2437           g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]);
2438         }
2439     }
2440 
2441   g_object_thaw_notify (obj);
2442 }
2443 
2444 /*< private >
2445  * clutter_actor_set_allocation_internal:
2446  * @self: a #ClutterActor
2447  * @box: a #ClutterActorBox
2448  * @flags: allocation flags
2449  *
2450  * Stores the allocation of @self.
2451  *
2452  * This function only performs basic storage and property notification.
2453  *
2454  * This function should be called by clutter_actor_set_allocation()
2455  * and by the default implementation of #ClutterActorClass.allocate().
2456  *
2457  * Return value: %TRUE if the allocation of the #ClutterActor has been
2458  *   changed, and %FALSE otherwise
2459  */
2460 static inline gboolean
clutter_actor_set_allocation_internal(ClutterActor * self,const ClutterActorBox * box,ClutterAllocationFlags flags)2461 clutter_actor_set_allocation_internal (ClutterActor           *self,
2462                                        const ClutterActorBox  *box,
2463                                        ClutterAllocationFlags  flags)
2464 {
2465   ClutterActorPrivate *priv = self->priv;
2466   GObject *obj;
2467   gboolean x1_changed, y1_changed, x2_changed, y2_changed;
2468   gboolean retval;
2469   ClutterActorBox old_alloc = { 0, };
2470 
2471   obj = G_OBJECT (self);
2472 
2473   g_object_freeze_notify (obj);
2474 
2475   clutter_actor_store_old_geometry (self, &old_alloc);
2476 
2477   x1_changed = priv->allocation.x1 != box->x1;
2478   y1_changed = priv->allocation.y1 != box->y1;
2479   x2_changed = priv->allocation.x2 != box->x2;
2480   y2_changed = priv->allocation.y2 != box->y2;
2481 
2482   priv->allocation = *box;
2483   priv->allocation_flags = flags;
2484 
2485   /* allocation is authoritative */
2486   priv->needs_width_request = FALSE;
2487   priv->needs_height_request = FALSE;
2488   priv->needs_allocation = FALSE;
2489 
2490   if (x1_changed ||
2491       y1_changed ||
2492       x2_changed ||
2493       y2_changed)
2494     {
2495       CLUTTER_NOTE (LAYOUT, "Allocation for '%s' changed",
2496                     _clutter_actor_get_debug_name (self));
2497 
2498       priv->transform_valid = FALSE;
2499 
2500       g_object_notify_by_pspec (obj, obj_props[PROP_ALLOCATION]);
2501 
2502       /* if the allocation changes, so does the content box */
2503       if (priv->content != NULL)
2504         {
2505           priv->content_box_valid = FALSE;
2506           g_object_notify_by_pspec (obj, obj_props[PROP_CONTENT_BOX]);
2507         }
2508 
2509       retval = TRUE;
2510     }
2511   else
2512     retval = FALSE;
2513 
2514   clutter_actor_notify_if_geometry_changed (self, &old_alloc);
2515 
2516   g_object_thaw_notify (obj);
2517 
2518   return retval;
2519 }
2520 
2521 static void clutter_actor_real_allocate (ClutterActor           *self,
2522                                          const ClutterActorBox  *box,
2523                                          ClutterAllocationFlags  flags);
2524 
2525 static inline void
clutter_actor_maybe_layout_children(ClutterActor * self,const ClutterActorBox * allocation,ClutterAllocationFlags flags)2526 clutter_actor_maybe_layout_children (ClutterActor           *self,
2527                                      const ClutterActorBox  *allocation,
2528                                      ClutterAllocationFlags  flags)
2529 {
2530   ClutterActorPrivate *priv = self->priv;
2531 
2532   /* this is going to be a bit hard to follow, so let's put an explanation
2533    * here.
2534    *
2535    * we want ClutterActor to have a default layout manager if the actor was
2536    * created using "g_object_new (CLUTTER_TYPE_ACTOR, NULL)".
2537    *
2538    * we also want any subclass of ClutterActor that does not override the
2539    * ::allocate() virtual function to delegate to a layout manager.
2540    *
2541    * finally, we want to allow people subclassing ClutterActor and overriding
2542    * the ::allocate() vfunc to let Clutter delegate to the layout manager.
2543    *
2544    * on the other hand, we want existing actor subclasses overriding the
2545    * ::allocate() virtual function and chaining up to the parent's
2546    * implementation to continue working without allocating their children
2547    * twice, or without entering an allocation loop.
2548    *
2549    * for the first two points, we check if the class of the actor is
2550    * overridding the ::allocate() virtual function; if it isn't, then we
2551    * follow through with checking whether we have children and a layout
2552    * manager, and eventually calling clutter_layout_manager_allocate().
2553    *
2554    * for the third point, we check the CLUTTER_DELEGATE_LAYOUT flag in the
2555    * allocation flags that we got passed, and if it is present, we continue
2556    * with the check above.
2557    *
2558    * if neither of these two checks yields a positive result, we just
2559    * assume that the ::allocate() virtual function that resulted in this
2560    * function being called will also allocate the children of the actor.
2561    */
2562 
2563   if (CLUTTER_ACTOR_GET_CLASS (self)->allocate == clutter_actor_real_allocate)
2564     goto check_layout;
2565 
2566   if ((flags & CLUTTER_DELEGATE_LAYOUT) != 0)
2567     goto check_layout;
2568 
2569   return;
2570 
2571 check_layout:
2572   if (priv->n_children != 0 &&
2573       priv->layout_manager != NULL)
2574     {
2575       ClutterContainer *container = CLUTTER_CONTAINER (self);
2576       ClutterAllocationFlags children_flags;
2577       ClutterActorBox children_box;
2578 
2579       /* normalize the box passed to the layout manager */
2580       children_box.x1 = children_box.y1 = 0.f;
2581       children_box.x2 = (allocation->x2 - allocation->x1);
2582       children_box.y2 = (allocation->y2 - allocation->y1);
2583 
2584       /* remove the DELEGATE_LAYOUT flag; this won't be passed to
2585        * the actor's children, since it refers only to the current
2586        * actor's allocation.
2587        */
2588       children_flags = flags;
2589       children_flags &= ~CLUTTER_DELEGATE_LAYOUT;
2590 
2591       CLUTTER_NOTE (LAYOUT,
2592                     "Allocating %d children of %s "
2593                     "at { %.2f, %.2f - %.2f x %.2f } "
2594                     "using %s",
2595                     priv->n_children,
2596                     _clutter_actor_get_debug_name (self),
2597                     allocation->x1,
2598                     allocation->y1,
2599                     (allocation->x2 - allocation->x1),
2600                     (allocation->y2 - allocation->y1),
2601                     G_OBJECT_TYPE_NAME (priv->layout_manager));
2602 
2603       clutter_layout_manager_allocate (priv->layout_manager,
2604                                        container,
2605                                        &children_box,
2606                                        children_flags);
2607     }
2608 }
2609 
2610 static void
clutter_actor_real_allocate(ClutterActor * self,const ClutterActorBox * box,ClutterAllocationFlags flags)2611 clutter_actor_real_allocate (ClutterActor           *self,
2612                              const ClutterActorBox  *box,
2613                              ClutterAllocationFlags  flags)
2614 {
2615   ClutterActorPrivate *priv = self->priv;
2616   gboolean changed;
2617 
2618   g_object_freeze_notify (G_OBJECT (self));
2619 
2620   changed = clutter_actor_set_allocation_internal (self, box, flags);
2621 
2622   /* we allocate our children before we notify changes in our geometry,
2623    * so that people connecting to properties will be able to get valid
2624    * data out of the sub-tree of the scene graph that has this actor at
2625    * the root.
2626    */
2627   clutter_actor_maybe_layout_children (self, box, flags);
2628 
2629   if (changed)
2630     {
2631       ClutterActorBox signal_box = priv->allocation;
2632       ClutterAllocationFlags signal_flags = priv->allocation_flags;
2633 
2634       g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0,
2635                      &signal_box,
2636                      signal_flags);
2637     }
2638 
2639   g_object_thaw_notify (G_OBJECT (self));
2640 }
2641 
2642 static void
_clutter_actor_signal_queue_redraw(ClutterActor * self,ClutterActor * origin)2643 _clutter_actor_signal_queue_redraw (ClutterActor *self,
2644                                     ClutterActor *origin)
2645 {
2646   /* no point in queuing a redraw on a destroyed actor */
2647   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
2648     return;
2649 
2650   /* NB: We can't bail out early here if the actor is hidden in case
2651    * the actor bas been cloned. In this case the clone will need to
2652    * receive the signal so it can queue its own redraw.
2653    */
2654 
2655   _clutter_actor_queue_redraw_on_clones (self);
2656 
2657   /* calls klass->queue_redraw in default handler */
2658   g_signal_emit (self, actor_signals[QUEUE_REDRAW], 0, origin);
2659 }
2660 
2661 static void
clutter_actor_real_queue_redraw(ClutterActor * self,ClutterActor * origin)2662 clutter_actor_real_queue_redraw (ClutterActor *self,
2663                                  ClutterActor *origin)
2664 {
2665   ClutterActor *parent;
2666 
2667   CLUTTER_NOTE (PAINT, "Redraw queued on '%s' (from: '%s')",
2668                 _clutter_actor_get_debug_name (self),
2669                 origin != NULL ? _clutter_actor_get_debug_name (origin)
2670                                : "same actor");
2671 
2672   /* no point in queuing a redraw on a destroyed actor */
2673   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
2674     return;
2675 
2676   /* If the queue redraw is coming from a child then the actor has
2677      become dirty and any queued effect is no longer valid */
2678   if (self != origin)
2679     {
2680       self->priv->is_dirty = TRUE;
2681       self->priv->effect_to_redraw = NULL;
2682     }
2683 
2684   /* If the actor isn't visible, we still had to emit the signal
2685    * to allow for a ClutterClone, but the appearance of the parent
2686    * won't change so we don't have to propagate up the hierarchy.
2687    */
2688   if (!CLUTTER_ACTOR_IS_VISIBLE (self))
2689     return;
2690 
2691   /* Although we could determine here that a full stage redraw
2692    * has already been queued and immediately bail out, we actually
2693    * guarantee that we will propagate a queue-redraw signal to our
2694    * parent at least once so that it's possible to implement a
2695    * container that tracks which of its children have queued a
2696    * redraw.
2697    */
2698   if (self->priv->propagated_one_redraw)
2699     {
2700       ClutterActor *stage = _clutter_actor_get_stage_internal (self);
2701       if (stage != NULL &&
2702           _clutter_stage_has_full_redraw_queued (CLUTTER_STAGE (stage)))
2703         return;
2704     }
2705 
2706   self->priv->propagated_one_redraw = TRUE;
2707 
2708   /* notify parents, if they are all visible eventually we'll
2709    * queue redraw on the stage, which queues the redraw idle.
2710    */
2711   parent = clutter_actor_get_parent (self);
2712   if (parent != NULL)
2713     {
2714       /* this will go up recursively */
2715       _clutter_actor_signal_queue_redraw (parent, origin);
2716     }
2717 }
2718 
2719 static void
clutter_actor_real_queue_relayout(ClutterActor * self)2720 clutter_actor_real_queue_relayout (ClutterActor *self)
2721 {
2722   ClutterActorPrivate *priv = self->priv;
2723 
2724   /* no point in queueing a redraw on a destroyed actor */
2725   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
2726     return;
2727 
2728   priv->needs_width_request  = TRUE;
2729   priv->needs_height_request = TRUE;
2730   priv->needs_allocation     = TRUE;
2731 
2732   /* reset the cached size requests */
2733   memset (priv->width_requests, 0,
2734           N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest));
2735   memset (priv->height_requests, 0,
2736           N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest));
2737 
2738   /* We need to go all the way up the hierarchy */
2739   if (priv->parent != NULL)
2740     _clutter_actor_queue_only_relayout (priv->parent);
2741 }
2742 
2743 /**
2744  * clutter_actor_apply_relative_transform_to_point:
2745  * @self: A #ClutterActor
2746  * @ancestor: (allow-none): A #ClutterActor ancestor, or %NULL to use the
2747  *   default #ClutterStage
2748  * @point: A point as #ClutterVertex
2749  * @vertex: (out caller-allocates): The translated #ClutterVertex
2750  *
2751  * Transforms @point in coordinates relative to the actor into
2752  * ancestor-relative coordinates using the relevant transform
2753  * stack (i.e. scale, rotation, etc).
2754  *
2755  * If @ancestor is %NULL the ancestor will be the #ClutterStage. In
2756  * this case, the coordinates returned will be the coordinates on
2757  * the stage before the projection is applied. This is different from
2758  * the behaviour of clutter_actor_apply_transform_to_point().
2759  *
2760  * Since: 0.6
2761  */
2762 void
clutter_actor_apply_relative_transform_to_point(ClutterActor * self,ClutterActor * ancestor,const ClutterVertex * point,ClutterVertex * vertex)2763 clutter_actor_apply_relative_transform_to_point (ClutterActor        *self,
2764 						 ClutterActor        *ancestor,
2765 						 const ClutterVertex *point,
2766 						 ClutterVertex       *vertex)
2767 {
2768   gfloat w;
2769   CoglMatrix matrix;
2770 
2771   g_return_if_fail (CLUTTER_IS_ACTOR (self));
2772   g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor));
2773   g_return_if_fail (point != NULL);
2774   g_return_if_fail (vertex != NULL);
2775 
2776   *vertex = *point;
2777   w = 1.0;
2778 
2779   if (ancestor == NULL)
2780     ancestor = _clutter_actor_get_stage_internal (self);
2781 
2782   if (ancestor == NULL)
2783     {
2784       *vertex = *point;
2785       return;
2786     }
2787 
2788   _clutter_actor_get_relative_transformation_matrix (self, ancestor, &matrix);
2789   cogl_matrix_transform_point (&matrix, &vertex->x, &vertex->y, &vertex->z, &w);
2790 }
2791 
2792 static gboolean
_clutter_actor_fully_transform_vertices(ClutterActor * self,const ClutterVertex * vertices_in,ClutterVertex * vertices_out,int n_vertices)2793 _clutter_actor_fully_transform_vertices (ClutterActor *self,
2794                                          const ClutterVertex *vertices_in,
2795                                          ClutterVertex *vertices_out,
2796                                          int n_vertices)
2797 {
2798   ClutterActor *stage;
2799   CoglMatrix modelview;
2800   CoglMatrix projection;
2801   float viewport[4];
2802 
2803   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
2804 
2805   stage = _clutter_actor_get_stage_internal (self);
2806 
2807   /* We really can't do anything meaningful in this case so don't try
2808    * to do any transform */
2809   if (stage == NULL)
2810     return FALSE;
2811 
2812   /* Note: we pass NULL as the ancestor because we don't just want the modelview
2813    * that gets us to stage coordinates, we want to go all the way to eye
2814    * coordinates */
2815   _clutter_actor_apply_relative_transformation_matrix (self, NULL, &modelview);
2816 
2817   /* Fetch the projection and viewport */
2818   _clutter_stage_get_projection_matrix (CLUTTER_STAGE (stage), &projection);
2819   _clutter_stage_get_viewport (CLUTTER_STAGE (stage),
2820                                &viewport[0],
2821                                &viewport[1],
2822                                &viewport[2],
2823                                &viewport[3]);
2824 
2825   _clutter_util_fully_transform_vertices (&modelview,
2826                                           &projection,
2827                                           viewport,
2828                                           vertices_in,
2829                                           vertices_out,
2830                                           n_vertices);
2831 
2832   return TRUE;
2833 }
2834 
2835 /**
2836  * clutter_actor_apply_transform_to_point:
2837  * @self: A #ClutterActor
2838  * @point: A point as #ClutterVertex
2839  * @vertex: (out caller-allocates): The translated #ClutterVertex
2840  *
2841  * Transforms @point in coordinates relative to the actor
2842  * into screen-relative coordinates with the current actor
2843  * transformation (i.e. scale, rotation, etc)
2844  *
2845  * Since: 0.4
2846  **/
2847 void
clutter_actor_apply_transform_to_point(ClutterActor * self,const ClutterVertex * point,ClutterVertex * vertex)2848 clutter_actor_apply_transform_to_point (ClutterActor        *self,
2849                                         const ClutterVertex *point,
2850                                         ClutterVertex       *vertex)
2851 {
2852   g_return_if_fail (point != NULL);
2853   g_return_if_fail (vertex != NULL);
2854   _clutter_actor_fully_transform_vertices (self, point, vertex, 1);
2855 }
2856 
2857 /*
2858  * _clutter_actor_get_relative_transformation_matrix:
2859  * @self: The actor whose coordinate space you want to transform from.
2860  * @ancestor: The ancestor actor whose coordinate space you want to transform too
2861  *            or %NULL if you want to transform all the way to eye coordinates.
2862  * @matrix: A #CoglMatrix to store the transformation
2863  *
2864  * This gets a transformation @matrix that will transform coordinates from the
2865  * coordinate space of @self into the coordinate space of @ancestor.
2866  *
2867  * For example if you need a matrix that can transform the local actor
2868  * coordinates of @self into stage coordinates you would pass the actor's stage
2869  * pointer as the @ancestor.
2870  *
2871  * If you pass %NULL then the transformation will take you all the way through
2872  * to eye coordinates. This can be useful if you want to extract the entire
2873  * modelview transform that Clutter applies before applying the projection
2874  * transformation. If you want to explicitly set a modelview on a CoglFramebuffer
2875  * using cogl_set_modelview_matrix() for example then you would want a matrix
2876  * that transforms into eye coordinates.
2877  *
2878  * Note: This function explicitly initializes the given @matrix. If you just
2879  * want clutter to multiply a relative transformation with an existing matrix
2880  * you can use clutter_actor_apply_relative_transformation_matrix()
2881  * instead.
2882  *
2883  */
2884 /* XXX: We should consider caching the stage relative modelview along with
2885  * the actor itself */
2886 static void
_clutter_actor_get_relative_transformation_matrix(ClutterActor * self,ClutterActor * ancestor,CoglMatrix * matrix)2887 _clutter_actor_get_relative_transformation_matrix (ClutterActor *self,
2888                                                    ClutterActor *ancestor,
2889                                                    CoglMatrix *matrix)
2890 {
2891   cogl_matrix_init_identity (matrix);
2892 
2893   _clutter_actor_apply_relative_transformation_matrix (self, ancestor, matrix);
2894 }
2895 
2896 /* Project the given @box into stage window coordinates, writing the
2897  * transformed vertices to @verts[]. */
2898 static gboolean
_clutter_actor_transform_and_project_box(ClutterActor * self,const ClutterActorBox * box,ClutterVertex verts[])2899 _clutter_actor_transform_and_project_box (ClutterActor          *self,
2900 					  const ClutterActorBox *box,
2901 					  ClutterVertex          verts[])
2902 {
2903   ClutterVertex box_vertices[4];
2904 
2905   box_vertices[0].x = box->x1;
2906   box_vertices[0].y = box->y1;
2907   box_vertices[0].z = 0;
2908   box_vertices[1].x = box->x2;
2909   box_vertices[1].y = box->y1;
2910   box_vertices[1].z = 0;
2911   box_vertices[2].x = box->x1;
2912   box_vertices[2].y = box->y2;
2913   box_vertices[2].z = 0;
2914   box_vertices[3].x = box->x2;
2915   box_vertices[3].y = box->y2;
2916   box_vertices[3].z = 0;
2917 
2918   return
2919     _clutter_actor_fully_transform_vertices (self, box_vertices, verts, 4);
2920 }
2921 
2922 /**
2923  * clutter_actor_get_allocation_vertices:
2924  * @self: A #ClutterActor
2925  * @ancestor: (allow-none): A #ClutterActor to calculate the vertices
2926  *   against, or %NULL to use the #ClutterStage
2927  * @verts: (out) (array fixed-size=4) (element-type Clutter.Vertex): return
2928  *   location for an array of 4 #ClutterVertex in which to store the result
2929  *
2930  * Calculates the transformed coordinates of the four corners of the
2931  * actor in the plane of @ancestor. The returned vertices relate to
2932  * the #ClutterActorBox coordinates as follows:
2933  *
2934  *  - @verts[0] contains (x1, y1)
2935  *  - @verts[1] contains (x2, y1)
2936  *  - @verts[2] contains (x1, y2)
2937  *  - @verts[3] contains (x2, y2)
2938  *
2939  * If @ancestor is %NULL the ancestor will be the #ClutterStage. In
2940  * this case, the coordinates returned will be the coordinates on
2941  * the stage before the projection is applied. This is different from
2942  * the behaviour of clutter_actor_get_abs_allocation_vertices().
2943  *
2944  * Since: 0.6
2945  */
2946 void
clutter_actor_get_allocation_vertices(ClutterActor * self,ClutterActor * ancestor,ClutterVertex verts[])2947 clutter_actor_get_allocation_vertices (ClutterActor  *self,
2948                                        ClutterActor  *ancestor,
2949                                        ClutterVertex  verts[])
2950 {
2951   ClutterActorPrivate *priv;
2952   ClutterActorBox box;
2953   ClutterVertex vertices[4];
2954   CoglMatrix modelview;
2955 
2956   g_return_if_fail (CLUTTER_IS_ACTOR (self));
2957   g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor));
2958 
2959   if (ancestor == NULL)
2960     ancestor = _clutter_actor_get_stage_internal (self);
2961 
2962   /* Fallback to a NOP transform if the actor isn't parented under a
2963    * stage. */
2964   if (ancestor == NULL)
2965     ancestor = self;
2966 
2967   priv = self->priv;
2968 
2969   /* if the actor needs to be allocated we force a relayout, so that
2970    * we will have valid values to use in the transformations */
2971   if (priv->needs_allocation)
2972     {
2973       ClutterActor *stage = _clutter_actor_get_stage_internal (self);
2974       if (stage)
2975         _clutter_stage_maybe_relayout (stage);
2976       else
2977         {
2978           box.x1 = box.y1 = 0;
2979           /* The result isn't really meaningful in this case but at
2980            * least try to do something *vaguely* reasonable... */
2981           clutter_actor_get_size (self, &box.x2, &box.y2);
2982         }
2983     }
2984 
2985   clutter_actor_get_allocation_box (self, &box);
2986 
2987   vertices[0].x = box.x1;
2988   vertices[0].y = box.y1;
2989   vertices[0].z = 0;
2990   vertices[1].x = box.x2;
2991   vertices[1].y = box.y1;
2992   vertices[1].z = 0;
2993   vertices[2].x = box.x1;
2994   vertices[2].y = box.y2;
2995   vertices[2].z = 0;
2996   vertices[3].x = box.x2;
2997   vertices[3].y = box.y2;
2998   vertices[3].z = 0;
2999 
3000   _clutter_actor_get_relative_transformation_matrix (self, ancestor,
3001                                                      &modelview);
3002 
3003   cogl_matrix_transform_points (&modelview,
3004                                 3,
3005                                 sizeof (ClutterVertex),
3006                                 vertices,
3007                                 sizeof (ClutterVertex),
3008                                 vertices,
3009                                 4);
3010 }
3011 
3012 /**
3013  * clutter_actor_get_abs_allocation_vertices:
3014  * @self: A #ClutterActor
3015  * @verts: (out) (array fixed-size=4): Pointer to a location of an array
3016  *   of 4 #ClutterVertex where to store the result.
3017  *
3018  * Calculates the transformed screen coordinates of the four corners of
3019  * the actor; the returned vertices relate to the #ClutterActorBox
3020  * coordinates  as follows:
3021  *
3022  *  - v[0] contains (x1, y1)
3023  *  - v[1] contains (x2, y1)
3024  *  - v[2] contains (x1, y2)
3025  *  - v[3] contains (x2, y2)
3026  *
3027  * Since: 0.4
3028  */
3029 void
clutter_actor_get_abs_allocation_vertices(ClutterActor * self,ClutterVertex verts[])3030 clutter_actor_get_abs_allocation_vertices (ClutterActor  *self,
3031                                            ClutterVertex  verts[])
3032 {
3033   ClutterActorPrivate *priv;
3034   ClutterActorBox actor_space_allocation;
3035 
3036   g_return_if_fail (CLUTTER_IS_ACTOR (self));
3037 
3038   priv = self->priv;
3039 
3040   /* if the actor needs to be allocated we force a relayout, so that
3041    * the actor allocation box will be valid for
3042    * _clutter_actor_transform_and_project_box()
3043    */
3044   if (priv->needs_allocation)
3045     {
3046       ClutterActor *stage = _clutter_actor_get_stage_internal (self);
3047       /* There's nothing meaningful we can do now */
3048       if (!stage)
3049         return;
3050 
3051       _clutter_stage_maybe_relayout (stage);
3052     }
3053 
3054   /* NB: _clutter_actor_transform_and_project_box expects a box in the actor's
3055    * own coordinate space... */
3056   actor_space_allocation.x1 = 0;
3057   actor_space_allocation.y1 = 0;
3058   actor_space_allocation.x2 = priv->allocation.x2 - priv->allocation.x1;
3059   actor_space_allocation.y2 = priv->allocation.y2 - priv->allocation.y1;
3060   _clutter_actor_transform_and_project_box (self,
3061 					    &actor_space_allocation,
3062 					    verts);
3063 }
3064 
3065 static void
clutter_actor_real_apply_transform(ClutterActor * self,ClutterMatrix * matrix)3066 clutter_actor_real_apply_transform (ClutterActor  *self,
3067                                     ClutterMatrix *matrix)
3068 {
3069   ClutterActorPrivate *priv = self->priv;
3070   CoglMatrix *transform = &priv->transform;
3071   const ClutterTransformInfo *info;
3072   float pivot_x = 0.f, pivot_y = 0.f;
3073 
3074   /* we already have a cached transformation */
3075   if (priv->transform_valid)
3076     goto multiply_and_return;
3077 
3078   info = _clutter_actor_get_transform_info_or_defaults (self);
3079 
3080   /* compute the pivot point given the allocated size */
3081   pivot_x = (priv->allocation.x2 - priv->allocation.x1)
3082           * info->pivot.x;
3083   pivot_y = (priv->allocation.y2 - priv->allocation.y1)
3084           * info->pivot.y;
3085 
3086   CLUTTER_NOTE (PAINT,
3087                 "Allocation: (%.2f, %2.f), "
3088                 "pivot: (%.2f, %.2f), "
3089                 "translation: (%.2f, %.2f) -> "
3090                 "new origin: (%.2f, %.2f)",
3091                 priv->allocation.x1, priv->allocation.y1,
3092                 info->pivot.x, info->pivot.y,
3093                 info->translation.x, info->translation.y,
3094                 priv->allocation.x1 + pivot_x + info->translation.x,
3095                 priv->allocation.y1 + pivot_y + info->translation.y);
3096 
3097   /* we apply the :child-transform from the parent actor, if we have one */
3098   if (priv->parent != NULL)
3099     {
3100       const ClutterTransformInfo *parent_info;
3101 
3102       parent_info = _clutter_actor_get_transform_info_or_defaults (priv->parent);
3103       clutter_matrix_init_from_matrix (transform, &(parent_info->child_transform));
3104     }
3105   else
3106     clutter_matrix_init_identity (transform);
3107 
3108   /* if we have an overriding transformation, we use that, and get out */
3109   if (info->transform_set)
3110     {
3111       /* we still need to apply the :allocation's origin and :pivot-point
3112        * translations, since :transform is relative to the actor's coordinate
3113        * space, and to the pivot point
3114        */
3115       cogl_matrix_translate (transform,
3116                              priv->allocation.x1 + pivot_x,
3117                              priv->allocation.y1 + pivot_y,
3118                              info->pivot_z);
3119       cogl_matrix_multiply (transform, transform, &info->transform);
3120       goto roll_back_pivot;
3121     }
3122 
3123   /* basic translation: :allocation's origin and :z-position; instead
3124    * of decomposing the pivot and translation info separate operations,
3125    * we just compose everything into a single translation
3126    */
3127   cogl_matrix_translate (transform,
3128                          priv->allocation.x1 + pivot_x + info->translation.x,
3129                          priv->allocation.y1 + pivot_y + info->translation.y,
3130                          info->z_position + info->pivot_z + info->translation.z);
3131 
3132   /* because the rotation involves translations, we must scale
3133    * before applying the rotations (if we apply the scale after
3134    * the rotations, the translations included in the rotation are
3135    * not scaled and so the entire object will move on the screen
3136    * as a result of rotating it).
3137    *
3138    * XXX:2.0 the comment has to be reworded once we remove the
3139    * per-transformation centers; we also may want to apply rotation
3140    * first and scaling after, to match the matrix decomposition
3141    * code we use when interpolating transformations
3142    */
3143   if (info->scale_x != 1.0 || info->scale_y != 1.0 || info->scale_z != 1.0)
3144     {
3145       /* XXX:2.0 remove anchor coord */
3146       TRANSFORM_ABOUT_ANCHOR_COORD (self, transform,
3147                                     &info->scale_center,
3148                                     cogl_matrix_scale (transform,
3149                                                        info->scale_x,
3150                                                        info->scale_y,
3151                                                        info->scale_z));
3152     }
3153 
3154   if (info->rz_angle)
3155     {
3156       /* XXX:2.0 remove anchor coord */
3157       TRANSFORM_ABOUT_ANCHOR_COORD (self, transform,
3158                                     &info->rz_center,
3159                                     cogl_matrix_rotate (transform,
3160                                                         info->rz_angle,
3161                                                         0, 0, 1.0));
3162     }
3163 
3164   if (info->ry_angle)
3165     {
3166       /* XXX:2.0 remove anchor coord */
3167       TRANSFORM_ABOUT_ANCHOR_COORD (self, transform,
3168                                     &info->ry_center,
3169                                     cogl_matrix_rotate (transform,
3170                                                         info->ry_angle,
3171                                                         0, 1.0, 0));
3172     }
3173 
3174   if (info->rx_angle)
3175     {
3176       /* XXX:2.0 remove anchor coord */
3177       TRANSFORM_ABOUT_ANCHOR_COORD (self, transform,
3178                                     &info->rx_center,
3179                                     cogl_matrix_rotate (transform,
3180                                                         info->rx_angle,
3181                                                         1.0, 0, 0));
3182     }
3183 
3184   /* XXX:2.0 remove anchor point translation */
3185   if (!clutter_anchor_coord_is_zero (&info->anchor))
3186     {
3187       gfloat x, y, z;
3188 
3189       clutter_anchor_coord_get_units (self, &info->anchor, &x, &y, &z);
3190       cogl_matrix_translate (transform, -x, -y, -z);
3191     }
3192 
3193 roll_back_pivot:
3194   /* roll back the pivot translation */
3195   if (pivot_x != 0.f || pivot_y != 0.f || info->pivot_z != 0.f)
3196     cogl_matrix_translate (transform, -pivot_x, -pivot_y, -info->pivot_z);
3197 
3198   /* we have a valid modelview */
3199   priv->transform_valid = TRUE;
3200 
3201 multiply_and_return:
3202   cogl_matrix_multiply (matrix, matrix, &priv->transform);
3203 }
3204 
3205 /* Applies the transforms associated with this actor to the given
3206  * matrix. */
3207 void
_clutter_actor_apply_modelview_transform(ClutterActor * self,ClutterMatrix * matrix)3208 _clutter_actor_apply_modelview_transform (ClutterActor  *self,
3209                                           ClutterMatrix *matrix)
3210 {
3211   CLUTTER_ACTOR_GET_CLASS (self)->apply_transform (self, matrix);
3212 }
3213 
3214 /*
3215  * clutter_actor_apply_relative_transformation_matrix:
3216  * @self: The actor whose coordinate space you want to transform from.
3217  * @ancestor: The ancestor actor whose coordinate space you want to transform too
3218  *            or %NULL if you want to transform all the way to eye coordinates.
3219  * @matrix: A #ClutterMatrix to apply the transformation too.
3220  *
3221  * This multiplies a transform with @matrix that will transform coordinates
3222  * from the coordinate space of @self into the coordinate space of @ancestor.
3223  *
3224  * For example if you need a matrix that can transform the local actor
3225  * coordinates of @self into stage coordinates you would pass the actor's stage
3226  * pointer as the @ancestor.
3227  *
3228  * If you pass %NULL then the transformation will take you all the way through
3229  * to eye coordinates. This can be useful if you want to extract the entire
3230  * modelview transform that Clutter applies before applying the projection
3231  * transformation. If you want to explicitly set a modelview on a CoglFramebuffer
3232  * using cogl_set_modelview_matrix() for example then you would want a matrix
3233  * that transforms into eye coordinates.
3234  *
3235  * This function doesn't initialize the given @matrix, it simply
3236  * multiplies the requested transformation matrix with the existing contents of
3237  * @matrix. You can use cogl_matrix_init_identity() to initialize the @matrix
3238  * before calling this function, or you can use
3239  * clutter_actor_get_relative_transformation_matrix() instead.
3240  */
3241 void
_clutter_actor_apply_relative_transformation_matrix(ClutterActor * self,ClutterActor * ancestor,CoglMatrix * matrix)3242 _clutter_actor_apply_relative_transformation_matrix (ClutterActor *self,
3243                                                      ClutterActor *ancestor,
3244                                                      CoglMatrix *matrix)
3245 {
3246   ClutterActor *parent;
3247 
3248   /* Note we terminate before ever calling stage->apply_transform()
3249    * since that would conceptually be relative to the underlying
3250    * window OpenGL coordinates so we'd need a special @ancestor
3251    * value to represent the fake parent of the stage. */
3252   if (self == ancestor)
3253     return;
3254 
3255   parent = clutter_actor_get_parent (self);
3256 
3257   if (parent != NULL)
3258     _clutter_actor_apply_relative_transformation_matrix (parent, ancestor,
3259                                                          matrix);
3260 
3261   _clutter_actor_apply_modelview_transform (self, matrix);
3262 }
3263 
3264 static void
_clutter_actor_draw_paint_volume_full(ClutterActor * self,ClutterPaintVolume * pv,const char * label,const CoglColor * color)3265 _clutter_actor_draw_paint_volume_full (ClutterActor *self,
3266                                        ClutterPaintVolume *pv,
3267                                        const char *label,
3268                                        const CoglColor *color)
3269 {
3270   static CoglPipeline *outline = NULL;
3271   CoglPrimitive *prim;
3272   ClutterVertex line_ends[12 * 2];
3273   int n_vertices;
3274   CoglContext *ctx =
3275     clutter_backend_get_cogl_context (clutter_get_default_backend ());
3276   /* XXX: at some point we'll query this from the stage but we can't
3277    * do that until the osx backend uses Cogl natively. */
3278   CoglFramebuffer *fb = cogl_get_draw_framebuffer ();
3279 
3280   if (outline == NULL)
3281     outline = cogl_pipeline_new (ctx);
3282 
3283   _clutter_paint_volume_complete (pv);
3284 
3285   n_vertices = pv->is_2d ? 4 * 2 : 12 * 2;
3286 
3287   /* Front face */
3288   line_ends[0] = pv->vertices[0]; line_ends[1] = pv->vertices[1];
3289   line_ends[2] = pv->vertices[1]; line_ends[3] = pv->vertices[2];
3290   line_ends[4] = pv->vertices[2]; line_ends[5] = pv->vertices[3];
3291   line_ends[6] = pv->vertices[3]; line_ends[7] = pv->vertices[0];
3292 
3293   if (!pv->is_2d)
3294     {
3295       /* Back face */
3296       line_ends[8] = pv->vertices[4]; line_ends[9] = pv->vertices[5];
3297       line_ends[10] = pv->vertices[5]; line_ends[11] = pv->vertices[6];
3298       line_ends[12] = pv->vertices[6]; line_ends[13] = pv->vertices[7];
3299       line_ends[14] = pv->vertices[7]; line_ends[15] = pv->vertices[4];
3300 
3301       /* Lines connecting front face to back face */
3302       line_ends[16] = pv->vertices[0]; line_ends[17] = pv->vertices[4];
3303       line_ends[18] = pv->vertices[1]; line_ends[19] = pv->vertices[5];
3304       line_ends[20] = pv->vertices[2]; line_ends[21] = pv->vertices[6];
3305       line_ends[22] = pv->vertices[3]; line_ends[23] = pv->vertices[7];
3306     }
3307 
3308   prim = cogl_primitive_new_p3 (ctx, COGL_VERTICES_MODE_LINES,
3309                                 n_vertices,
3310                                 (CoglVertexP3 *)line_ends);
3311 
3312   cogl_pipeline_set_color (outline, color);
3313   cogl_framebuffer_draw_primitive (fb, outline, prim);
3314   cogl_object_unref (prim);
3315 
3316   if (label)
3317     {
3318       PangoLayout *layout;
3319       layout = pango_layout_new (clutter_actor_get_pango_context (self));
3320       pango_layout_set_text (layout, label, -1);
3321       cogl_pango_render_layout (layout,
3322                                 pv->vertices[0].x,
3323                                 pv->vertices[0].y,
3324                                 color,
3325                                 0);
3326       g_object_unref (layout);
3327     }
3328 }
3329 
3330 static void
_clutter_actor_draw_paint_volume(ClutterActor * self)3331 _clutter_actor_draw_paint_volume (ClutterActor *self)
3332 {
3333   ClutterPaintVolume *pv;
3334   CoglColor color;
3335 
3336   pv = _clutter_actor_get_paint_volume_mutable (self);
3337   if (!pv)
3338     {
3339       gfloat width, height;
3340       ClutterPaintVolume fake_pv;
3341 
3342       ClutterActor *stage = _clutter_actor_get_stage_internal (self);
3343       _clutter_paint_volume_init_static (&fake_pv, stage);
3344 
3345       clutter_actor_get_size (self, &width, &height);
3346       clutter_paint_volume_set_width (&fake_pv, width);
3347       clutter_paint_volume_set_height (&fake_pv, height);
3348 
3349       cogl_color_init_from_4f (&color, 0, 0, 1, 1);
3350       _clutter_actor_draw_paint_volume_full (self, &fake_pv,
3351                                              _clutter_actor_get_debug_name (self),
3352                                              &color);
3353 
3354       clutter_paint_volume_free (&fake_pv);
3355     }
3356   else
3357     {
3358       cogl_color_init_from_4f (&color, 0, 1, 0, 1);
3359       _clutter_actor_draw_paint_volume_full (self, pv,
3360                                              _clutter_actor_get_debug_name (self),
3361                                              &color);
3362     }
3363 }
3364 
3365 static void
_clutter_actor_paint_cull_result(ClutterActor * self,gboolean success,ClutterCullResult result)3366 _clutter_actor_paint_cull_result (ClutterActor *self,
3367                                   gboolean success,
3368                                   ClutterCullResult result)
3369 {
3370   ClutterPaintVolume *pv;
3371   CoglColor color;
3372 
3373   if (success)
3374     {
3375       if (result == CLUTTER_CULL_RESULT_IN)
3376         cogl_color_init_from_4f (&color, 0, 1, 0, 1);
3377       else if (result == CLUTTER_CULL_RESULT_OUT)
3378         cogl_color_init_from_4f (&color, 0, 0, 1, 1);
3379       else
3380         cogl_color_init_from_4f (&color, 0, 1, 1, 1);
3381     }
3382   else
3383     cogl_color_init_from_4f (&color, 1, 1, 1, 1);
3384 
3385   if (success && (pv = _clutter_actor_get_paint_volume_mutable (self)))
3386     _clutter_actor_draw_paint_volume_full (self, pv,
3387                                            _clutter_actor_get_debug_name (self),
3388                                            &color);
3389   else
3390     {
3391       PangoLayout *layout;
3392       char *label =
3393         g_strdup_printf ("CULL FAILURE: %s", _clutter_actor_get_debug_name (self));
3394       cogl_color_init_from_4f (&color, 1, 1, 1, 1);
3395       cogl_set_source_color (&color);
3396 
3397       layout = pango_layout_new (clutter_actor_get_pango_context (self));
3398       pango_layout_set_text (layout, label, -1);
3399       cogl_pango_render_layout (layout,
3400                                 0,
3401                                 0,
3402                                 &color,
3403                                 0);
3404       g_free (label);
3405       g_object_unref (layout);
3406     }
3407 }
3408 
3409 static int clone_paint_level = 0;
3410 
3411 void
_clutter_actor_push_clone_paint(void)3412 _clutter_actor_push_clone_paint (void)
3413 {
3414   clone_paint_level++;
3415 }
3416 
3417 void
_clutter_actor_pop_clone_paint(void)3418 _clutter_actor_pop_clone_paint (void)
3419 {
3420   clone_paint_level--;
3421 }
3422 
3423 static gboolean
in_clone_paint(void)3424 in_clone_paint (void)
3425 {
3426   return clone_paint_level > 0;
3427 }
3428 
3429 /* Returns TRUE if the actor can be ignored */
3430 /* FIXME: we should return a ClutterCullResult, and
3431  * clutter_actor_paint should understand that a CLUTTER_CULL_RESULT_IN
3432  * means there's no point in trying to cull descendants of the current
3433  * node. */
3434 static gboolean
cull_actor(ClutterActor * self,ClutterCullResult * result_out)3435 cull_actor (ClutterActor      *self,
3436             ClutterCullResult *result_out)
3437 {
3438   ClutterActorPrivate *priv = self->priv;
3439   ClutterStage *stage;
3440   const ClutterPlane *stage_clip;
3441 
3442   if (!priv->last_paint_volume_valid)
3443     {
3444       CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): "
3445                     "->last_paint_volume_valid == FALSE",
3446                     _clutter_actor_get_debug_name (self));
3447       return FALSE;
3448     }
3449 
3450   if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CULLING))
3451     return FALSE;
3452 
3453   stage = (ClutterStage *) _clutter_actor_get_stage_internal (self);
3454   stage_clip = _clutter_stage_get_clip (stage);
3455   if (G_UNLIKELY (!stage_clip))
3456     {
3457       CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): "
3458                     "No stage clip set",
3459                     _clutter_actor_get_debug_name (self));
3460       return FALSE;
3461     }
3462 
3463   if (cogl_get_draw_framebuffer () != _clutter_stage_get_active_framebuffer (stage))
3464     {
3465       CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): "
3466                     "Current framebuffer doesn't correspond to stage",
3467                     _clutter_actor_get_debug_name (self));
3468       return FALSE;
3469     }
3470 
3471   *result_out =
3472     _clutter_paint_volume_cull (&priv->last_paint_volume, stage_clip);
3473 
3474   return TRUE;
3475 }
3476 
3477 static void
_clutter_actor_update_last_paint_volume(ClutterActor * self)3478 _clutter_actor_update_last_paint_volume (ClutterActor *self)
3479 {
3480   ClutterActorPrivate *priv = self->priv;
3481   const ClutterPaintVolume *pv;
3482 
3483   if (priv->last_paint_volume_valid)
3484     {
3485       clutter_paint_volume_free (&priv->last_paint_volume);
3486       priv->last_paint_volume_valid = FALSE;
3487     }
3488 
3489   pv = clutter_actor_get_paint_volume (self);
3490   if (!pv)
3491     {
3492       CLUTTER_NOTE (CLIPPING, "Bail from update_last_paint_volume (%s): "
3493                     "Actor failed to report a paint volume",
3494                     _clutter_actor_get_debug_name (self));
3495       return;
3496     }
3497 
3498   _clutter_paint_volume_copy_static (pv, &priv->last_paint_volume);
3499 
3500   _clutter_paint_volume_transform_relative (&priv->last_paint_volume,
3501                                             NULL); /* eye coordinates */
3502 
3503   priv->last_paint_volume_valid = TRUE;
3504 }
3505 
3506 static inline gboolean
actor_has_shader_data(ClutterActor * self)3507 actor_has_shader_data (ClutterActor *self)
3508 {
3509   return g_object_get_qdata (G_OBJECT (self), quark_shader_data) != NULL;
3510 }
3511 
3512 guint32
_clutter_actor_get_pick_id(ClutterActor * self)3513 _clutter_actor_get_pick_id (ClutterActor *self)
3514 {
3515   if (self->priv->pick_id < 0)
3516     return 0;
3517 
3518   return self->priv->pick_id;
3519 }
3520 
3521 /* This is the same as clutter_actor_add_effect except that it doesn't
3522    queue a redraw and it doesn't notify on the effect property */
3523 static void
_clutter_actor_add_effect_internal(ClutterActor * self,ClutterEffect * effect)3524 _clutter_actor_add_effect_internal (ClutterActor  *self,
3525                                     ClutterEffect *effect)
3526 {
3527   ClutterActorPrivate *priv = self->priv;
3528 
3529   if (priv->effects == NULL)
3530     {
3531       priv->effects = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
3532       priv->effects->actor = self;
3533     }
3534 
3535   _clutter_meta_group_add_meta (priv->effects, CLUTTER_ACTOR_META (effect));
3536 }
3537 
3538 /* This is the same as clutter_actor_remove_effect except that it doesn't
3539    queue a redraw and it doesn't notify on the effect property */
3540 static void
_clutter_actor_remove_effect_internal(ClutterActor * self,ClutterEffect * effect)3541 _clutter_actor_remove_effect_internal (ClutterActor  *self,
3542                                        ClutterEffect *effect)
3543 {
3544   ClutterActorPrivate *priv = self->priv;
3545 
3546   if (priv->effects == NULL)
3547     return;
3548 
3549   _clutter_meta_group_remove_meta (priv->effects, CLUTTER_ACTOR_META (effect));
3550 
3551   if (_clutter_meta_group_peek_metas (priv->effects) == NULL)
3552     g_clear_object (&priv->effects);
3553 }
3554 
3555 static gboolean
needs_flatten_effect(ClutterActor * self)3556 needs_flatten_effect (ClutterActor *self)
3557 {
3558   ClutterActorPrivate *priv = self->priv;
3559 
3560   if (G_UNLIKELY (clutter_paint_debug_flags &
3561                   CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT))
3562     return FALSE;
3563 
3564   if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ALWAYS)
3565     return TRUE;
3566   else if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY)
3567     {
3568       if (clutter_actor_get_paint_opacity (self) < 255 &&
3569           clutter_actor_has_overlaps (self))
3570         return TRUE;
3571     }
3572 
3573   return FALSE;
3574 }
3575 
3576 static void
add_or_remove_flatten_effect(ClutterActor * self)3577 add_or_remove_flatten_effect (ClutterActor *self)
3578 {
3579   ClutterActorPrivate *priv = self->priv;
3580 
3581   /* Add or remove the flatten effect depending on the
3582      offscreen-redirect property. */
3583   if (needs_flatten_effect (self))
3584     {
3585       if (priv->flatten_effect == NULL)
3586         {
3587           ClutterActorMeta *actor_meta;
3588           gint priority;
3589 
3590           priv->flatten_effect = _clutter_flatten_effect_new ();
3591           /* Keep a reference to the effect so that we can queue
3592              redraws from it */
3593           g_object_ref_sink (priv->flatten_effect);
3594 
3595           /* Set the priority of the effect to high so that it will
3596              always be applied to the actor first. It uses an internal
3597              priority so that it won't be visible to applications */
3598           actor_meta = CLUTTER_ACTOR_META (priv->flatten_effect);
3599           priority = CLUTTER_ACTOR_META_PRIORITY_INTERNAL_HIGH;
3600           _clutter_actor_meta_set_priority (actor_meta, priority);
3601 
3602           /* This will add the effect without queueing a redraw */
3603           _clutter_actor_add_effect_internal (self, priv->flatten_effect);
3604         }
3605     }
3606   else
3607     {
3608       if (priv->flatten_effect != NULL)
3609         {
3610           /* Destroy the effect so that it will lose its fbo cache of
3611              the actor */
3612           _clutter_actor_remove_effect_internal (self, priv->flatten_effect);
3613           g_clear_object (&priv->flatten_effect);
3614         }
3615     }
3616 }
3617 
3618 static void
clutter_actor_real_paint(ClutterActor * actor)3619 clutter_actor_real_paint (ClutterActor *actor)
3620 {
3621   ClutterActorPrivate *priv = actor->priv;
3622   ClutterActor *iter;
3623 
3624   for (iter = priv->first_child;
3625        iter != NULL;
3626        iter = iter->priv->next_sibling)
3627     {
3628       CLUTTER_NOTE (PAINT, "Painting %s, child of %s, at { %.2f, %.2f - %.2f x %.2f }",
3629                     _clutter_actor_get_debug_name (iter),
3630                     _clutter_actor_get_debug_name (actor),
3631                     iter->priv->allocation.x1,
3632                     iter->priv->allocation.y1,
3633                     iter->priv->allocation.x2 - iter->priv->allocation.x1,
3634                     iter->priv->allocation.y2 - iter->priv->allocation.y1);
3635 
3636       clutter_actor_paint (iter);
3637     }
3638 }
3639 
3640 static gboolean
clutter_actor_paint_node(ClutterActor * actor,ClutterPaintNode * root)3641 clutter_actor_paint_node (ClutterActor     *actor,
3642                           ClutterPaintNode *root)
3643 {
3644   ClutterActorPrivate *priv = actor->priv;
3645   ClutterActorBox box;
3646   ClutterColor bg_color;
3647 
3648   if (root == NULL)
3649     return FALSE;
3650 
3651   box.x1 = 0.f;
3652   box.y1 = 0.f;
3653   box.x2 = clutter_actor_box_get_width (&priv->allocation);
3654   box.y2 = clutter_actor_box_get_height (&priv->allocation);
3655 
3656   bg_color = priv->bg_color;
3657 
3658   if (CLUTTER_ACTOR_IS_TOPLEVEL (actor))
3659     {
3660       ClutterPaintNode *node;
3661       CoglFramebuffer *fb;
3662       CoglBufferBit clear_flags;
3663 
3664       fb = _clutter_stage_get_active_framebuffer (CLUTTER_STAGE (actor));
3665 
3666       if (clutter_stage_get_use_alpha (CLUTTER_STAGE (actor)))
3667         {
3668           bg_color.alpha = priv->opacity
3669                          * priv->bg_color.alpha
3670                          / 255;
3671         }
3672       else
3673         bg_color.alpha = 255;
3674 
3675       CLUTTER_NOTE (PAINT, "Stage clear color: (%d, %d, %d, %d)",
3676                     bg_color.red,
3677                     bg_color.green,
3678                     bg_color.blue,
3679                     bg_color.alpha);
3680 
3681       clear_flags = COGL_BUFFER_BIT_DEPTH;
3682       if (!clutter_stage_get_no_clear_hint (CLUTTER_STAGE (actor)))
3683         clear_flags |= COGL_BUFFER_BIT_COLOR;
3684 
3685       node = _clutter_root_node_new (fb, &bg_color, clear_flags);
3686       clutter_paint_node_set_name (node, "stageClear");
3687       clutter_paint_node_add_rectangle (node, &box);
3688       clutter_paint_node_add_child (root, node);
3689       clutter_paint_node_unref (node);
3690     }
3691   else if (priv->bg_color_set &&
3692            !clutter_color_equal (&priv->bg_color, CLUTTER_COLOR_Transparent))
3693     {
3694       ClutterPaintNode *node;
3695 
3696       bg_color.alpha = clutter_actor_get_paint_opacity_internal (actor)
3697                      * priv->bg_color.alpha
3698                      / 255;
3699 
3700       node = clutter_color_node_new (&bg_color);
3701       clutter_paint_node_set_name (node, "backgroundColor");
3702       clutter_paint_node_add_rectangle (node, &box);
3703       clutter_paint_node_add_child (root, node);
3704       clutter_paint_node_unref (node);
3705     }
3706 
3707   if (priv->content != NULL)
3708     _clutter_content_paint_content (priv->content, actor, root);
3709 
3710   if (CLUTTER_ACTOR_GET_CLASS (actor)->paint_node != NULL)
3711     CLUTTER_ACTOR_GET_CLASS (actor)->paint_node (actor, root);
3712 
3713   if (clutter_paint_node_get_n_children (root) == 0)
3714     return FALSE;
3715 
3716 #ifdef CLUTTER_ENABLE_DEBUG
3717   if (CLUTTER_HAS_DEBUG (PAINT))
3718     {
3719       /* dump the tree only if we have one */
3720       _clutter_paint_node_dump_tree (root);
3721     }
3722 #endif /* CLUTTER_ENABLE_DEBUG */
3723 
3724   _clutter_paint_node_paint (root);
3725 
3726   return TRUE;
3727 }
3728 
3729 /**
3730  * clutter_actor_paint:
3731  * @self: A #ClutterActor
3732  *
3733  * Renders the actor to display.
3734  *
3735  * This function should not be called directly by applications.
3736  * Call clutter_actor_queue_redraw() to queue paints, instead.
3737  *
3738  * This function is context-aware, and will either cause a
3739  * regular paint or a pick paint.
3740  *
3741  * This function will emit the #ClutterActor::paint signal or
3742  * the #ClutterActor::pick signal, depending on the context.
3743  *
3744  * This function does not paint the actor if the actor is set to 0,
3745  * unless it is performing a pick paint.
3746  */
3747 void
clutter_actor_paint(ClutterActor * self)3748 clutter_actor_paint (ClutterActor *self)
3749 {
3750   ClutterActorPrivate *priv;
3751   ClutterPickMode pick_mode;
3752   gboolean clip_set = FALSE;
3753   gboolean shader_applied = FALSE;
3754   ClutterStage *stage;
3755 
3756   g_return_if_fail (CLUTTER_IS_ACTOR (self));
3757 
3758   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
3759     return;
3760 
3761   priv = self->priv;
3762 
3763   pick_mode = _clutter_context_get_pick_mode ();
3764 
3765   if (pick_mode == CLUTTER_PICK_NONE)
3766     priv->propagated_one_redraw = FALSE;
3767 
3768   /* It's an important optimization that we consider painting of
3769    * actors with 0 opacity to be a NOP... */
3770   if (pick_mode == CLUTTER_PICK_NONE &&
3771       /* ignore top-levels, since they might be transparent */
3772       !CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
3773       /* Use the override opacity if its been set */
3774       ((priv->opacity_override >= 0) ?
3775        priv->opacity_override : priv->opacity) == 0)
3776     return;
3777 
3778   /* if we aren't paintable (not in a toplevel with all
3779    * parents paintable) then do nothing.
3780    */
3781   if (!CLUTTER_ACTOR_IS_MAPPED (self))
3782     return;
3783 
3784   stage = (ClutterStage *) _clutter_actor_get_stage_internal (self);
3785 
3786   /* mark that we are in the paint process */
3787   CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_PAINT);
3788 
3789   cogl_push_matrix ();
3790 
3791   if (priv->enable_model_view_transform)
3792     {
3793       CoglMatrix matrix;
3794 
3795       /* XXX: It could be better to cache the modelview with the actor
3796        * instead of progressively building up the transformations on
3797        * the matrix stack every time we paint. */
3798       cogl_get_modelview_matrix (&matrix);
3799       _clutter_actor_apply_modelview_transform (self, &matrix);
3800 
3801 #ifdef CLUTTER_ENABLE_DEBUG
3802       /* Catch when out-of-band transforms have been made by actors not as part
3803        * of an apply_transform vfunc... */
3804       if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_OOB_TRANSFORMS))
3805         {
3806           CoglMatrix expected_matrix;
3807 
3808           _clutter_actor_get_relative_transformation_matrix (self, NULL,
3809                                                              &expected_matrix);
3810 
3811           if (!cogl_matrix_equal (&matrix, &expected_matrix))
3812             {
3813               GString *buf = g_string_sized_new (1024);
3814               ClutterActor *parent;
3815 
3816               parent = self;
3817               while (parent != NULL)
3818                 {
3819                   g_string_append (buf, _clutter_actor_get_debug_name (parent));
3820 
3821                   if (parent->priv->parent != NULL)
3822                     g_string_append (buf, "->");
3823 
3824                   parent = parent->priv->parent;
3825                 }
3826 
3827               g_warning ("Unexpected transform found when painting actor "
3828                          "\"%s\". This will be caused by one of the actor's "
3829                          "ancestors (%s) using the Cogl API directly to transform "
3830                          "children instead of using ::apply_transform().",
3831                          _clutter_actor_get_debug_name (self),
3832                          buf->str);
3833 
3834               g_string_free (buf, TRUE);
3835             }
3836         }
3837 #endif /* CLUTTER_ENABLE_DEBUG */
3838 
3839       cogl_set_modelview_matrix (&matrix);
3840     }
3841 
3842   if (priv->has_clip)
3843     {
3844       CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
3845       cogl_framebuffer_push_rectangle_clip (fb,
3846                                             priv->clip.origin.x,
3847                                             priv->clip.origin.y,
3848                                             priv->clip.origin.x + priv->clip.size.width,
3849                                             priv->clip.origin.y + priv->clip.size.height);
3850       clip_set = TRUE;
3851     }
3852   else if (priv->clip_to_allocation)
3853     {
3854       CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
3855       gfloat width, height;
3856 
3857       width  = priv->allocation.x2 - priv->allocation.x1;
3858       height = priv->allocation.y2 - priv->allocation.y1;
3859 
3860       cogl_framebuffer_push_rectangle_clip (fb, 0, 0, width, height);
3861       clip_set = TRUE;
3862     }
3863 
3864   if (pick_mode == CLUTTER_PICK_NONE)
3865     {
3866       /* We check whether we need to add the flatten effect before
3867          each paint so that we can avoid having a mechanism for
3868          applications to notify when the value of the
3869          has_overlaps virtual changes. */
3870       add_or_remove_flatten_effect (self);
3871     }
3872 
3873   /* We save the current paint volume so that the next time the
3874    * actor queues a redraw we can constrain the redraw to just
3875    * cover the union of the new bounding box and the old.
3876    *
3877    * We also fetch the current paint volume to perform culling so
3878    * we can avoid painting actors outside the current clip region.
3879    *
3880    * If we are painting inside a clone, we should neither update
3881    * the paint volume or use it to cull painting, since the paint
3882    * box represents the location of the source actor on the
3883    * screen.
3884    *
3885    * XXX: We are starting to do a lot of vertex transforms on
3886    * the CPU in a typical paint, so at some point we should
3887    * audit these and consider caching some things.
3888    *
3889    * NB: We don't perform culling while picking at this point because
3890    * clutter-stage.c doesn't setup the clipping planes appropriately.
3891    *
3892    * NB: We don't want to update the last-paint-volume during picking
3893    * because the last-paint-volume is used to determine the old screen
3894    * space location of an actor that has moved so we can know the
3895    * minimal region to redraw to clear an old view of the actor. If we
3896    * update this during picking then by the time we come around to
3897    * paint then the last-paint-volume would likely represent the new
3898    * actor position not the old.
3899    */
3900   if (!in_clone_paint () && pick_mode == CLUTTER_PICK_NONE)
3901     {
3902       gboolean success;
3903       /* annoyingly gcc warns if uninitialized even though
3904        * the initialization is redundant :-( */
3905       ClutterCullResult result = CLUTTER_CULL_RESULT_IN;
3906 
3907       if (G_LIKELY ((clutter_paint_debug_flags &
3908                      (CLUTTER_DEBUG_DISABLE_CULLING |
3909                       CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)) !=
3910                     (CLUTTER_DEBUG_DISABLE_CULLING |
3911                      CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
3912         _clutter_actor_update_last_paint_volume (self);
3913 
3914       success = cull_actor (self, &result);
3915 
3916       if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS))
3917         _clutter_actor_paint_cull_result (self, success, result);
3918       else if (result == CLUTTER_CULL_RESULT_OUT && success)
3919         goto done;
3920     }
3921 
3922   if (priv->effects == NULL)
3923     {
3924       if (pick_mode == CLUTTER_PICK_NONE &&
3925           actor_has_shader_data (self))
3926         {
3927           _clutter_actor_shader_pre_paint (self, FALSE);
3928           shader_applied = TRUE;
3929         }
3930 
3931       priv->next_effect_to_paint = NULL;
3932     }
3933   else
3934     priv->next_effect_to_paint =
3935       _clutter_meta_group_peek_metas (priv->effects);
3936 
3937   clutter_actor_continue_paint (self);
3938 
3939   if (shader_applied)
3940     _clutter_actor_shader_post_paint (self);
3941 
3942   if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES &&
3943                   pick_mode == CLUTTER_PICK_NONE))
3944     _clutter_actor_draw_paint_volume (self);
3945 
3946 done:
3947   /* If we make it here then the actor has run through a complete
3948      paint run including all the effects so it's no longer dirty */
3949   if (pick_mode == CLUTTER_PICK_NONE)
3950     priv->is_dirty = FALSE;
3951 
3952   if (clip_set)
3953     {
3954       CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage);
3955 
3956       cogl_framebuffer_pop_clip (fb);
3957     }
3958 
3959   cogl_pop_matrix ();
3960 
3961   /* paint sequence complete */
3962   CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PAINT);
3963 }
3964 
3965 /**
3966  * clutter_actor_continue_paint:
3967  * @self: A #ClutterActor
3968  *
3969  * Run the next stage of the paint sequence. This function should only
3970  * be called within the implementation of the ‘run’ virtual of a
3971  * #ClutterEffect. It will cause the run method of the next effect to
3972  * be applied, or it will paint the actual actor if the current effect
3973  * is the last effect in the chain.
3974  *
3975  * Since: 1.8
3976  */
3977 void
clutter_actor_continue_paint(ClutterActor * self)3978 clutter_actor_continue_paint (ClutterActor *self)
3979 {
3980   ClutterActorPrivate *priv;
3981 
3982   g_return_if_fail (CLUTTER_IS_ACTOR (self));
3983   /* This should only be called from with in the ‘run’ implementation
3984      of a ClutterEffect */
3985   g_return_if_fail (CLUTTER_ACTOR_IN_PAINT (self));
3986 
3987   priv = self->priv;
3988 
3989   /* Skip any effects that are disabled */
3990   while (priv->next_effect_to_paint &&
3991          !clutter_actor_meta_get_enabled (priv->next_effect_to_paint->data))
3992     priv->next_effect_to_paint = priv->next_effect_to_paint->next;
3993 
3994   /* If this has come from the last effect then we'll just paint the
3995      actual actor */
3996   if (priv->next_effect_to_paint == NULL)
3997     {
3998       if (_clutter_context_get_pick_mode () == CLUTTER_PICK_NONE)
3999         {
4000           ClutterPaintNode *dummy;
4001 
4002           /* XXX - this will go away in 2.0, when we can get rid of this
4003            * stuff and switch to a pure retained render tree of PaintNodes
4004            * for the entire frame, starting from the Stage; the paint()
4005            * virtual function can then be called directly.
4006            */
4007           dummy = _clutter_dummy_node_new (self);
4008           clutter_paint_node_set_name (dummy, "Root");
4009 
4010           /* XXX - for 1.12, we use the return value of paint_node() to
4011            * decide whether we should emit the ::paint signal.
4012            */
4013           clutter_actor_paint_node (self, dummy);
4014           clutter_paint_node_unref (dummy);
4015 
4016           /* XXX:2.0 - Call the paint() virtual directly */
4017           g_signal_emit (self, actor_signals[PAINT], 0);
4018         }
4019       else
4020         {
4021           ClutterColor col = { 0, };
4022 
4023           _clutter_id_to_color (_clutter_actor_get_pick_id (self), &col);
4024 
4025           /* Actor will then paint silhouette of itself in supplied
4026            * color.  See clutter_stage_get_actor_at_pos() for where
4027            * picking is enabled.
4028            *
4029            * XXX:2.0 - Call the pick() virtual directly
4030            */
4031           g_signal_emit (self, actor_signals[PICK], 0, &col);
4032         }
4033     }
4034   else
4035     {
4036       ClutterEffect *old_current_effect;
4037       ClutterEffectPaintFlags run_flags = 0;
4038 
4039       /* Cache the current effect so that we can put it back before
4040          returning */
4041       old_current_effect = priv->current_effect;
4042 
4043       priv->current_effect = priv->next_effect_to_paint->data;
4044       priv->next_effect_to_paint = priv->next_effect_to_paint->next;
4045 
4046       if (_clutter_context_get_pick_mode () == CLUTTER_PICK_NONE)
4047         {
4048           if (priv->is_dirty)
4049             {
4050               /* If there's an effect queued with this redraw then all
4051                  effects up to that one will be considered dirty. It
4052                  is expected the queued effect will paint the cached
4053                  image and not call clutter_actor_continue_paint again
4054                  (although it should work ok if it does) */
4055               if (priv->effect_to_redraw == NULL ||
4056                   priv->current_effect != priv->effect_to_redraw)
4057                 run_flags |= CLUTTER_EFFECT_PAINT_ACTOR_DIRTY;
4058             }
4059 
4060           _clutter_effect_paint (priv->current_effect, run_flags);
4061         }
4062       else
4063         {
4064           /* We can't determine when an actor has been modified since
4065              its last pick so lets just assume it has always been
4066              modified */
4067           run_flags |= CLUTTER_EFFECT_PAINT_ACTOR_DIRTY;
4068 
4069           _clutter_effect_pick (priv->current_effect, run_flags);
4070         }
4071 
4072       priv->current_effect = old_current_effect;
4073     }
4074 }
4075 
4076 static void
_clutter_actor_stop_transitions(ClutterActor * self)4077 _clutter_actor_stop_transitions (ClutterActor *self)
4078 {
4079   const ClutterAnimationInfo *info;
4080   GHashTableIter iter;
4081   gpointer value;
4082 
4083   info = _clutter_actor_get_animation_info_or_defaults (self);
4084   if (info->transitions == NULL)
4085     return;
4086 
4087   g_hash_table_iter_init (&iter, info->transitions);
4088   while (g_hash_table_iter_next (&iter, NULL, &value))
4089     {
4090       TransitionClosure *closure = value;
4091 
4092       if (clutter_transition_get_remove_on_complete (closure->transition))
4093         {
4094           g_hash_table_iter_remove (&iter);
4095         }
4096       else
4097         {
4098           /* otherwise we stop it, and the transition will be removed
4099            * later, either by the actor's destruction or by explicit
4100            * removal
4101            */
4102           clutter_timeline_stop (CLUTTER_TIMELINE (closure->transition));
4103         }
4104     }
4105 }
4106 
4107 static ClutterActorTraverseVisitFlags
invalidate_queue_redraw_entry(ClutterActor * self,int depth,gpointer user_data)4108 invalidate_queue_redraw_entry (ClutterActor *self,
4109                                int           depth,
4110                                gpointer      user_data)
4111 {
4112   ClutterActorPrivate *priv = self->priv;
4113 
4114   if (priv->queue_redraw_entry != NULL)
4115     {
4116       _clutter_stage_queue_redraw_entry_invalidate (priv->queue_redraw_entry);
4117       priv->queue_redraw_entry = NULL;
4118     }
4119 
4120   return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
4121 }
4122 
4123 static inline void
remove_child(ClutterActor * self,ClutterActor * child)4124 remove_child (ClutterActor *self,
4125               ClutterActor *child)
4126 {
4127   ClutterActor *prev_sibling, *next_sibling;
4128 
4129   prev_sibling = child->priv->prev_sibling;
4130   next_sibling = child->priv->next_sibling;
4131 
4132   if (prev_sibling != NULL)
4133     prev_sibling->priv->next_sibling = next_sibling;
4134 
4135   if (next_sibling != NULL)
4136     next_sibling->priv->prev_sibling = prev_sibling;
4137 
4138   if (self->priv->first_child == child)
4139     self->priv->first_child = next_sibling;
4140 
4141   if (self->priv->last_child == child)
4142     self->priv->last_child = prev_sibling;
4143 
4144   child->priv->parent = NULL;
4145   child->priv->prev_sibling = NULL;
4146   child->priv->next_sibling = NULL;
4147 }
4148 
4149 typedef enum {
4150   REMOVE_CHILD_DESTROY_META       = 1 << 0,
4151   REMOVE_CHILD_EMIT_PARENT_SET    = 1 << 1,
4152   REMOVE_CHILD_EMIT_ACTOR_REMOVED = 1 << 2,
4153   REMOVE_CHILD_CHECK_STATE        = 1 << 3,
4154   REMOVE_CHILD_FLUSH_QUEUE        = 1 << 4,
4155   REMOVE_CHILD_NOTIFY_FIRST_LAST  = 1 << 5,
4156   REMOVE_CHILD_STOP_TRANSITIONS   = 1 << 6,
4157 
4158   /* default flags for public API */
4159   REMOVE_CHILD_DEFAULT_FLAGS      = REMOVE_CHILD_STOP_TRANSITIONS |
4160                                     REMOVE_CHILD_DESTROY_META |
4161                                     REMOVE_CHILD_EMIT_PARENT_SET |
4162                                     REMOVE_CHILD_EMIT_ACTOR_REMOVED |
4163                                     REMOVE_CHILD_CHECK_STATE |
4164                                     REMOVE_CHILD_FLUSH_QUEUE |
4165                                     REMOVE_CHILD_NOTIFY_FIRST_LAST,
4166 
4167   /* flags for legacy/deprecated API */
4168   REMOVE_CHILD_LEGACY_FLAGS       = REMOVE_CHILD_STOP_TRANSITIONS |
4169                                     REMOVE_CHILD_CHECK_STATE |
4170                                     REMOVE_CHILD_FLUSH_QUEUE |
4171                                     REMOVE_CHILD_EMIT_PARENT_SET |
4172                                     REMOVE_CHILD_NOTIFY_FIRST_LAST
4173 } ClutterActorRemoveChildFlags;
4174 
4175 /*< private >
4176  * clutter_actor_remove_child_internal:
4177  * @self: a #ClutterActor
4178  * @child: the child of @self that has to be removed
4179  * @flags: control the removal operations
4180  *
4181  * Removes @child from the list of children of @self.
4182  */
4183 static void
clutter_actor_remove_child_internal(ClutterActor * self,ClutterActor * child,ClutterActorRemoveChildFlags flags)4184 clutter_actor_remove_child_internal (ClutterActor                 *self,
4185                                      ClutterActor                 *child,
4186                                      ClutterActorRemoveChildFlags  flags)
4187 {
4188   ClutterActor *old_first, *old_last;
4189   gboolean destroy_meta, emit_parent_set, emit_actor_removed, check_state;
4190   gboolean flush_queue;
4191   gboolean notify_first_last;
4192   gboolean was_mapped;
4193   gboolean stop_transitions;
4194   GObject *obj;
4195 
4196   if (self == child)
4197     {
4198       g_warning ("Cannot remove actor '%s' from itself.",
4199                  _clutter_actor_get_debug_name (self));
4200       return;
4201     }
4202 
4203   destroy_meta = (flags & REMOVE_CHILD_DESTROY_META) != 0;
4204   emit_parent_set = (flags & REMOVE_CHILD_EMIT_PARENT_SET) != 0;
4205   emit_actor_removed = (flags & REMOVE_CHILD_EMIT_ACTOR_REMOVED) != 0;
4206   check_state = (flags & REMOVE_CHILD_CHECK_STATE) != 0;
4207   flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0;
4208   notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0;
4209   stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0;
4210 
4211   obj = G_OBJECT (self);
4212   g_object_freeze_notify (obj);
4213 
4214   if (stop_transitions)
4215     _clutter_actor_stop_transitions (child);
4216 
4217   if (destroy_meta)
4218     clutter_container_destroy_child_meta (CLUTTER_CONTAINER (self), child);
4219 
4220   if (check_state)
4221     {
4222       was_mapped = CLUTTER_ACTOR_IS_MAPPED (child);
4223 
4224       /* we need to unrealize *before* we set parent_actor to NULL,
4225        * because in an unrealize method actors are dissociating from the
4226        * stage, which means they need to be able to
4227        * clutter_actor_get_stage().
4228        *
4229        * yhis should unmap and unrealize, unless we're reparenting.
4230        */
4231       clutter_actor_update_map_state (child, MAP_STATE_MAKE_UNREALIZED);
4232     }
4233   else
4234     was_mapped = FALSE;
4235 
4236   if (flush_queue)
4237     {
4238       /* We take this opportunity to invalidate any queue redraw entry
4239        * associated with the actor and descendants since we won't be able to
4240        * determine the appropriate stage after this.
4241        *
4242        * we do this after we updated the mapped state because actors might
4243        * end up queueing redraws inside their mapped/unmapped virtual
4244        * functions, and if we invalidate the redraw entry we could end up
4245        * with an inconsistent state and weird memory corruption. see
4246        * bugs:
4247        *
4248        *   http://bugzilla.clutter-project.org/show_bug.cgi?id=2621
4249        *   https://bugzilla.gnome.org/show_bug.cgi?id=652036
4250        */
4251       _clutter_actor_traverse (child,
4252                                0,
4253                                invalidate_queue_redraw_entry,
4254                                NULL,
4255                                NULL);
4256     }
4257 
4258   old_first = self->priv->first_child;
4259   old_last = self->priv->last_child;
4260 
4261   remove_child (self, child);
4262 
4263   self->priv->n_children -= 1;
4264 
4265   self->priv->age += 1;
4266 
4267   /* if the child that got removed was visible and set to
4268    * expand then we want to reset the parent's state in
4269    * case the child was the only thing that was making it
4270    * expand.
4271    */
4272   if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
4273       (child->priv->needs_compute_expand ||
4274        child->priv->needs_x_expand ||
4275        child->priv->needs_y_expand))
4276     {
4277       clutter_actor_queue_compute_expand (self);
4278     }
4279 
4280   /* clutter_actor_reparent() will emit ::parent-set for us */
4281   if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child))
4282     g_signal_emit (child, actor_signals[PARENT_SET], 0, self);
4283 
4284   /* if the child was mapped then we need to relayout ourselves to account
4285    * for the removed child
4286    */
4287   if (was_mapped)
4288     clutter_actor_queue_relayout (self);
4289 
4290   /* we need to emit the signal before dropping the reference */
4291   if (emit_actor_removed)
4292     g_signal_emit_by_name (self, "actor-removed", child);
4293 
4294   if (notify_first_last)
4295     {
4296       if (old_first != self->priv->first_child)
4297         g_object_notify_by_pspec (obj, obj_props[PROP_FIRST_CHILD]);
4298 
4299       if (old_last != self->priv->last_child)
4300         g_object_notify_by_pspec (obj, obj_props[PROP_LAST_CHILD]);
4301     }
4302 
4303   g_object_thaw_notify (obj);
4304 
4305   /* remove the reference we acquired in clutter_actor_add_child() */
4306   g_object_unref (child);
4307 }
4308 
4309 static const ClutterTransformInfo default_transform_info = {
4310   0.0, { 0, },                  /* rotation-x */
4311   0.0, { 0, },                  /* rotation-y */
4312   0.0, { 0, },                  /* rotation-z */
4313 
4314   1.0, 1.0, 1.0, { 0, },        /* scale */
4315 
4316   { 0, },                       /* anchor XXX:2.0 - remove*/
4317 
4318   CLUTTER_VERTEX_INIT_ZERO,     /* translation */
4319 
4320   0.f,                          /* z-position */
4321 
4322   CLUTTER_POINT_INIT_ZERO,      /* pivot */
4323   0.f,                          /* pivot-z */
4324 
4325   CLUTTER_MATRIX_INIT_IDENTITY,
4326   FALSE,                        /* transform */
4327   CLUTTER_MATRIX_INIT_IDENTITY,
4328   FALSE,                        /* child-transform */
4329 };
4330 
4331 /*< private >
4332  * _clutter_actor_get_transform_info_or_defaults:
4333  * @self: a #ClutterActor
4334  *
4335  * Retrieves the ClutterTransformInfo structure associated to an actor.
4336  *
4337  * If the actor does not have a ClutterTransformInfo structure associated
4338  * to it, then the default structure will be returned.
4339  *
4340  * This function should only be used for getters.
4341  *
4342  * Return value: a const pointer to the ClutterTransformInfo structure
4343  */
4344 const ClutterTransformInfo *
_clutter_actor_get_transform_info_or_defaults(ClutterActor * self)4345 _clutter_actor_get_transform_info_or_defaults (ClutterActor *self)
4346 {
4347   ClutterTransformInfo *info;
4348 
4349   info = g_object_get_qdata (G_OBJECT (self), quark_actor_transform_info);
4350   if (info != NULL)
4351     return info;
4352 
4353   return &default_transform_info;
4354 }
4355 
4356 static void
clutter_transform_info_free(gpointer data)4357 clutter_transform_info_free (gpointer data)
4358 {
4359   if (data != NULL)
4360     g_slice_free (ClutterTransformInfo, data);
4361 }
4362 
4363 /*< private >
4364  * _clutter_actor_get_transform_info:
4365  * @self: a #ClutterActor
4366  *
4367  * Retrieves a pointer to the ClutterTransformInfo structure.
4368  *
4369  * If the actor does not have a ClutterTransformInfo associated to it, one
4370  * will be created and initialized to the default values.
4371  *
4372  * This function should be used for setters.
4373  *
4374  * For getters, you should use _clutter_actor_get_transform_info_or_defaults()
4375  * instead.
4376  *
4377  * Return value: (transfer none): a pointer to the ClutterTransformInfo
4378  *   structure
4379  */
4380 ClutterTransformInfo *
_clutter_actor_get_transform_info(ClutterActor * self)4381 _clutter_actor_get_transform_info (ClutterActor *self)
4382 {
4383   ClutterTransformInfo *info;
4384 
4385   info = g_object_get_qdata (G_OBJECT (self), quark_actor_transform_info);
4386   if (info == NULL)
4387     {
4388       info = g_slice_new (ClutterTransformInfo);
4389 
4390       *info = default_transform_info;
4391 
4392       g_object_set_qdata_full (G_OBJECT (self), quark_actor_transform_info,
4393                                info,
4394                                clutter_transform_info_free);
4395     }
4396 
4397   return info;
4398 }
4399 
4400 static inline void
clutter_actor_set_pivot_point_internal(ClutterActor * self,const ClutterPoint * pivot)4401 clutter_actor_set_pivot_point_internal (ClutterActor       *self,
4402                                         const ClutterPoint *pivot)
4403 {
4404   ClutterTransformInfo *info;
4405 
4406   info = _clutter_actor_get_transform_info (self);
4407   info->pivot = *pivot;
4408 
4409   self->priv->transform_valid = FALSE;
4410 
4411   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PIVOT_POINT]);
4412 
4413   clutter_actor_queue_redraw (self);
4414 }
4415 
4416 static inline void
clutter_actor_set_pivot_point_z_internal(ClutterActor * self,float pivot_z)4417 clutter_actor_set_pivot_point_z_internal (ClutterActor *self,
4418                                           float         pivot_z)
4419 {
4420   ClutterTransformInfo *info;
4421 
4422   info = _clutter_actor_get_transform_info (self);
4423   info->pivot_z = pivot_z;
4424 
4425   self->priv->transform_valid = FALSE;
4426 
4427   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PIVOT_POINT_Z]);
4428 
4429   clutter_actor_queue_redraw (self);
4430 }
4431 
4432 /*< private >
4433  * clutter_actor_set_translation_internal:
4434  * @self: a #ClutterActor
4435  * @axis: the axis of the translation to change
4436  * @angle: the translation as a value along @axis
4437  *
4438  * Sets the translation on the given @axis
4439  */
4440 static void
clutter_actor_set_translation_internal(ClutterActor * self,gfloat value,GParamSpec * pspec)4441 clutter_actor_set_translation_internal (ClutterActor *self,
4442                                         gfloat        value,
4443                                         GParamSpec   *pspec)
4444 {
4445   GObject *obj = G_OBJECT (self);
4446   ClutterTransformInfo *info;
4447 
4448   info = _clutter_actor_get_transform_info (self);
4449 
4450   if (pspec == obj_props[PROP_TRANSLATION_X])
4451     info->translation.x = value;
4452   else if (pspec == obj_props[PROP_TRANSLATION_Y])
4453     info->translation.y = value;
4454   else if (pspec == obj_props[PROP_TRANSLATION_Z])
4455     info->translation.z = value;
4456   else
4457     g_assert_not_reached ();
4458 
4459   self->priv->transform_valid = FALSE;
4460   clutter_actor_queue_redraw (self);
4461   g_object_notify_by_pspec (obj, pspec);
4462 }
4463 
4464 static inline void
clutter_actor_set_translation_factor(ClutterActor * self,ClutterRotateAxis axis,gdouble value)4465 clutter_actor_set_translation_factor (ClutterActor      *self,
4466                                       ClutterRotateAxis  axis,
4467                                       gdouble            value)
4468 {
4469   const ClutterTransformInfo *info;
4470   const float *translate_p = NULL;
4471   GParamSpec *pspec = NULL;
4472 
4473   info = _clutter_actor_get_transform_info_or_defaults (self);
4474 
4475   switch (axis)
4476     {
4477     case CLUTTER_X_AXIS:
4478       pspec = obj_props[PROP_TRANSLATION_X];
4479       translate_p = &info->translation.x;
4480       break;
4481 
4482     case CLUTTER_Y_AXIS:
4483       pspec = obj_props[PROP_TRANSLATION_Y];
4484       translate_p = &info->translation.y;
4485       break;
4486 
4487     case CLUTTER_Z_AXIS:
4488       pspec = obj_props[PROP_TRANSLATION_Z];
4489       translate_p = &info->translation.z;
4490       break;
4491     }
4492 
4493   g_assert (pspec != NULL);
4494   g_assert (translate_p != NULL);
4495 
4496   _clutter_actor_create_transition (self, pspec, *translate_p, value);
4497 }
4498 
4499 /**
4500  * clutter_actor_set_translation:
4501  * @self: a #ClutterActor
4502  * @translate_x: the translation along the X axis
4503  * @translate_y: the translation along the Y axis
4504  * @translate_z: the translation along the Z axis
4505  *
4506  * Sets an additional translation transformation on a #ClutterActor,
4507  * relative to the #ClutterActor:pivot-point.
4508  *
4509  * Since: 1.12
4510  */
4511 void
clutter_actor_set_translation(ClutterActor * self,gfloat translate_x,gfloat translate_y,gfloat translate_z)4512 clutter_actor_set_translation (ClutterActor *self,
4513                                gfloat        translate_x,
4514                                gfloat        translate_y,
4515                                gfloat        translate_z)
4516 {
4517   g_return_if_fail (CLUTTER_IS_ACTOR (self));
4518 
4519   g_object_freeze_notify (G_OBJECT (self));
4520 
4521   clutter_actor_set_translation_factor (self, CLUTTER_X_AXIS, translate_x);
4522   clutter_actor_set_translation_factor (self, CLUTTER_Y_AXIS, translate_y);
4523   clutter_actor_set_translation_factor (self, CLUTTER_Z_AXIS, translate_z);
4524 
4525   g_object_thaw_notify (G_OBJECT (self));
4526 }
4527 
4528 /**
4529  * clutter_actor_get_translation:
4530  * @self: a #ClutterActor
4531  * @translate_x: (out) (allow-none): return location for the X component
4532  *   of the translation, or %NULL
4533  * @translate_y: (out) (allow-none): return location for the Y component
4534  *   of the translation, or %NULL
4535  * @translate_z: (out) (allow-none): return location for the Z component
4536  *   of the translation, or %NULL
4537  *
4538  * Retrieves the translation set using clutter_actor_set_translation().
4539  *
4540  * Since: 1.12
4541  */
4542 void
clutter_actor_get_translation(ClutterActor * self,gfloat * translate_x,gfloat * translate_y,gfloat * translate_z)4543 clutter_actor_get_translation (ClutterActor *self,
4544                                gfloat       *translate_x,
4545                                gfloat       *translate_y,
4546                                gfloat       *translate_z)
4547 {
4548   const ClutterTransformInfo *info;
4549 
4550   g_return_if_fail (CLUTTER_IS_ACTOR (self));
4551 
4552   info = _clutter_actor_get_transform_info_or_defaults (self);
4553 
4554   if (translate_x != NULL)
4555     *translate_x = info->translation.x;
4556 
4557   if (translate_y != NULL)
4558     *translate_y = info->translation.y;
4559 
4560   if (translate_z != NULL)
4561     *translate_z = info->translation.z;
4562 }
4563 
4564 /*< private >
4565  * clutter_actor_set_rotation_angle_internal:
4566  * @self: a #ClutterActor
4567  * @angle: the angle of rotation
4568  * @pspec: the #GParamSpec of the property
4569  *
4570  * Sets the rotation angle on the given axis without affecting the
4571  * rotation center point.
4572  */
4573 static inline void
clutter_actor_set_rotation_angle_internal(ClutterActor * self,gdouble angle,GParamSpec * pspec)4574 clutter_actor_set_rotation_angle_internal (ClutterActor *self,
4575                                            gdouble       angle,
4576                                            GParamSpec   *pspec)
4577 {
4578   ClutterTransformInfo *info;
4579 
4580   info = _clutter_actor_get_transform_info (self);
4581 
4582   if (pspec == obj_props[PROP_ROTATION_ANGLE_X])
4583     info->rx_angle = angle;
4584   else if (pspec == obj_props[PROP_ROTATION_ANGLE_Y])
4585     info->ry_angle = angle;
4586   else if (pspec == obj_props[PROP_ROTATION_ANGLE_Z])
4587     info->rz_angle = angle;
4588   else
4589     g_assert_not_reached ();
4590 
4591   self->priv->transform_valid = FALSE;
4592 
4593   clutter_actor_queue_redraw (self);
4594 
4595   g_object_notify_by_pspec (G_OBJECT (self), pspec);
4596 }
4597 
4598 /**
4599  * clutter_actor_set_rotation_angle:
4600  * @self: a #ClutterActor
4601  * @axis: the axis to set the angle one
4602  * @angle: the angle of rotation, in degrees
4603  *
4604  * Sets the @angle of rotation of a #ClutterActor on the given @axis.
4605  *
4606  * This function is a convenience for setting the rotation properties
4607  * #ClutterActor:rotation-angle-x, #ClutterActor:rotation-angle-y,
4608  * and #ClutterActor:rotation-angle-z.
4609  *
4610  * The center of rotation is established by the #ClutterActor:pivot-point
4611  * property.
4612  *
4613  * Since: 1.12
4614  */
4615 void
clutter_actor_set_rotation_angle(ClutterActor * self,ClutterRotateAxis axis,gdouble angle)4616 clutter_actor_set_rotation_angle (ClutterActor      *self,
4617                                   ClutterRotateAxis  axis,
4618                                   gdouble            angle)
4619 {
4620   const ClutterTransformInfo *info;
4621   const double *cur_angle_p = NULL;
4622   GParamSpec *pspec = NULL;
4623 
4624   g_return_if_fail (CLUTTER_IS_ACTOR (self));
4625 
4626   info = _clutter_actor_get_transform_info_or_defaults (self);
4627 
4628   switch (axis)
4629     {
4630     case CLUTTER_X_AXIS:
4631       cur_angle_p = &info->rx_angle;
4632       pspec = obj_props[PROP_ROTATION_ANGLE_X];
4633       break;
4634 
4635     case CLUTTER_Y_AXIS:
4636       cur_angle_p = &info->ry_angle;
4637       pspec = obj_props[PROP_ROTATION_ANGLE_Y];
4638       break;
4639 
4640     case CLUTTER_Z_AXIS:
4641       cur_angle_p = &info->rz_angle;
4642       pspec = obj_props[PROP_ROTATION_ANGLE_Z];
4643       break;
4644     }
4645 
4646   g_assert (pspec != NULL);
4647   g_assert (cur_angle_p != NULL);
4648 
4649   _clutter_actor_create_transition (self, pspec, *cur_angle_p, angle);
4650 }
4651 
4652 /**
4653  * clutter_actor_get_rotation_angle:
4654  * @self: a #ClutterActor
4655  * @axis: the axis of the rotation
4656  *
4657  * Retrieves the angle of rotation set by clutter_actor_set_rotation_angle().
4658  *
4659  * Return value: the angle of rotation, in degrees
4660  *
4661  * Since: 1.12
4662  */
4663 gdouble
clutter_actor_get_rotation_angle(ClutterActor * self,ClutterRotateAxis axis)4664 clutter_actor_get_rotation_angle (ClutterActor      *self,
4665                                   ClutterRotateAxis  axis)
4666 {
4667   const ClutterTransformInfo *info;
4668   gdouble retval;
4669 
4670   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.0);
4671 
4672   info = _clutter_actor_get_transform_info_or_defaults (self);
4673 
4674   switch (axis)
4675     {
4676     case CLUTTER_X_AXIS:
4677       retval = info->rx_angle;
4678       break;
4679 
4680     case CLUTTER_Y_AXIS:
4681       retval = info->ry_angle;
4682       break;
4683 
4684     case CLUTTER_Z_AXIS:
4685       retval = info->rz_angle;
4686       break;
4687 
4688     default:
4689       g_warn_if_reached ();
4690       retval = 0.0;
4691       break;
4692     }
4693 
4694   return retval;
4695 }
4696 
4697 /*< private >
4698  * clutter_actor_set_rotation_center_internal:
4699  * @self: a #ClutterActor
4700  * @axis: the axis of the center to change
4701  * @center: the coordinates of the rotation center
4702  *
4703  * Sets the rotation center on the given axis without affecting the
4704  * rotation angle.
4705  */
4706 static inline void
clutter_actor_set_rotation_center_internal(ClutterActor * self,ClutterRotateAxis axis,const ClutterVertex * center)4707 clutter_actor_set_rotation_center_internal (ClutterActor        *self,
4708                                             ClutterRotateAxis    axis,
4709                                             const ClutterVertex *center)
4710 {
4711   ClutterVertex v = CLUTTER_VERTEX_INIT_ZERO;
4712   GObject *obj = G_OBJECT (self);
4713   ClutterTransformInfo *info;
4714 
4715   info = _clutter_actor_get_transform_info (self);
4716 
4717   if (center != NULL)
4718     v = *center;
4719 
4720   g_object_freeze_notify (obj);
4721 
4722   switch (axis)
4723     {
4724     case CLUTTER_X_AXIS:
4725       clutter_anchor_coord_set_units (&info->rx_center, v.x, v.y, v.z);
4726       g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_X]);
4727       break;
4728 
4729     case CLUTTER_Y_AXIS:
4730       clutter_anchor_coord_set_units (&info->ry_center, v.x, v.y, v.z);
4731       g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Y]);
4732       break;
4733 
4734     case CLUTTER_Z_AXIS:
4735       /* if the previously set rotation center was fractional, then
4736        * setting explicit coordinates will have to notify the
4737        * :rotation-center-z-gravity property as well
4738        */
4739       if (info->rz_center.is_fractional)
4740         g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Z_GRAVITY]);
4741 
4742       clutter_anchor_coord_set_units (&info->rz_center, v.x, v.y, v.z);
4743       g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Z]);
4744       break;
4745     }
4746 
4747   self->priv->transform_valid = FALSE;
4748 
4749   g_object_thaw_notify (obj);
4750 
4751   clutter_actor_queue_redraw (self);
4752 }
4753 
4754 static void
clutter_actor_set_scale_factor_internal(ClutterActor * self,double factor,GParamSpec * pspec)4755 clutter_actor_set_scale_factor_internal (ClutterActor *self,
4756                                          double factor,
4757                                          GParamSpec *pspec)
4758 {
4759   GObject *obj = G_OBJECT (self);
4760   ClutterTransformInfo *info;
4761 
4762   info = _clutter_actor_get_transform_info (self);
4763 
4764   if (pspec == obj_props[PROP_SCALE_X])
4765     info->scale_x = factor;
4766   else if (pspec == obj_props[PROP_SCALE_Y])
4767     info->scale_y = factor;
4768   else if (pspec == obj_props[PROP_SCALE_Z])
4769     info->scale_z = factor;
4770   else
4771     g_assert_not_reached ();
4772 
4773   self->priv->transform_valid = FALSE;
4774   clutter_actor_queue_redraw (self);
4775   g_object_notify_by_pspec (obj, pspec);
4776 }
4777 
4778 static inline void
clutter_actor_set_scale_factor(ClutterActor * self,ClutterRotateAxis axis,gdouble factor)4779 clutter_actor_set_scale_factor (ClutterActor      *self,
4780                                 ClutterRotateAxis  axis,
4781                                 gdouble            factor)
4782 {
4783   const ClutterTransformInfo *info;
4784   const double *scale_p = NULL;
4785   GParamSpec *pspec = NULL;
4786 
4787   info = _clutter_actor_get_transform_info_or_defaults (self);
4788 
4789   switch (axis)
4790     {
4791     case CLUTTER_X_AXIS:
4792       pspec = obj_props[PROP_SCALE_X];
4793       scale_p = &info->scale_x;
4794       break;
4795 
4796     case CLUTTER_Y_AXIS:
4797       pspec = obj_props[PROP_SCALE_Y];
4798       scale_p = &info->scale_y;
4799       break;
4800 
4801     case CLUTTER_Z_AXIS:
4802       pspec = obj_props[PROP_SCALE_Z];
4803       scale_p = &info->scale_z;
4804       break;
4805     }
4806 
4807   g_assert (pspec != NULL);
4808   g_assert (scale_p != NULL);
4809 
4810   _clutter_actor_create_transition (self, pspec, *scale_p, factor);
4811 }
4812 
4813 static inline void
clutter_actor_set_scale_center(ClutterActor * self,ClutterRotateAxis axis,gfloat coord)4814 clutter_actor_set_scale_center (ClutterActor      *self,
4815                                 ClutterRotateAxis  axis,
4816                                 gfloat             coord)
4817 {
4818   GObject *obj = G_OBJECT (self);
4819   ClutterTransformInfo *info;
4820   gfloat center_x, center_y;
4821 
4822   info = _clutter_actor_get_transform_info (self);
4823 
4824   g_object_freeze_notify (obj);
4825 
4826   /* get the current scale center coordinates */
4827   clutter_anchor_coord_get_units (self, &info->scale_center,
4828                                   &center_x,
4829                                   &center_y,
4830                                   NULL);
4831 
4832   /* we need to notify this too, because setting explicit coordinates will
4833    * change the gravity as a side effect
4834    */
4835   if (info->scale_center.is_fractional)
4836     g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_GRAVITY]);
4837 
4838   switch (axis)
4839     {
4840     case CLUTTER_X_AXIS:
4841       clutter_anchor_coord_set_units (&info->scale_center, coord, center_y, 0);
4842       g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_X]);
4843       break;
4844 
4845     case CLUTTER_Y_AXIS:
4846       clutter_anchor_coord_set_units (&info->scale_center, center_x, coord, 0);
4847       g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_Y]);
4848       break;
4849 
4850     default:
4851       g_assert_not_reached ();
4852     }
4853 
4854   self->priv->transform_valid = FALSE;
4855 
4856   clutter_actor_queue_redraw (self);
4857 
4858   g_object_thaw_notify (obj);
4859 }
4860 
4861 static inline void
clutter_actor_set_scale_gravity(ClutterActor * self,ClutterGravity gravity)4862 clutter_actor_set_scale_gravity (ClutterActor   *self,
4863                                  ClutterGravity  gravity)
4864 {
4865   ClutterTransformInfo *info;
4866   GObject *obj;
4867 
4868   info = _clutter_actor_get_transform_info (self);
4869   obj = G_OBJECT (self);
4870 
4871   if (gravity == CLUTTER_GRAVITY_NONE)
4872     clutter_anchor_coord_set_units (&info->scale_center, 0, 0, 0);
4873   else
4874     clutter_anchor_coord_set_gravity (&info->scale_center, gravity);
4875 
4876   self->priv->transform_valid = FALSE;
4877 
4878   g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_X]);
4879   g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_Y]);
4880   g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_GRAVITY]);
4881 
4882   clutter_actor_queue_redraw (self);
4883 }
4884 
4885 /* XXX:2.0 - remove */
4886 static inline void
clutter_actor_set_anchor_coord(ClutterActor * self,ClutterRotateAxis axis,gfloat coord)4887 clutter_actor_set_anchor_coord (ClutterActor      *self,
4888                                 ClutterRotateAxis  axis,
4889                                 gfloat             coord)
4890 {
4891   GObject *obj = G_OBJECT (self);
4892   ClutterTransformInfo *info;
4893   gfloat anchor_x, anchor_y;
4894 
4895   info = _clutter_actor_get_transform_info (self);
4896 
4897   g_object_freeze_notify (obj);
4898 
4899   clutter_anchor_coord_get_units (self, &info->anchor,
4900                                   &anchor_x,
4901                                   &anchor_y,
4902                                   NULL);
4903 
4904   if (info->anchor.is_fractional)
4905     g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_GRAVITY]);
4906 
4907   switch (axis)
4908     {
4909     case CLUTTER_X_AXIS:
4910       clutter_anchor_coord_set_units (&info->anchor,
4911                                       coord,
4912                                       anchor_y,
4913                                       0.0);
4914       g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_X]);
4915       break;
4916 
4917     case CLUTTER_Y_AXIS:
4918       clutter_anchor_coord_set_units (&info->anchor,
4919                                       anchor_x,
4920                                       coord,
4921                                       0.0);
4922       g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_Y]);
4923       break;
4924 
4925     default:
4926       g_assert_not_reached ();
4927     }
4928 
4929   self->priv->transform_valid = FALSE;
4930 
4931   clutter_actor_queue_redraw (self);
4932 
4933   g_object_thaw_notify (obj);
4934 }
4935 
4936 static void
clutter_actor_set_clip_rect(ClutterActor * self,const ClutterRect * clip)4937 clutter_actor_set_clip_rect (ClutterActor      *self,
4938                              const ClutterRect *clip)
4939 {
4940   ClutterActorPrivate *priv = self->priv;
4941   GObject *obj = G_OBJECT (self);
4942 
4943   if (clip != NULL)
4944     {
4945       priv->clip = *clip;
4946       priv->has_clip = TRUE;
4947     }
4948   else
4949     priv->has_clip = FALSE;
4950 
4951   clutter_actor_queue_redraw (self);
4952 
4953   g_object_notify_by_pspec (obj, obj_props[PROP_CLIP]); /* XXX:2.0 - remove */
4954   g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]);
4955   g_object_notify_by_pspec (obj, obj_props[PROP_HAS_CLIP]);
4956 }
4957 
4958 static void
clutter_actor_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)4959 clutter_actor_set_property (GObject      *object,
4960 			    guint         prop_id,
4961 			    const GValue *value,
4962 			    GParamSpec   *pspec)
4963 {
4964   ClutterActor *actor = CLUTTER_ACTOR (object);
4965   ClutterActorPrivate *priv = actor->priv;
4966 
4967   switch (prop_id)
4968     {
4969     case PROP_X:
4970       clutter_actor_set_x (actor, g_value_get_float (value));
4971       break;
4972 
4973     case PROP_Y:
4974       clutter_actor_set_y (actor, g_value_get_float (value));
4975       break;
4976 
4977     case PROP_POSITION:
4978       {
4979         const ClutterPoint *pos = g_value_get_boxed (value);
4980 
4981         if (pos != NULL)
4982           clutter_actor_set_position (actor, pos->x, pos->y);
4983         else
4984           clutter_actor_set_fixed_position_set (actor, FALSE);
4985       }
4986       break;
4987 
4988     case PROP_WIDTH:
4989       clutter_actor_set_width (actor, g_value_get_float (value));
4990       break;
4991 
4992     case PROP_HEIGHT:
4993       clutter_actor_set_height (actor, g_value_get_float (value));
4994       break;
4995 
4996     case PROP_SIZE:
4997       {
4998         const ClutterSize *size = g_value_get_boxed (value);
4999 
5000         if (size != NULL)
5001           clutter_actor_set_size (actor, size->width, size->height);
5002         else
5003           clutter_actor_set_size (actor, -1, -1);
5004       }
5005       break;
5006 
5007     case PROP_FIXED_X:
5008       clutter_actor_set_x (actor, g_value_get_float (value));
5009       break;
5010 
5011     case PROP_FIXED_Y:
5012       clutter_actor_set_y (actor, g_value_get_float (value));
5013       break;
5014 
5015     case PROP_FIXED_POSITION_SET:
5016       clutter_actor_set_fixed_position_set (actor, g_value_get_boolean (value));
5017       break;
5018 
5019     case PROP_MIN_WIDTH:
5020       clutter_actor_set_min_width (actor, g_value_get_float (value));
5021       break;
5022 
5023     case PROP_MIN_HEIGHT:
5024       clutter_actor_set_min_height (actor, g_value_get_float (value));
5025       break;
5026 
5027     case PROP_NATURAL_WIDTH:
5028       clutter_actor_set_natural_width (actor, g_value_get_float (value));
5029       break;
5030 
5031     case PROP_NATURAL_HEIGHT:
5032       clutter_actor_set_natural_height (actor, g_value_get_float (value));
5033       break;
5034 
5035     case PROP_MIN_WIDTH_SET:
5036       clutter_actor_set_min_width_set (actor, g_value_get_boolean (value));
5037       break;
5038 
5039     case PROP_MIN_HEIGHT_SET:
5040       clutter_actor_set_min_height_set (actor, g_value_get_boolean (value));
5041       break;
5042 
5043     case PROP_NATURAL_WIDTH_SET:
5044       clutter_actor_set_natural_width_set (actor, g_value_get_boolean (value));
5045       break;
5046 
5047     case PROP_NATURAL_HEIGHT_SET:
5048       clutter_actor_set_natural_height_set (actor, g_value_get_boolean (value));
5049       break;
5050 
5051     case PROP_REQUEST_MODE:
5052       clutter_actor_set_request_mode (actor, g_value_get_enum (value));
5053       break;
5054 
5055     case PROP_DEPTH: /* XXX:2.0 - remove */
5056       clutter_actor_set_depth (actor, g_value_get_float (value));
5057       break;
5058 
5059     case PROP_Z_POSITION:
5060       clutter_actor_set_z_position (actor, g_value_get_float (value));
5061       break;
5062 
5063     case PROP_OPACITY:
5064       clutter_actor_set_opacity (actor, g_value_get_uint (value));
5065       break;
5066 
5067     case PROP_OFFSCREEN_REDIRECT:
5068       clutter_actor_set_offscreen_redirect (actor, g_value_get_enum (value));
5069       break;
5070 
5071     case PROP_NAME:
5072       clutter_actor_set_name (actor, g_value_get_string (value));
5073       break;
5074 
5075     case PROP_VISIBLE:
5076       if (g_value_get_boolean (value) == TRUE)
5077 	clutter_actor_show (actor);
5078       else
5079 	clutter_actor_hide (actor);
5080       break;
5081 
5082     case PROP_PIVOT_POINT:
5083       {
5084         const ClutterPoint *pivot = g_value_get_boxed (value);
5085 
5086         if (pivot == NULL)
5087           pivot = clutter_point_zero ();
5088 
5089         clutter_actor_set_pivot_point (actor, pivot->x, pivot->y);
5090       }
5091       break;
5092 
5093     case PROP_PIVOT_POINT_Z:
5094       clutter_actor_set_pivot_point_z (actor, g_value_get_float (value));
5095       break;
5096 
5097     case PROP_TRANSLATION_X:
5098       clutter_actor_set_translation_factor (actor, CLUTTER_X_AXIS,
5099                                             g_value_get_float (value));
5100       break;
5101 
5102     case PROP_TRANSLATION_Y:
5103       clutter_actor_set_translation_factor (actor, CLUTTER_Y_AXIS,
5104                                             g_value_get_float (value));
5105       break;
5106 
5107     case PROP_TRANSLATION_Z:
5108       clutter_actor_set_translation_factor (actor, CLUTTER_Z_AXIS,
5109                                             g_value_get_float (value));
5110       break;
5111 
5112     case PROP_SCALE_X:
5113       clutter_actor_set_scale_factor (actor, CLUTTER_X_AXIS,
5114                                       g_value_get_double (value));
5115       break;
5116 
5117     case PROP_SCALE_Y:
5118       clutter_actor_set_scale_factor (actor, CLUTTER_Y_AXIS,
5119                                       g_value_get_double (value));
5120       break;
5121 
5122     case PROP_SCALE_Z:
5123       clutter_actor_set_scale_factor (actor, CLUTTER_Z_AXIS,
5124                                       g_value_get_double (value));
5125       break;
5126 
5127     case PROP_SCALE_CENTER_X: /* XXX:2.0 - remove */
5128       clutter_actor_set_scale_center (actor, CLUTTER_X_AXIS,
5129                                       g_value_get_float (value));
5130       break;
5131 
5132     case PROP_SCALE_CENTER_Y: /* XXX:2.0 - remove */
5133       clutter_actor_set_scale_center (actor, CLUTTER_Y_AXIS,
5134                                       g_value_get_float (value));
5135       break;
5136 
5137     case PROP_SCALE_GRAVITY: /* XXX:2.0 - remove */
5138       clutter_actor_set_scale_gravity (actor, g_value_get_enum (value));
5139       break;
5140 
5141     case PROP_CLIP: /* XXX:2.0 - remove */
5142       {
5143         const ClutterGeometry *geom = g_value_get_boxed (value);
5144 
5145 	clutter_actor_set_clip (actor,
5146 				geom->x, geom->y,
5147 				geom->width, geom->height);
5148       }
5149       break;
5150 
5151     case PROP_CLIP_RECT:
5152       clutter_actor_set_clip_rect (actor, g_value_get_boxed (value));
5153       break;
5154 
5155     case PROP_CLIP_TO_ALLOCATION:
5156       clutter_actor_set_clip_to_allocation (actor, g_value_get_boolean (value));
5157       break;
5158 
5159     case PROP_REACTIVE:
5160       clutter_actor_set_reactive (actor, g_value_get_boolean (value));
5161       break;
5162 
5163     case PROP_ROTATION_ANGLE_X:
5164       clutter_actor_set_rotation_angle (actor,
5165                                         CLUTTER_X_AXIS,
5166                                         g_value_get_double (value));
5167       break;
5168 
5169     case PROP_ROTATION_ANGLE_Y:
5170       clutter_actor_set_rotation_angle (actor,
5171                                         CLUTTER_Y_AXIS,
5172                                         g_value_get_double (value));
5173       break;
5174 
5175     case PROP_ROTATION_ANGLE_Z:
5176       clutter_actor_set_rotation_angle (actor,
5177                                         CLUTTER_Z_AXIS,
5178                                         g_value_get_double (value));
5179       break;
5180 
5181     case PROP_ROTATION_CENTER_X: /* XXX:2.0 - remove */
5182       clutter_actor_set_rotation_center_internal (actor,
5183                                                   CLUTTER_X_AXIS,
5184                                                   g_value_get_boxed (value));
5185       break;
5186 
5187     case PROP_ROTATION_CENTER_Y: /* XXX:2.0 - remove */
5188       clutter_actor_set_rotation_center_internal (actor,
5189                                                   CLUTTER_Y_AXIS,
5190                                                   g_value_get_boxed (value));
5191       break;
5192 
5193     case PROP_ROTATION_CENTER_Z: /* XXX:2.0 - remove */
5194       clutter_actor_set_rotation_center_internal (actor,
5195                                                   CLUTTER_Z_AXIS,
5196                                                   g_value_get_boxed (value));
5197       break;
5198 
5199     case PROP_ROTATION_CENTER_Z_GRAVITY: /* XXX:2.0 - remove */
5200       {
5201         const ClutterTransformInfo *info;
5202 
5203         info = _clutter_actor_get_transform_info_or_defaults (actor);
5204         clutter_actor_set_z_rotation_from_gravity (actor, info->rz_angle,
5205                                                    g_value_get_enum (value));
5206       }
5207       break;
5208 
5209     case PROP_ANCHOR_X: /* XXX:2.0 - remove */
5210       clutter_actor_set_anchor_coord (actor, CLUTTER_X_AXIS,
5211                                       g_value_get_float (value));
5212       break;
5213 
5214     case PROP_ANCHOR_Y: /* XXX:2.0 - remove */
5215       clutter_actor_set_anchor_coord (actor, CLUTTER_Y_AXIS,
5216                                       g_value_get_float (value));
5217       break;
5218 
5219     case PROP_ANCHOR_GRAVITY: /* XXX:2.0 - remove */
5220       clutter_actor_set_anchor_point_from_gravity (actor,
5221                                                    g_value_get_enum (value));
5222       break;
5223 
5224     case PROP_TRANSFORM:
5225       clutter_actor_set_transform (actor, g_value_get_boxed (value));
5226       break;
5227 
5228     case PROP_CHILD_TRANSFORM:
5229       clutter_actor_set_child_transform (actor, g_value_get_boxed (value));
5230       break;
5231 
5232     case PROP_SHOW_ON_SET_PARENT: /* XXX:2.0 - remove */
5233       priv->show_on_set_parent = g_value_get_boolean (value);
5234       break;
5235 
5236     case PROP_TEXT_DIRECTION:
5237       clutter_actor_set_text_direction (actor, g_value_get_enum (value));
5238       break;
5239 
5240     case PROP_ACTIONS:
5241       clutter_actor_add_action (actor, g_value_get_object (value));
5242       break;
5243 
5244     case PROP_CONSTRAINTS:
5245       clutter_actor_add_constraint (actor, g_value_get_object (value));
5246       break;
5247 
5248     case PROP_EFFECT:
5249       clutter_actor_add_effect (actor, g_value_get_object (value));
5250       break;
5251 
5252     case PROP_LAYOUT_MANAGER:
5253       clutter_actor_set_layout_manager (actor, g_value_get_object (value));
5254       break;
5255 
5256     case PROP_X_EXPAND:
5257       clutter_actor_set_x_expand (actor, g_value_get_boolean (value));
5258       break;
5259 
5260     case PROP_Y_EXPAND:
5261       clutter_actor_set_y_expand (actor, g_value_get_boolean (value));
5262       break;
5263 
5264     case PROP_X_ALIGN:
5265       clutter_actor_set_x_align (actor, g_value_get_enum (value));
5266       break;
5267 
5268     case PROP_Y_ALIGN:
5269       clutter_actor_set_y_align (actor, g_value_get_enum (value));
5270       break;
5271 
5272     case PROP_MARGIN_TOP:
5273       clutter_actor_set_margin_top (actor, g_value_get_float (value));
5274       break;
5275 
5276     case PROP_MARGIN_BOTTOM:
5277       clutter_actor_set_margin_bottom (actor, g_value_get_float (value));
5278       break;
5279 
5280     case PROP_MARGIN_LEFT:
5281       clutter_actor_set_margin_left (actor, g_value_get_float (value));
5282       break;
5283 
5284     case PROP_MARGIN_RIGHT:
5285       clutter_actor_set_margin_right (actor, g_value_get_float (value));
5286       break;
5287 
5288     case PROP_BACKGROUND_COLOR:
5289       clutter_actor_set_background_color (actor, g_value_get_boxed (value));
5290       break;
5291 
5292     case PROP_CONTENT:
5293       clutter_actor_set_content (actor, g_value_get_object (value));
5294       break;
5295 
5296     case PROP_CONTENT_GRAVITY:
5297       clutter_actor_set_content_gravity (actor, g_value_get_enum (value));
5298       break;
5299 
5300     case PROP_MINIFICATION_FILTER:
5301       clutter_actor_set_content_scaling_filters (actor,
5302                                                  g_value_get_enum (value),
5303                                                  actor->priv->mag_filter);
5304       break;
5305 
5306     case PROP_MAGNIFICATION_FILTER:
5307       clutter_actor_set_content_scaling_filters (actor,
5308                                                  actor->priv->min_filter,
5309                                                  g_value_get_enum (value));
5310       break;
5311 
5312     case PROP_CONTENT_REPEAT:
5313       clutter_actor_set_content_repeat (actor, g_value_get_flags (value));
5314       break;
5315 
5316     default:
5317       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5318       break;
5319     }
5320 }
5321 
5322 static void
clutter_actor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)5323 clutter_actor_get_property (GObject    *object,
5324 			    guint       prop_id,
5325 			    GValue     *value,
5326 			    GParamSpec *pspec)
5327 {
5328   ClutterActor *actor = CLUTTER_ACTOR (object);
5329   ClutterActorPrivate *priv = actor->priv;
5330 
5331   switch (prop_id)
5332     {
5333     case PROP_X:
5334       g_value_set_float (value, clutter_actor_get_x (actor));
5335       break;
5336 
5337     case PROP_Y:
5338       g_value_set_float (value, clutter_actor_get_y (actor));
5339       break;
5340 
5341     case PROP_POSITION:
5342       {
5343         ClutterPoint position;
5344 
5345         clutter_point_init (&position,
5346                             clutter_actor_get_x (actor),
5347                             clutter_actor_get_y (actor));
5348         g_value_set_boxed (value, &position);
5349       }
5350       break;
5351 
5352     case PROP_WIDTH:
5353       g_value_set_float (value, clutter_actor_get_width (actor));
5354       break;
5355 
5356     case PROP_HEIGHT:
5357       g_value_set_float (value, clutter_actor_get_height (actor));
5358       break;
5359 
5360     case PROP_SIZE:
5361       {
5362         ClutterSize size;
5363 
5364         clutter_size_init (&size,
5365                            clutter_actor_get_width (actor),
5366                            clutter_actor_get_height (actor));
5367         g_value_set_boxed (value, &size);
5368       }
5369       break;
5370 
5371     case PROP_FIXED_X:
5372       {
5373         const ClutterLayoutInfo *info;
5374 
5375         info = _clutter_actor_get_layout_info_or_defaults (actor);
5376         g_value_set_float (value, info->fixed_pos.x);
5377       }
5378       break;
5379 
5380     case PROP_FIXED_Y:
5381       {
5382         const ClutterLayoutInfo *info;
5383 
5384         info = _clutter_actor_get_layout_info_or_defaults (actor);
5385         g_value_set_float (value, info->fixed_pos.y);
5386       }
5387       break;
5388 
5389     case PROP_FIXED_POSITION_SET:
5390       g_value_set_boolean (value, priv->position_set);
5391       break;
5392 
5393     case PROP_MIN_WIDTH:
5394       {
5395         const ClutterLayoutInfo *info;
5396 
5397         info = _clutter_actor_get_layout_info_or_defaults (actor);
5398         g_value_set_float (value, info->minimum.width);
5399       }
5400       break;
5401 
5402     case PROP_MIN_HEIGHT:
5403       {
5404         const ClutterLayoutInfo *info;
5405 
5406         info = _clutter_actor_get_layout_info_or_defaults (actor);
5407         g_value_set_float (value, info->minimum.height);
5408       }
5409       break;
5410 
5411     case PROP_NATURAL_WIDTH:
5412       {
5413         const ClutterLayoutInfo *info;
5414 
5415         info = _clutter_actor_get_layout_info_or_defaults (actor);
5416         g_value_set_float (value, info->natural.width);
5417       }
5418       break;
5419 
5420     case PROP_NATURAL_HEIGHT:
5421       {
5422         const ClutterLayoutInfo *info;
5423 
5424         info = _clutter_actor_get_layout_info_or_defaults (actor);
5425         g_value_set_float (value, info->natural.height);
5426       }
5427       break;
5428 
5429     case PROP_MIN_WIDTH_SET:
5430       g_value_set_boolean (value, priv->min_width_set);
5431       break;
5432 
5433     case PROP_MIN_HEIGHT_SET:
5434       g_value_set_boolean (value, priv->min_height_set);
5435       break;
5436 
5437     case PROP_NATURAL_WIDTH_SET:
5438       g_value_set_boolean (value, priv->natural_width_set);
5439       break;
5440 
5441     case PROP_NATURAL_HEIGHT_SET:
5442       g_value_set_boolean (value, priv->natural_height_set);
5443       break;
5444 
5445     case PROP_REQUEST_MODE:
5446       g_value_set_enum (value, priv->request_mode);
5447       break;
5448 
5449     case PROP_ALLOCATION:
5450       g_value_set_boxed (value, &priv->allocation);
5451       break;
5452 
5453     case PROP_DEPTH: /* XXX:2.0 - remove */
5454       g_value_set_float (value, clutter_actor_get_depth (actor));
5455       break;
5456 
5457     case PROP_Z_POSITION:
5458       g_value_set_float (value, clutter_actor_get_z_position (actor));
5459       break;
5460 
5461     case PROP_OPACITY:
5462       g_value_set_uint (value, priv->opacity);
5463       break;
5464 
5465     case PROP_OFFSCREEN_REDIRECT:
5466       g_value_set_flags (value, priv->offscreen_redirect);
5467       break;
5468 
5469     case PROP_NAME:
5470       g_value_set_string (value, priv->name);
5471       break;
5472 
5473     case PROP_VISIBLE:
5474       g_value_set_boolean (value, CLUTTER_ACTOR_IS_VISIBLE (actor));
5475       break;
5476 
5477     case PROP_MAPPED:
5478       g_value_set_boolean (value, CLUTTER_ACTOR_IS_MAPPED (actor));
5479       break;
5480 
5481     case PROP_REALIZED:
5482       g_value_set_boolean (value, CLUTTER_ACTOR_IS_REALIZED (actor));
5483       break;
5484 
5485     case PROP_HAS_CLIP:
5486       g_value_set_boolean (value, priv->has_clip);
5487       break;
5488 
5489     case PROP_CLIP: /* XXX:2.0 - remove */
5490       {
5491         ClutterGeometry clip;
5492 
5493         clip.x      = CLUTTER_NEARBYINT (priv->clip.origin.x);
5494         clip.y      = CLUTTER_NEARBYINT (priv->clip.origin.y);
5495         clip.width  = CLUTTER_NEARBYINT (priv->clip.size.width);
5496         clip.height = CLUTTER_NEARBYINT (priv->clip.size.height);
5497 
5498         g_value_set_boxed (value, &clip);
5499       }
5500       break;
5501 
5502     case PROP_CLIP_RECT:
5503       g_value_set_boxed (value, &priv->clip);
5504       break;
5505 
5506     case PROP_CLIP_TO_ALLOCATION:
5507       g_value_set_boolean (value, priv->clip_to_allocation);
5508       break;
5509 
5510     case PROP_PIVOT_POINT:
5511       {
5512         const ClutterTransformInfo *info;
5513 
5514         info = _clutter_actor_get_transform_info_or_defaults (actor);
5515         g_value_set_boxed (value, &info->pivot);
5516       }
5517       break;
5518 
5519     case PROP_PIVOT_POINT_Z:
5520       {
5521         const ClutterTransformInfo *info;
5522 
5523         info = _clutter_actor_get_transform_info_or_defaults (actor);
5524         g_value_set_float (value, info->pivot_z);
5525       }
5526       break;
5527 
5528     case PROP_TRANSLATION_X:
5529       {
5530         const ClutterTransformInfo *info;
5531 
5532         info = _clutter_actor_get_transform_info_or_defaults (actor);
5533         g_value_set_float (value, info->translation.x);
5534       }
5535       break;
5536 
5537     case PROP_TRANSLATION_Y:
5538       {
5539         const ClutterTransformInfo *info;
5540 
5541         info = _clutter_actor_get_transform_info_or_defaults (actor);
5542         g_value_set_float (value, info->translation.y);
5543       }
5544       break;
5545 
5546     case PROP_TRANSLATION_Z:
5547       {
5548         const ClutterTransformInfo *info;
5549 
5550         info = _clutter_actor_get_transform_info_or_defaults (actor);
5551         g_value_set_float (value, info->translation.z);
5552       }
5553       break;
5554 
5555     case PROP_SCALE_X:
5556       {
5557         const ClutterTransformInfo *info;
5558 
5559         info = _clutter_actor_get_transform_info_or_defaults (actor);
5560         g_value_set_double (value, info->scale_x);
5561       }
5562       break;
5563 
5564     case PROP_SCALE_Y:
5565       {
5566         const ClutterTransformInfo *info;
5567 
5568         info = _clutter_actor_get_transform_info_or_defaults (actor);
5569         g_value_set_double (value, info->scale_y);
5570       }
5571       break;
5572 
5573     case PROP_SCALE_Z:
5574       {
5575         const ClutterTransformInfo *info;
5576 
5577         info = _clutter_actor_get_transform_info_or_defaults (actor);
5578         g_value_set_double (value, info->scale_z);
5579       }
5580       break;
5581 
5582     case PROP_SCALE_CENTER_X: /* XXX:2.0 - remove */
5583       {
5584         gfloat center;
5585 
5586         clutter_actor_get_scale_center (actor, &center, NULL);
5587 
5588         g_value_set_float (value, center);
5589       }
5590       break;
5591 
5592     case PROP_SCALE_CENTER_Y: /* XXX:2.0 - remove */
5593       {
5594         gfloat center;
5595 
5596         clutter_actor_get_scale_center (actor, NULL, &center);
5597 
5598         g_value_set_float (value, center);
5599       }
5600       break;
5601 
5602     case PROP_SCALE_GRAVITY: /* XXX:2.0 - remove */
5603       g_value_set_enum (value, clutter_actor_get_scale_gravity (actor));
5604       break;
5605 
5606     case PROP_REACTIVE:
5607       g_value_set_boolean (value, clutter_actor_get_reactive (actor));
5608       break;
5609 
5610     case PROP_ROTATION_ANGLE_X:
5611       {
5612         const ClutterTransformInfo *info;
5613 
5614         info = _clutter_actor_get_transform_info_or_defaults (actor);
5615         g_value_set_double (value, info->rx_angle);
5616       }
5617       break;
5618 
5619     case PROP_ROTATION_ANGLE_Y:
5620       {
5621         const ClutterTransformInfo *info;
5622 
5623         info = _clutter_actor_get_transform_info_or_defaults (actor);
5624         g_value_set_double (value, info->ry_angle);
5625       }
5626       break;
5627 
5628     case PROP_ROTATION_ANGLE_Z:
5629       {
5630         const ClutterTransformInfo *info;
5631 
5632         info = _clutter_actor_get_transform_info_or_defaults (actor);
5633         g_value_set_double (value, info->rz_angle);
5634       }
5635       break;
5636 
5637     case PROP_ROTATION_CENTER_X: /* XXX:2.0 - remove */
5638       {
5639         ClutterVertex center;
5640 
5641         clutter_actor_get_rotation (actor, CLUTTER_X_AXIS,
5642                                     &center.x,
5643                                     &center.y,
5644                                     &center.z);
5645 
5646         g_value_set_boxed (value, &center);
5647       }
5648       break;
5649 
5650     case PROP_ROTATION_CENTER_Y: /* XXX:2.0 - remove */
5651       {
5652         ClutterVertex center;
5653 
5654         clutter_actor_get_rotation (actor, CLUTTER_Y_AXIS,
5655                                     &center.x,
5656                                     &center.y,
5657                                     &center.z);
5658 
5659         g_value_set_boxed (value, &center);
5660       }
5661       break;
5662 
5663     case PROP_ROTATION_CENTER_Z: /* XXX:2.0 - remove */
5664       {
5665         ClutterVertex center;
5666 
5667         clutter_actor_get_rotation (actor, CLUTTER_Z_AXIS,
5668                                     &center.x,
5669                                     &center.y,
5670                                     &center.z);
5671 
5672         g_value_set_boxed (value, &center);
5673       }
5674       break;
5675 
5676     case PROP_ROTATION_CENTER_Z_GRAVITY: /* XXX:2.0 - remove */
5677       g_value_set_enum (value, clutter_actor_get_z_rotation_gravity (actor));
5678       break;
5679 
5680     case PROP_ANCHOR_X: /* XXX:2.0 - remove */
5681       {
5682         const ClutterTransformInfo *info;
5683         gfloat anchor_x;
5684 
5685         info = _clutter_actor_get_transform_info_or_defaults (actor);
5686         clutter_anchor_coord_get_units (actor, &info->anchor,
5687                                         &anchor_x,
5688                                         NULL,
5689                                         NULL);
5690         g_value_set_float (value, anchor_x);
5691       }
5692       break;
5693 
5694     case PROP_ANCHOR_Y: /* XXX:2.0 - remove */
5695       {
5696         const ClutterTransformInfo *info;
5697         gfloat anchor_y;
5698 
5699         info = _clutter_actor_get_transform_info_or_defaults (actor);
5700         clutter_anchor_coord_get_units (actor, &info->anchor,
5701                                         NULL,
5702                                         &anchor_y,
5703                                         NULL);
5704         g_value_set_float (value, anchor_y);
5705       }
5706       break;
5707 
5708     case PROP_ANCHOR_GRAVITY: /* XXX:2.0 - remove */
5709       g_value_set_enum (value, clutter_actor_get_anchor_point_gravity (actor));
5710       break;
5711 
5712     case PROP_TRANSFORM:
5713       {
5714         ClutterMatrix m;
5715 
5716         clutter_actor_get_transform (actor, &m);
5717         g_value_set_boxed (value, &m);
5718       }
5719       break;
5720 
5721     case PROP_TRANSFORM_SET:
5722       {
5723         const ClutterTransformInfo *info;
5724 
5725         info = _clutter_actor_get_transform_info_or_defaults (actor);
5726         g_value_set_boolean (value, info->transform_set);
5727       }
5728       break;
5729 
5730     case PROP_CHILD_TRANSFORM:
5731       {
5732         ClutterMatrix m;
5733 
5734         clutter_actor_get_child_transform (actor, &m);
5735         g_value_set_boxed (value, &m);
5736       }
5737       break;
5738 
5739     case PROP_CHILD_TRANSFORM_SET:
5740       {
5741         const ClutterTransformInfo *info;
5742 
5743         info = _clutter_actor_get_transform_info_or_defaults (actor);
5744         g_value_set_boolean (value, info->child_transform_set);
5745       }
5746       break;
5747 
5748     case PROP_SHOW_ON_SET_PARENT: /* XXX:2.0 - remove */
5749       g_value_set_boolean (value, priv->show_on_set_parent);
5750       break;
5751 
5752     case PROP_TEXT_DIRECTION:
5753       g_value_set_enum (value, priv->text_direction);
5754       break;
5755 
5756     case PROP_HAS_POINTER:
5757       g_value_set_boolean (value, priv->has_pointer);
5758       break;
5759 
5760     case PROP_LAYOUT_MANAGER:
5761       g_value_set_object (value, priv->layout_manager);
5762       break;
5763 
5764     case PROP_X_EXPAND:
5765       {
5766         const ClutterLayoutInfo *info;
5767 
5768         info = _clutter_actor_get_layout_info_or_defaults (actor);
5769         g_value_set_boolean (value, info->x_expand);
5770       }
5771       break;
5772 
5773     case PROP_Y_EXPAND:
5774       {
5775         const ClutterLayoutInfo *info;
5776 
5777         info = _clutter_actor_get_layout_info_or_defaults (actor);
5778         g_value_set_boolean (value, info->y_expand);
5779       }
5780       break;
5781 
5782     case PROP_X_ALIGN:
5783       {
5784         const ClutterLayoutInfo *info;
5785 
5786         info = _clutter_actor_get_layout_info_or_defaults (actor);
5787         g_value_set_enum (value, info->x_align);
5788       }
5789       break;
5790 
5791     case PROP_Y_ALIGN:
5792       {
5793         const ClutterLayoutInfo *info;
5794 
5795         info = _clutter_actor_get_layout_info_or_defaults (actor);
5796         g_value_set_enum (value, info->y_align);
5797       }
5798       break;
5799 
5800     case PROP_MARGIN_TOP:
5801       {
5802         const ClutterLayoutInfo *info;
5803 
5804         info = _clutter_actor_get_layout_info_or_defaults (actor);
5805         g_value_set_float (value, info->margin.top);
5806       }
5807       break;
5808 
5809     case PROP_MARGIN_BOTTOM:
5810       {
5811         const ClutterLayoutInfo *info;
5812 
5813         info = _clutter_actor_get_layout_info_or_defaults (actor);
5814         g_value_set_float (value, info->margin.bottom);
5815       }
5816       break;
5817 
5818     case PROP_MARGIN_LEFT:
5819       {
5820         const ClutterLayoutInfo *info;
5821 
5822         info = _clutter_actor_get_layout_info_or_defaults (actor);
5823         g_value_set_float (value, info->margin.left);
5824       }
5825       break;
5826 
5827     case PROP_MARGIN_RIGHT:
5828       {
5829         const ClutterLayoutInfo *info;
5830 
5831         info = _clutter_actor_get_layout_info_or_defaults (actor);
5832         g_value_set_float (value, info->margin.right);
5833       }
5834       break;
5835 
5836     case PROP_BACKGROUND_COLOR_SET:
5837       g_value_set_boolean (value, priv->bg_color_set);
5838       break;
5839 
5840     case PROP_BACKGROUND_COLOR:
5841       g_value_set_boxed (value, &priv->bg_color);
5842       break;
5843 
5844     case PROP_FIRST_CHILD:
5845       g_value_set_object (value, priv->first_child);
5846       break;
5847 
5848     case PROP_LAST_CHILD:
5849       g_value_set_object (value, priv->last_child);
5850       break;
5851 
5852     case PROP_CONTENT:
5853       g_value_set_object (value, priv->content);
5854       break;
5855 
5856     case PROP_CONTENT_GRAVITY:
5857       g_value_set_enum (value, priv->content_gravity);
5858       break;
5859 
5860     case PROP_CONTENT_BOX:
5861       {
5862         ClutterActorBox box = { 0, };
5863 
5864         clutter_actor_get_content_box (actor, &box);
5865         g_value_set_boxed (value, &box);
5866       }
5867       break;
5868 
5869     case PROP_MINIFICATION_FILTER:
5870       g_value_set_enum (value, priv->min_filter);
5871       break;
5872 
5873     case PROP_MAGNIFICATION_FILTER:
5874       g_value_set_enum (value, priv->mag_filter);
5875       break;
5876 
5877     case PROP_CONTENT_REPEAT:
5878       g_value_set_flags (value, priv->content_repeat);
5879       break;
5880 
5881     default:
5882       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5883       break;
5884     }
5885 }
5886 
5887 static void
clutter_actor_dispose(GObject * object)5888 clutter_actor_dispose (GObject *object)
5889 {
5890   ClutterActor *self = CLUTTER_ACTOR (object);
5891   ClutterActorPrivate *priv = self->priv;
5892 
5893   CLUTTER_NOTE (MISC, "Dispose actor (name='%s', ref_count:%d) of type '%s'",
5894 		_clutter_actor_get_debug_name (self),
5895                 object->ref_count,
5896 		g_type_name (G_OBJECT_TYPE (self)));
5897 
5898   g_signal_emit (self, actor_signals[DESTROY], 0);
5899 
5900   /* avoid recursing when called from clutter_actor_destroy() */
5901   if (priv->parent != NULL)
5902     {
5903       ClutterActor *parent = priv->parent;
5904 
5905       /* go through the Container implementation unless this
5906        * is an internal child and has been marked as such.
5907        *
5908        * removing the actor from its parent will reset the
5909        * realized and mapped states.
5910        */
5911       if (!CLUTTER_ACTOR_IS_INTERNAL_CHILD (self))
5912         clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self);
5913       else
5914         clutter_actor_remove_child_internal (parent, self,
5915                                              REMOVE_CHILD_LEGACY_FLAGS);
5916     }
5917 
5918   /* parent must be gone at this point */
5919   g_assert (priv->parent == NULL);
5920 
5921   if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
5922     {
5923       /* can't be mapped or realized with no parent */
5924       g_assert (!CLUTTER_ACTOR_IS_MAPPED (self));
5925       g_assert (!CLUTTER_ACTOR_IS_REALIZED (self));
5926     }
5927 
5928   g_clear_object (&priv->pango_context);
5929   g_clear_object (&priv->actions);
5930   g_clear_object (&priv->constraints);
5931   g_clear_object (&priv->effects);
5932   g_clear_object (&priv->flatten_effect);
5933 
5934   if (priv->child_model != NULL)
5935     {
5936       if (priv->create_child_notify != NULL)
5937         priv->create_child_notify (priv->create_child_data);
5938 
5939       priv->create_child_func = NULL;
5940       priv->create_child_data = NULL;
5941       priv->create_child_notify = NULL;
5942 
5943       g_clear_object (&priv->child_model);
5944     }
5945 
5946   if (priv->layout_manager != NULL)
5947     {
5948       clutter_layout_manager_set_container (priv->layout_manager, NULL);
5949       g_clear_object (&priv->layout_manager);
5950     }
5951 
5952   if (priv->content != NULL)
5953     {
5954       _clutter_content_detached (priv->content, self);
5955       g_clear_object (&priv->content);
5956     }
5957 
5958   if (priv->clones != NULL)
5959     {
5960       g_hash_table_unref (priv->clones);
5961       priv->clones = NULL;
5962     }
5963 
5964   G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object);
5965 }
5966 
5967 static void
clutter_actor_finalize(GObject * object)5968 clutter_actor_finalize (GObject *object)
5969 {
5970   ClutterActorPrivate *priv = CLUTTER_ACTOR (object)->priv;
5971 
5972   CLUTTER_NOTE (MISC, "Finalize actor (name='%s') of type '%s'",
5973                 _clutter_actor_get_debug_name ((ClutterActor *) object),
5974                 g_type_name (G_OBJECT_TYPE (object)));
5975 
5976   g_free (priv->name);
5977 
5978 #ifdef CLUTTER_ENABLE_DEBUG
5979   g_free (priv->debug_name);
5980 #endif
5981 
5982   G_OBJECT_CLASS (clutter_actor_parent_class)->finalize (object);
5983 }
5984 
5985 
5986 /**
5987  * clutter_actor_get_accessible:
5988  * @self: a #ClutterActor
5989  *
5990  * Returns the accessible object that describes the actor to an
5991  * assistive technology.
5992  *
5993  * If no class-specific #AtkObject implementation is available for the
5994  * actor instance in question, it will inherit an #AtkObject
5995  * implementation from the first ancestor class for which such an
5996  * implementation is defined.
5997  *
5998  * The documentation of the <ulink
5999  * url="http://developer.gnome.org/doc/API/2.0/atk/index.html">ATK</ulink>
6000  * library contains more information about accessible objects and
6001  * their uses.
6002  *
6003  * Returns: (transfer none): the #AtkObject associated with @actor
6004  */
6005 AtkObject *
clutter_actor_get_accessible(ClutterActor * self)6006 clutter_actor_get_accessible (ClutterActor *self)
6007 {
6008   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
6009 
6010   return CLUTTER_ACTOR_GET_CLASS (self)->get_accessible (self);
6011 }
6012 
6013 static AtkObject *
clutter_actor_real_get_accessible(ClutterActor * actor)6014 clutter_actor_real_get_accessible (ClutterActor *actor)
6015 {
6016   return atk_gobject_accessible_for_object (G_OBJECT (actor));
6017 }
6018 
6019 static AtkObject *
_clutter_actor_ref_accessible(AtkImplementor * implementor)6020 _clutter_actor_ref_accessible (AtkImplementor *implementor)
6021 {
6022   AtkObject *accessible;
6023 
6024   accessible = clutter_actor_get_accessible (CLUTTER_ACTOR (implementor));
6025   if (accessible != NULL)
6026     g_object_ref (accessible);
6027 
6028   return accessible;
6029 }
6030 
6031 static void
atk_implementor_iface_init(AtkImplementorIface * iface)6032 atk_implementor_iface_init (AtkImplementorIface *iface)
6033 {
6034   iface->ref_accessible = _clutter_actor_ref_accessible;
6035 }
6036 
6037 static gboolean
clutter_actor_update_default_paint_volume(ClutterActor * self,ClutterPaintVolume * volume)6038 clutter_actor_update_default_paint_volume (ClutterActor       *self,
6039                                            ClutterPaintVolume *volume)
6040 {
6041   ClutterActorPrivate *priv = self->priv;
6042   gboolean res = TRUE;
6043 
6044   /* this should be checked before we call this function, but it's a
6045    * good idea to be explicit when it costs us nothing
6046    */
6047   if (priv->needs_allocation)
6048     return FALSE;
6049 
6050   /* we start from the allocation */
6051   clutter_paint_volume_set_width (volume,
6052                                   priv->allocation.x2 - priv->allocation.x1);
6053   clutter_paint_volume_set_height (volume,
6054                                    priv->allocation.y2 - priv->allocation.y1);
6055 
6056   /* if the actor has a clip set then we have a pretty definite
6057    * size for the paint volume: the actor cannot possibly paint
6058    * outside the clip region.
6059    */
6060   if (priv->clip_to_allocation)
6061     {
6062       /* the allocation has already been set, so we just flip the
6063        * return value
6064        */
6065       res = TRUE;
6066     }
6067   else
6068     {
6069       ClutterActor *child;
6070 
6071       if (priv->has_clip &&
6072           priv->clip.size.width >= 0 &&
6073           priv->clip.size.height >= 0)
6074         {
6075           ClutterVertex origin;
6076 
6077           origin.x = priv->clip.origin.x;
6078           origin.y = priv->clip.origin.y;
6079           origin.z = 0;
6080 
6081           clutter_paint_volume_set_origin (volume, &origin);
6082           clutter_paint_volume_set_width (volume, priv->clip.size.width);
6083           clutter_paint_volume_set_height (volume, priv->clip.size.height);
6084 
6085           res = TRUE;
6086         }
6087 
6088       /* if we don't have children we just bail out here... */
6089       if (priv->n_children == 0)
6090         return res;
6091 
6092       /* ...but if we have children then we ask for their paint volume in
6093        * our coordinates. if any of our children replies that it doesn't
6094        * have a paint volume, we bail out
6095        */
6096       for (child = priv->first_child;
6097            child != NULL;
6098            child = child->priv->next_sibling)
6099         {
6100           const ClutterPaintVolume *child_volume;
6101 
6102           /* we ignore unmapped children, since they won't be painted.
6103            *
6104            * XXX: we also have to ignore mapped children without a valid
6105            * allocation, because apparently some code above Clutter allows
6106            * them.
6107            */
6108           if (!CLUTTER_ACTOR_IS_MAPPED (child) || !clutter_actor_has_allocation (child))
6109             continue;
6110 
6111           child_volume = clutter_actor_get_transformed_paint_volume (child, self);
6112           if (child_volume == NULL)
6113             {
6114               res = FALSE;
6115               break;
6116             }
6117 
6118           clutter_paint_volume_union (volume, child_volume);
6119           res = TRUE;
6120         }
6121     }
6122 
6123   return res;
6124 
6125 }
6126 
6127 static gboolean
clutter_actor_real_get_paint_volume(ClutterActor * self,ClutterPaintVolume * volume)6128 clutter_actor_real_get_paint_volume (ClutterActor       *self,
6129                                      ClutterPaintVolume *volume)
6130 {
6131   ClutterActorClass *klass;
6132   gboolean res;
6133 
6134   klass = CLUTTER_ACTOR_GET_CLASS (self);
6135 
6136   /* XXX - this thoroughly sucks, but we don't want to penalize users
6137    * who use ClutterActor as a "new ClutterGroup" by forcing a full-stage
6138    * redraw. This should go away in 2.0.
6139    */
6140   if (klass->paint == clutter_actor_real_paint &&
6141       klass->get_paint_volume == clutter_actor_real_get_paint_volume)
6142     {
6143       res = TRUE;
6144     }
6145   else
6146     {
6147       /* this is the default return value: we cannot know if a class
6148        * is going to paint outside its allocation, so we take the
6149        * conservative approach.
6150        */
6151       res = FALSE;
6152     }
6153 
6154   /* update_default_paint_volume() should only fail if one of the children
6155    * reported an invalid, or no, paint volume
6156    */
6157   if (!clutter_actor_update_default_paint_volume (self, volume))
6158     return FALSE;
6159 
6160   return res;
6161 }
6162 
6163 /**
6164  * clutter_actor_get_default_paint_volume:
6165  * @self: a #ClutterActor
6166  *
6167  * Retrieves the default paint volume for @self.
6168  *
6169  * This function provides the same #ClutterPaintVolume that would be
6170  * computed by the default implementation inside #ClutterActor of the
6171  * #ClutterActorClass.get_paint_volume() virtual function.
6172  *
6173  * This function should only be used by #ClutterActor subclasses that
6174  * cannot chain up to the parent implementation when computing their
6175  * paint volume.
6176  *
6177  * Return value: (transfer none): a pointer to the default
6178  *   #ClutterPaintVolume, relative to the #ClutterActor, or %NULL if
6179  *   the actor could not compute a valid paint volume. The returned value
6180  *   is not guaranteed to be stable across multiple frames, so if you
6181  *   want to retain it, you will need to copy it using
6182  *   clutter_paint_volume_copy().
6183  *
6184  * Since: 1.10
6185  */
6186 const ClutterPaintVolume *
clutter_actor_get_default_paint_volume(ClutterActor * self)6187 clutter_actor_get_default_paint_volume (ClutterActor *self)
6188 {
6189   ClutterPaintVolume volume;
6190   ClutterPaintVolume *res;
6191 
6192   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
6193 
6194   res = NULL;
6195   _clutter_paint_volume_init_static (&volume, self);
6196   if (clutter_actor_update_default_paint_volume (self, &volume))
6197     {
6198       ClutterActor *stage = _clutter_actor_get_stage_internal (self);
6199 
6200       if (stage != NULL)
6201         {
6202           res = _clutter_stage_paint_volume_stack_allocate (CLUTTER_STAGE (stage));
6203           _clutter_paint_volume_copy_static (&volume, res);
6204         }
6205     }
6206 
6207   clutter_paint_volume_free (&volume);
6208 
6209   return res;
6210 }
6211 
6212 static gboolean
clutter_actor_real_has_overlaps(ClutterActor * self)6213 clutter_actor_real_has_overlaps (ClutterActor *self)
6214 {
6215   /* By default we'll assume that all actors need an offscreen redirect to get
6216    * the correct opacity. Actors such as ClutterTexture that would never need
6217    * an offscreen redirect can override this to return FALSE. */
6218   return TRUE;
6219 }
6220 
6221 static void
clutter_actor_real_destroy(ClutterActor * actor)6222 clutter_actor_real_destroy (ClutterActor *actor)
6223 {
6224   ClutterActorIter iter;
6225 
6226   g_object_freeze_notify (G_OBJECT (actor));
6227 
6228   clutter_actor_iter_init (&iter, actor);
6229   while (clutter_actor_iter_next (&iter, NULL))
6230     clutter_actor_iter_destroy (&iter);
6231 
6232   g_object_thaw_notify (G_OBJECT (actor));
6233 }
6234 
6235 static GObject *
clutter_actor_constructor(GType gtype,guint n_props,GObjectConstructParam * props)6236 clutter_actor_constructor (GType gtype,
6237                            guint n_props,
6238                            GObjectConstructParam *props)
6239 {
6240   GObjectClass *gobject_class;
6241   ClutterActor *self;
6242   GObject *retval;
6243 
6244   gobject_class = G_OBJECT_CLASS (clutter_actor_parent_class);
6245   retval = gobject_class->constructor (gtype, n_props, props);
6246   self = CLUTTER_ACTOR (retval);
6247 
6248   if (self->priv->layout_manager == NULL)
6249     {
6250       ClutterLayoutManager *default_layout;
6251 
6252       CLUTTER_NOTE (LAYOUT, "Creating default layout manager");
6253 
6254       default_layout = clutter_fixed_layout_new ();
6255       clutter_actor_set_layout_manager (self, default_layout);
6256     }
6257 
6258   return retval;
6259 }
6260 
6261 static void
clutter_actor_class_init(ClutterActorClass * klass)6262 clutter_actor_class_init (ClutterActorClass *klass)
6263 {
6264   GObjectClass *object_class = G_OBJECT_CLASS (klass);
6265 
6266   quark_shader_data = g_quark_from_static_string ("-clutter-actor-shader-data");
6267   quark_actor_layout_info = g_quark_from_static_string ("-clutter-actor-layout-info");
6268   quark_actor_transform_info = g_quark_from_static_string ("-clutter-actor-transform-info");
6269   quark_actor_animation_info = g_quark_from_static_string ("-clutter-actor-animation-info");
6270 
6271   object_class->constructor = clutter_actor_constructor;
6272   object_class->set_property = clutter_actor_set_property;
6273   object_class->get_property = clutter_actor_get_property;
6274   object_class->dispose = clutter_actor_dispose;
6275   object_class->finalize = clutter_actor_finalize;
6276 
6277   klass->show = clutter_actor_real_show;
6278   klass->show_all = clutter_actor_show;
6279   klass->hide = clutter_actor_real_hide;
6280   klass->hide_all = clutter_actor_hide;
6281   klass->map = clutter_actor_real_map;
6282   klass->unmap = clutter_actor_real_unmap;
6283   klass->unrealize = clutter_actor_real_unrealize;
6284   klass->pick = clutter_actor_real_pick;
6285   klass->get_preferred_width = clutter_actor_real_get_preferred_width;
6286   klass->get_preferred_height = clutter_actor_real_get_preferred_height;
6287   klass->allocate = clutter_actor_real_allocate;
6288   klass->queue_redraw = clutter_actor_real_queue_redraw;
6289   klass->queue_relayout = clutter_actor_real_queue_relayout;
6290   klass->apply_transform = clutter_actor_real_apply_transform;
6291   klass->get_accessible = clutter_actor_real_get_accessible;
6292   klass->get_paint_volume = clutter_actor_real_get_paint_volume;
6293   klass->has_overlaps = clutter_actor_real_has_overlaps;
6294   klass->paint = clutter_actor_real_paint;
6295   klass->destroy = clutter_actor_real_destroy;
6296 
6297   /**
6298    * ClutterActor:x:
6299    *
6300    * X coordinate of the actor in pixels. If written, forces a fixed
6301    * position for the actor. If read, returns the fixed position if any,
6302    * otherwise the allocation if available, otherwise 0.
6303    *
6304    * The #ClutterActor:x property is animatable.
6305    */
6306   obj_props[PROP_X] =
6307     g_param_spec_float ("x",
6308                         P_("X coordinate"),
6309                         P_("X coordinate of the actor"),
6310                         -G_MAXFLOAT, G_MAXFLOAT,
6311                         0.0,
6312                         G_PARAM_READWRITE |
6313                         G_PARAM_STATIC_STRINGS |
6314                         CLUTTER_PARAM_ANIMATABLE);
6315 
6316   /**
6317    * ClutterActor:y:
6318    *
6319    * Y coordinate of the actor in pixels. If written, forces a fixed
6320    * position for the actor.  If read, returns the fixed position if
6321    * any, otherwise the allocation if available, otherwise 0.
6322    *
6323    * The #ClutterActor:y property is animatable.
6324    */
6325   obj_props[PROP_Y] =
6326     g_param_spec_float ("y",
6327                         P_("Y coordinate"),
6328                         P_("Y coordinate of the actor"),
6329                         -G_MAXFLOAT, G_MAXFLOAT,
6330                         0.0,
6331                         G_PARAM_READWRITE |
6332                         G_PARAM_STATIC_STRINGS |
6333                         CLUTTER_PARAM_ANIMATABLE);
6334 
6335   /**
6336    * ClutterActor:position:
6337    *
6338    * The position of the origin of the actor.
6339    *
6340    * This property is a shorthand for setting and getting the
6341    * #ClutterActor:x and #ClutterActor:y properties at the same
6342    * time.
6343    *
6344    * The #ClutterActor:position property is animatable.
6345    *
6346    * Since: 1.12
6347    */
6348   obj_props[PROP_POSITION] =
6349     g_param_spec_boxed ("position",
6350                         P_("Position"),
6351                         P_("The position of the origin of the actor"),
6352                         CLUTTER_TYPE_POINT,
6353                         G_PARAM_READWRITE |
6354                         G_PARAM_STATIC_STRINGS |
6355                         CLUTTER_PARAM_ANIMATABLE);
6356 
6357   /**
6358    * ClutterActor:width:
6359    *
6360    * Width of the actor (in pixels). If written, forces the minimum and
6361    * natural size request of the actor to the given width. If read, returns
6362    * the allocated width if available, otherwise the width request.
6363    *
6364    * The #ClutterActor:width property is animatable.
6365    */
6366   obj_props[PROP_WIDTH] =
6367     g_param_spec_float ("width",
6368                         P_("Width"),
6369                         P_("Width of the actor"),
6370                         0.0, G_MAXFLOAT,
6371                         0.0,
6372                         G_PARAM_READWRITE |
6373                         G_PARAM_STATIC_STRINGS |
6374                         CLUTTER_PARAM_ANIMATABLE);
6375 
6376   /**
6377    * ClutterActor:height:
6378    *
6379    * Height of the actor (in pixels).  If written, forces the minimum and
6380    * natural size request of the actor to the given height. If read, returns
6381    * the allocated height if available, otherwise the height request.
6382    *
6383    * The #ClutterActor:height property is animatable.
6384    */
6385   obj_props[PROP_HEIGHT] =
6386     g_param_spec_float ("height",
6387                         P_("Height"),
6388                         P_("Height of the actor"),
6389                         0.0, G_MAXFLOAT,
6390                         0.0,
6391                         G_PARAM_READWRITE |
6392                         G_PARAM_STATIC_STRINGS |
6393                         CLUTTER_PARAM_ANIMATABLE);
6394 
6395   /**
6396    * ClutterActor:size:
6397    *
6398    * The size of the actor.
6399    *
6400    * This property is a shorthand for setting and getting the
6401    * #ClutterActor:width and #ClutterActor:height at the same time.
6402    *
6403    * The #ClutterActor:size property is animatable.
6404    *
6405    * Since: 1.12
6406    */
6407   obj_props[PROP_SIZE] =
6408     g_param_spec_boxed ("size",
6409                         P_("Size"),
6410                         P_("The size of the actor"),
6411                         CLUTTER_TYPE_SIZE,
6412                         G_PARAM_READWRITE |
6413                         G_PARAM_STATIC_STRINGS |
6414                         CLUTTER_PARAM_ANIMATABLE);
6415 
6416   /**
6417    * ClutterActor:fixed-x:
6418    *
6419    * The fixed X position of the actor in pixels.
6420    *
6421    * Writing this property sets #ClutterActor:fixed-position-set
6422    * property as well, as a side effect
6423    *
6424    * Since: 0.8
6425    */
6426   obj_props[PROP_FIXED_X] =
6427     g_param_spec_float ("fixed-x",
6428                         P_("Fixed X"),
6429                         P_("Forced X position of the actor"),
6430                         -G_MAXFLOAT, G_MAXFLOAT,
6431                         0.0,
6432                         CLUTTER_PARAM_READWRITE);
6433 
6434   /**
6435    * ClutterActor:fixed-y:
6436    *
6437    * The fixed Y position of the actor in pixels.
6438    *
6439    * Writing this property sets the #ClutterActor:fixed-position-set
6440    * property as well, as a side effect
6441    *
6442    * Since: 0.8
6443    */
6444   obj_props[PROP_FIXED_Y] =
6445     g_param_spec_float ("fixed-y",
6446                         P_("Fixed Y"),
6447                         P_("Forced Y position of the actor"),
6448                         -G_MAXFLOAT, G_MAXFLOAT,
6449                         0,
6450                         CLUTTER_PARAM_READWRITE);
6451 
6452   /**
6453    * ClutterActor:fixed-position-set:
6454    *
6455    * This flag controls whether the #ClutterActor:fixed-x and
6456    * #ClutterActor:fixed-y properties are used
6457    *
6458    * Since: 0.8
6459    */
6460   obj_props[PROP_FIXED_POSITION_SET] =
6461     g_param_spec_boolean ("fixed-position-set",
6462                           P_("Fixed position set"),
6463                           P_("Whether to use fixed positioning for the actor"),
6464                           FALSE,
6465                           CLUTTER_PARAM_READWRITE);
6466 
6467   /**
6468    * ClutterActor:min-width:
6469    *
6470    * A forced minimum width request for the actor, in pixels
6471    *
6472    * Writing this property sets the #ClutterActor:min-width-set property
6473    * as well, as a side effect.
6474    *
6475    *This property overrides the usual width request of the actor.
6476    *
6477    * Since: 0.8
6478    */
6479   obj_props[PROP_MIN_WIDTH] =
6480     g_param_spec_float ("min-width",
6481                         P_("Min Width"),
6482                         P_("Forced minimum width request for the actor"),
6483                         0.0, G_MAXFLOAT,
6484                         0.0,
6485                         CLUTTER_PARAM_READWRITE);
6486 
6487   /**
6488    * ClutterActor:min-height:
6489    *
6490    * A forced minimum height request for the actor, in pixels
6491    *
6492    * Writing this property sets the #ClutterActor:min-height-set property
6493    * as well, as a side effect. This property overrides the usual height
6494    * request of the actor.
6495    *
6496    * Since: 0.8
6497    */
6498   obj_props[PROP_MIN_HEIGHT] =
6499     g_param_spec_float ("min-height",
6500                         P_("Min Height"),
6501                         P_("Forced minimum height request for the actor"),
6502                         0.0, G_MAXFLOAT,
6503                         0.0,
6504                         CLUTTER_PARAM_READWRITE);
6505 
6506   /**
6507    * ClutterActor:natural-width:
6508    *
6509    * A forced natural width request for the actor, in pixels
6510    *
6511    * Writing this property sets the #ClutterActor:natural-width-set
6512    * property as well, as a side effect. This property overrides the
6513    * usual width request of the actor
6514    *
6515    * Since: 0.8
6516    */
6517   obj_props[PROP_NATURAL_WIDTH] =
6518     g_param_spec_float ("natural-width",
6519                         P_("Natural Width"),
6520                         P_("Forced natural width request for the actor"),
6521                         0.0, G_MAXFLOAT,
6522                         0.0,
6523                         CLUTTER_PARAM_READWRITE);
6524 
6525   /**
6526    * ClutterActor:natural-height:
6527    *
6528    * A forced natural height request for the actor, in pixels
6529    *
6530    * Writing this property sets the #ClutterActor:natural-height-set
6531    * property as well, as a side effect. This property overrides the
6532    * usual height request of the actor
6533    *
6534    * Since: 0.8
6535    */
6536   obj_props[PROP_NATURAL_HEIGHT] =
6537     g_param_spec_float ("natural-height",
6538                         P_("Natural Height"),
6539                         P_("Forced natural height request for the actor"),
6540                         0.0, G_MAXFLOAT,
6541                         0.0,
6542                         CLUTTER_PARAM_READWRITE);
6543 
6544   /**
6545    * ClutterActor:min-width-set:
6546    *
6547    * This flag controls whether the #ClutterActor:min-width property
6548    * is used
6549    *
6550    * Since: 0.8
6551    */
6552   obj_props[PROP_MIN_WIDTH_SET] =
6553     g_param_spec_boolean ("min-width-set",
6554                           P_("Minimum width set"),
6555                           P_("Whether to use the min-width property"),
6556                           FALSE,
6557                           CLUTTER_PARAM_READWRITE);
6558 
6559   /**
6560    * ClutterActor:min-height-set:
6561    *
6562    * This flag controls whether the #ClutterActor:min-height property
6563    * is used
6564    *
6565    * Since: 0.8
6566    */
6567   obj_props[PROP_MIN_HEIGHT_SET] =
6568     g_param_spec_boolean ("min-height-set",
6569                           P_("Minimum height set"),
6570                           P_("Whether to use the min-height property"),
6571                           FALSE,
6572                           CLUTTER_PARAM_READWRITE);
6573 
6574   /**
6575    * ClutterActor:natural-width-set:
6576    *
6577    * This flag controls whether the #ClutterActor:natural-width property
6578    * is used
6579    *
6580    * Since: 0.8
6581    */
6582   obj_props[PROP_NATURAL_WIDTH_SET] =
6583     g_param_spec_boolean ("natural-width-set",
6584                           P_("Natural width set"),
6585                           P_("Whether to use the natural-width property"),
6586                           FALSE,
6587                           CLUTTER_PARAM_READWRITE);
6588 
6589   /**
6590    * ClutterActor:natural-height-set:
6591    *
6592    * This flag controls whether the #ClutterActor:natural-height property
6593    * is used
6594    *
6595    * Since: 0.8
6596    */
6597   obj_props[PROP_NATURAL_HEIGHT_SET] =
6598     g_param_spec_boolean ("natural-height-set",
6599                           P_("Natural height set"),
6600                           P_("Whether to use the natural-height property"),
6601                           FALSE,
6602                           CLUTTER_PARAM_READWRITE);
6603 
6604   /**
6605    * ClutterActor:allocation:
6606    *
6607    * The allocation for the actor, in pixels
6608    *
6609    * This is property is read-only, but you might monitor it to know when an
6610    * actor moves or resizes
6611    *
6612    * Since: 0.8
6613    */
6614   obj_props[PROP_ALLOCATION] =
6615     g_param_spec_boxed ("allocation",
6616                         P_("Allocation"),
6617                         P_("The actor’s allocation"),
6618                         CLUTTER_TYPE_ACTOR_BOX,
6619                         G_PARAM_READABLE |
6620                         G_PARAM_STATIC_STRINGS |
6621                         CLUTTER_PARAM_ANIMATABLE);
6622 
6623   /**
6624    * ClutterActor:request-mode:
6625    *
6626    * Request mode for the #ClutterActor. The request mode determines the
6627    * type of geometry management used by the actor, either height for width
6628    * (the default) or width for height.
6629    *
6630    * For actors implementing height for width, the parent container should get
6631    * the preferred width first, and then the preferred height for that width.
6632    *
6633    * For actors implementing width for height, the parent container should get
6634    * the preferred height first, and then the preferred width for that height.
6635    *
6636    * For instance:
6637    *
6638    * |[<!-- language="C" -->
6639    *   ClutterRequestMode mode;
6640    *   gfloat natural_width, min_width;
6641    *   gfloat natural_height, min_height;
6642    *
6643    *   mode = clutter_actor_get_request_mode (child);
6644    *   if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
6645    *     {
6646    *       clutter_actor_get_preferred_width (child, -1,
6647    *                                          &min_width,
6648    *                                          &natural_width);
6649    *       clutter_actor_get_preferred_height (child, natural_width,
6650    *                                           &min_height,
6651    *                                           &natural_height);
6652    *     }
6653    *   else if (mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
6654    *     {
6655    *       clutter_actor_get_preferred_height (child, -1,
6656    *                                           &min_height,
6657    *                                           &natural_height);
6658    *       clutter_actor_get_preferred_width (child, natural_height,
6659    *                                          &min_width,
6660    *                                          &natural_width);
6661    *     }
6662    *   else if (mode == CLUTTER_REQUEST_CONTENT_SIZE)
6663    *     {
6664    *       ClutterContent *content = clutter_actor_get_content (child);
6665    *
6666    *       min_width, min_height = 0;
6667    *       natural_width = natural_height = 0;
6668    *
6669    *       if (content != NULL)
6670    *         clutter_content_get_preferred_size (content, &natural_width, &natural_height);
6671    *     }
6672    * ]|
6673    *
6674    * will retrieve the minimum and natural width and height depending on the
6675    * preferred request mode of the #ClutterActor "child".
6676    *
6677    * The clutter_actor_get_preferred_size() function will implement this
6678    * check for you.
6679    *
6680    * Since: 0.8
6681    */
6682   obj_props[PROP_REQUEST_MODE] =
6683     g_param_spec_enum ("request-mode",
6684                        P_("Request Mode"),
6685                        P_("The actor’s request mode"),
6686                        CLUTTER_TYPE_REQUEST_MODE,
6687                        CLUTTER_REQUEST_HEIGHT_FOR_WIDTH,
6688                        CLUTTER_PARAM_READWRITE);
6689 
6690   /**
6691    * ClutterActor:depth:
6692    *
6693    * The position of the actor on the Z axis.
6694    *
6695    * The #ClutterActor:depth property is relative to the parent's
6696    * modelview matrix.
6697    *
6698    * Setting this property will call #ClutterContainerIface.sort_depth_order()
6699    * which is usually a no-op, and it's most likely not what you want.
6700    *
6701    * The #ClutterActor:depth property is animatable.
6702    *
6703    * Since: 0.6
6704    *
6705    * Deprecated: 1.12: Use #ClutterActor:z-position instead.
6706    */
6707   obj_props[PROP_DEPTH] =
6708     g_param_spec_float ("depth",
6709                         P_("Depth"),
6710                         P_("Position on the Z axis"),
6711                         -G_MAXFLOAT, G_MAXFLOAT,
6712                         0.0,
6713                         G_PARAM_READWRITE |
6714                         G_PARAM_STATIC_STRINGS |
6715                         G_PARAM_DEPRECATED |
6716                         CLUTTER_PARAM_ANIMATABLE);
6717 
6718   /**
6719    * ClutterActor:z-position:
6720    *
6721    * The actor's position on the Z axis, relative to the parent's
6722    * transformations.
6723    *
6724    * Positive values will bring the actor's position nearer to the user,
6725    * whereas negative values will bring the actor's position farther from
6726    * the user.
6727    *
6728    * The #ClutterActor:z-position does not affect the paint or allocation
6729    * order.
6730    *
6731    * The #ClutterActor:z-position property is animatable.
6732    *
6733    * Since: 1.12
6734    */
6735   obj_props[PROP_Z_POSITION] =
6736     g_param_spec_float ("z-position",
6737                         P_("Z Position"),
6738                         P_("The actor’s position on the Z axis"),
6739                         -G_MAXFLOAT, G_MAXFLOAT,
6740                         0.0f,
6741                         G_PARAM_READWRITE |
6742                         G_PARAM_STATIC_STRINGS |
6743                         CLUTTER_PARAM_ANIMATABLE);
6744 
6745   /**
6746    * ClutterActor:opacity:
6747    *
6748    * Opacity of an actor, between 0 (fully transparent) and
6749    * 255 (fully opaque)
6750    *
6751    * The #ClutterActor:opacity property is animatable.
6752    */
6753   obj_props[PROP_OPACITY] =
6754     g_param_spec_uint ("opacity",
6755                        P_("Opacity"),
6756                        P_("Opacity of an actor"),
6757                        0, 255,
6758                        255,
6759                        G_PARAM_READWRITE |
6760                        G_PARAM_STATIC_STRINGS |
6761                        CLUTTER_PARAM_ANIMATABLE);
6762 
6763   /**
6764    * ClutterActor:offscreen-redirect:
6765    *
6766    * Determines the conditions in which the actor will be redirected
6767    * to an offscreen framebuffer while being painted. For example this
6768    * can be used to cache an actor in a framebuffer or for improved
6769    * handling of transparent actors. See
6770    * clutter_actor_set_offscreen_redirect() for details.
6771    *
6772    * Since: 1.8
6773    */
6774   obj_props[PROP_OFFSCREEN_REDIRECT] =
6775     g_param_spec_flags ("offscreen-redirect",
6776                         P_("Offscreen redirect"),
6777                         P_("Flags controlling when to flatten the actor into a single image"),
6778                         CLUTTER_TYPE_OFFSCREEN_REDIRECT,
6779                         0,
6780                         CLUTTER_PARAM_READWRITE);
6781 
6782   /**
6783    * ClutterActor:visible:
6784    *
6785    * Whether the actor is set to be visible or not
6786    *
6787    * See also #ClutterActor:mapped
6788    */
6789   obj_props[PROP_VISIBLE] =
6790     g_param_spec_boolean ("visible",
6791                           P_("Visible"),
6792                           P_("Whether the actor is visible or not"),
6793                           FALSE,
6794                           CLUTTER_PARAM_READWRITE);
6795 
6796   /**
6797    * ClutterActor:mapped:
6798    *
6799    * Whether the actor is mapped (will be painted when the stage
6800    * to which it belongs is mapped)
6801    *
6802    * Since: 1.0
6803    */
6804   obj_props[PROP_MAPPED] =
6805     g_param_spec_boolean ("mapped",
6806                           P_("Mapped"),
6807                           P_("Whether the actor will be painted"),
6808                           FALSE,
6809                           CLUTTER_PARAM_READABLE);
6810 
6811   /**
6812    * ClutterActor:realized:
6813    *
6814    * Whether the actor has been realized
6815    *
6816    * Since: 1.0
6817    */
6818   obj_props[PROP_REALIZED] =
6819     g_param_spec_boolean ("realized",
6820                           P_("Realized"),
6821                           P_("Whether the actor has been realized"),
6822                           FALSE,
6823                           CLUTTER_PARAM_READABLE);
6824 
6825   /**
6826    * ClutterActor:reactive:
6827    *
6828    * Whether the actor is reactive to events or not
6829    *
6830    * Only reactive actors will emit event-related signals
6831    *
6832    * Since: 0.6
6833    */
6834   obj_props[PROP_REACTIVE] =
6835     g_param_spec_boolean ("reactive",
6836                           P_("Reactive"),
6837                           P_("Whether the actor is reactive to events"),
6838                           FALSE,
6839                           CLUTTER_PARAM_READWRITE);
6840 
6841   /**
6842    * ClutterActor:has-clip:
6843    *
6844    * Whether the actor has the #ClutterActor:clip property set or not
6845    */
6846   obj_props[PROP_HAS_CLIP] =
6847     g_param_spec_boolean ("has-clip",
6848                           P_("Has Clip"),
6849                           P_("Whether the actor has a clip set"),
6850                           FALSE,
6851                           CLUTTER_PARAM_READABLE);
6852 
6853   /**
6854    * ClutterActor:clip:
6855    *
6856    * The visible region of the actor, in actor-relative coordinates
6857    *
6858    * Deprecated: 1.12: Use #ClutterActor:clip-rect instead.
6859    */
6860   obj_props[PROP_CLIP] = /* XXX:2.0 - remove */
6861     g_param_spec_boxed ("clip",
6862                         P_("Clip"),
6863                         P_("The clip region for the actor"),
6864                         CLUTTER_TYPE_GEOMETRY,
6865                         CLUTTER_PARAM_READWRITE);
6866 
6867   /**
6868    * ClutterActor:clip-rect:
6869    *
6870    * The visible region of the actor, in actor-relative coordinates,
6871    * expressed as a #ClutterRect.
6872    *
6873    * Setting this property to %NULL will unset the existing clip.
6874    *
6875    * Setting this property will change the #ClutterActor:has-clip
6876    * property as a side effect.
6877    *
6878    * Since: 1.12
6879    */
6880   obj_props[PROP_CLIP_RECT] =
6881     g_param_spec_boxed ("clip-rect",
6882                         P_("Clip Rectangle"),
6883                         P_("The visible region of the actor"),
6884                         CLUTTER_TYPE_RECT,
6885                         G_PARAM_READWRITE |
6886                         G_PARAM_STATIC_STRINGS);
6887 
6888   /**
6889    * ClutterActor:name:
6890    *
6891    * The name of the actor
6892    *
6893    * Since: 0.2
6894    */
6895   obj_props[PROP_NAME] =
6896     g_param_spec_string ("name",
6897                          P_("Name"),
6898                          P_("Name of the actor"),
6899                          NULL,
6900                          CLUTTER_PARAM_READWRITE);
6901 
6902   /**
6903    * ClutterActor:pivot-point:
6904    *
6905    * The point around which the scaling and rotation transformations occur.
6906    *
6907    * The pivot point is expressed in normalized coordinates space, with (0, 0)
6908    * being the top left corner of the actor and (1, 1) the bottom right corner
6909    * of the actor.
6910    *
6911    * The default pivot point is located at (0, 0).
6912    *
6913    * The #ClutterActor:pivot-point property is animatable.
6914    *
6915    * Since: 1.12
6916    */
6917   obj_props[PROP_PIVOT_POINT] =
6918     g_param_spec_boxed ("pivot-point",
6919                         P_("Pivot Point"),
6920                         P_("The point around which the scaling and rotation occur"),
6921                         CLUTTER_TYPE_POINT,
6922                         G_PARAM_READWRITE |
6923                         G_PARAM_STATIC_STRINGS |
6924                         CLUTTER_PARAM_ANIMATABLE);
6925 
6926   /**
6927    * ClutterActor:pivot-point-z:
6928    *
6929    * The Z component of the #ClutterActor:pivot-point, expressed as a value
6930    * along the Z axis.
6931    *
6932    * The #ClutterActor:pivot-point-z property is animatable.
6933    *
6934    * Since: 1.12
6935    */
6936   obj_props[PROP_PIVOT_POINT_Z] =
6937     g_param_spec_float ("pivot-point-z",
6938                         P_("Pivot Point Z"),
6939                         P_("Z component of the pivot point"),
6940                         -G_MAXFLOAT, G_MAXFLOAT,
6941                         0.f,
6942                         G_PARAM_READWRITE |
6943                         G_PARAM_STATIC_STRINGS |
6944                         CLUTTER_PARAM_ANIMATABLE);
6945 
6946   /**
6947    * ClutterActor:scale-x:
6948    *
6949    * The horizontal scale of the actor.
6950    *
6951    * The #ClutterActor:scale-x property is animatable.
6952    *
6953    * Since: 0.6
6954    */
6955   obj_props[PROP_SCALE_X] =
6956     g_param_spec_double ("scale-x",
6957                          P_("Scale X"),
6958                          P_("Scale factor on the X axis"),
6959                          -G_MAXDOUBLE, G_MAXDOUBLE,
6960                          1.0,
6961                          G_PARAM_READWRITE |
6962                          G_PARAM_STATIC_STRINGS |
6963                          CLUTTER_PARAM_ANIMATABLE);
6964 
6965   /**
6966    * ClutterActor:scale-y:
6967    *
6968    * The vertical scale of the actor.
6969    *
6970    * The #ClutterActor:scale-y property is animatable.
6971    *
6972    * Since: 0.6
6973    */
6974   obj_props[PROP_SCALE_Y] =
6975     g_param_spec_double ("scale-y",
6976                          P_("Scale Y"),
6977                          P_("Scale factor on the Y axis"),
6978                          -G_MAXDOUBLE, G_MAXDOUBLE,
6979                          1.0,
6980                          G_PARAM_READWRITE |
6981                          G_PARAM_STATIC_STRINGS |
6982                          CLUTTER_PARAM_ANIMATABLE);
6983 
6984   /**
6985    * ClutterActor:scale-z:
6986    *
6987    * The scale factor of the actor along the Z axis.
6988    *
6989    * The #ClutterActor:scale-y property is animatable.
6990    *
6991    * Since: 1.12
6992    */
6993   obj_props[PROP_SCALE_Z] =
6994     g_param_spec_double ("scale-z",
6995                          P_("Scale Z"),
6996                          P_("Scale factor on the Z axis"),
6997                          -G_MAXDOUBLE, G_MAXDOUBLE,
6998                          1.0,
6999                          G_PARAM_READWRITE |
7000                          G_PARAM_STATIC_STRINGS |
7001                          CLUTTER_PARAM_ANIMATABLE);
7002 
7003   /**
7004    * ClutterActor:scale-center-x:
7005    *
7006    * The horizontal center point for scaling
7007    *
7008    * Since: 1.0
7009    *
7010    * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7011    */
7012   obj_props[PROP_SCALE_CENTER_X] = /* XXX:2.0 - remove */
7013     g_param_spec_float ("scale-center-x",
7014                         P_("Scale Center X"),
7015                         P_("Horizontal scale center"),
7016                         -G_MAXFLOAT, G_MAXFLOAT,
7017                         0.0,
7018                         G_PARAM_READWRITE |
7019                         G_PARAM_STATIC_STRINGS |
7020                         G_PARAM_DEPRECATED);
7021 
7022   /**
7023    * ClutterActor:scale-center-y:
7024    *
7025    * The vertical center point for scaling
7026    *
7027    * Since: 1.0
7028    *
7029    * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7030    */
7031   obj_props[PROP_SCALE_CENTER_Y] = /* XXX:2.0 - remove */
7032     g_param_spec_float ("scale-center-y",
7033                         P_("Scale Center Y"),
7034                         P_("Vertical scale center"),
7035                         -G_MAXFLOAT, G_MAXFLOAT,
7036                         0.0,
7037                         G_PARAM_READWRITE |
7038                         G_PARAM_STATIC_STRINGS |
7039                         G_PARAM_DEPRECATED);
7040 
7041   /**
7042    * ClutterActor:scale-gravity:
7043    *
7044    * The center point for scaling expressed as a #ClutterGravity
7045    *
7046    * Since: 1.0
7047    *
7048    * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7049    */
7050   obj_props[PROP_SCALE_GRAVITY] = /* XXX:2.0 - remove */
7051     g_param_spec_enum ("scale-gravity",
7052                        P_("Scale Gravity"),
7053                        P_("The center of scaling"),
7054                        CLUTTER_TYPE_GRAVITY,
7055                        CLUTTER_GRAVITY_NONE,
7056                        G_PARAM_READWRITE |
7057                        G_PARAM_STATIC_STRINGS |
7058                        G_PARAM_DEPRECATED);
7059 
7060   /**
7061    * ClutterActor:rotation-angle-x:
7062    *
7063    * The rotation angle on the X axis.
7064    *
7065    * The #ClutterActor:rotation-angle-x property is animatable.
7066    *
7067    * Since: 0.6
7068    */
7069   obj_props[PROP_ROTATION_ANGLE_X] =
7070     g_param_spec_double ("rotation-angle-x",
7071                          P_("Rotation Angle X"),
7072                          P_("The rotation angle on the X axis"),
7073                          -G_MAXDOUBLE, G_MAXDOUBLE,
7074                          0.0,
7075                          G_PARAM_READWRITE |
7076                          G_PARAM_STATIC_STRINGS |
7077                          CLUTTER_PARAM_ANIMATABLE);
7078 
7079   /**
7080    * ClutterActor:rotation-angle-y:
7081    *
7082    * The rotation angle on the Y axis
7083    *
7084    * The #ClutterActor:rotation-angle-y property is animatable.
7085    *
7086    * Since: 0.6
7087    */
7088   obj_props[PROP_ROTATION_ANGLE_Y] =
7089     g_param_spec_double ("rotation-angle-y",
7090                          P_("Rotation Angle Y"),
7091                          P_("The rotation angle on the Y axis"),
7092                          -G_MAXDOUBLE, G_MAXDOUBLE,
7093                          0.0,
7094                          G_PARAM_READWRITE |
7095                          G_PARAM_STATIC_STRINGS |
7096                          CLUTTER_PARAM_ANIMATABLE);
7097 
7098   /**
7099    * ClutterActor:rotation-angle-z:
7100    *
7101    * The rotation angle on the Z axis
7102    *
7103    * The #ClutterActor:rotation-angle-z property is animatable.
7104    *
7105    * Since: 0.6
7106    */
7107   obj_props[PROP_ROTATION_ANGLE_Z] =
7108     g_param_spec_double ("rotation-angle-z",
7109                          P_("Rotation Angle Z"),
7110                          P_("The rotation angle on the Z axis"),
7111                          -G_MAXDOUBLE, G_MAXDOUBLE,
7112                          0.0,
7113                          G_PARAM_READWRITE |
7114                          G_PARAM_STATIC_STRINGS |
7115                          CLUTTER_PARAM_ANIMATABLE);
7116 
7117   /**
7118    * ClutterActor:rotation-center-x:
7119    *
7120    * The rotation center on the X axis.
7121    *
7122    * Since: 0.6
7123    *
7124    * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7125    */
7126   obj_props[PROP_ROTATION_CENTER_X] = /* XXX:2.0 - remove */
7127     g_param_spec_boxed ("rotation-center-x",
7128                         P_("Rotation Center X"),
7129                         P_("The rotation center on the X axis"),
7130                         CLUTTER_TYPE_VERTEX,
7131                         G_PARAM_READWRITE |
7132                         G_PARAM_STATIC_STRINGS |
7133                         G_PARAM_DEPRECATED);
7134 
7135   /**
7136    * ClutterActor:rotation-center-y:
7137    *
7138    * The rotation center on the Y axis.
7139    *
7140    * Since: 0.6
7141    *
7142    * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7143    */
7144   obj_props[PROP_ROTATION_CENTER_Y] = /* XXX:2.0 - remove */
7145     g_param_spec_boxed ("rotation-center-y",
7146                         P_("Rotation Center Y"),
7147                         P_("The rotation center on the Y axis"),
7148                         CLUTTER_TYPE_VERTEX,
7149                         G_PARAM_READWRITE |
7150                         G_PARAM_STATIC_STRINGS |
7151                         G_PARAM_DEPRECATED);
7152 
7153   /**
7154    * ClutterActor:rotation-center-z:
7155    *
7156    * The rotation center on the Z axis.
7157    *
7158    * Since: 0.6
7159    *
7160    * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7161    */
7162   obj_props[PROP_ROTATION_CENTER_Z] = /* XXX:2.0 - remove */
7163     g_param_spec_boxed ("rotation-center-z",
7164                         P_("Rotation Center Z"),
7165                         P_("The rotation center on the Z axis"),
7166                         CLUTTER_TYPE_VERTEX,
7167                         G_PARAM_READWRITE |
7168                         G_PARAM_STATIC_STRINGS |
7169                         G_PARAM_DEPRECATED);
7170 
7171   /**
7172    * ClutterActor:rotation-center-z-gravity:
7173    *
7174    * The rotation center on the Z axis expressed as a #ClutterGravity.
7175    *
7176    * Since: 1.0
7177    *
7178    * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7179    */
7180   obj_props[PROP_ROTATION_CENTER_Z_GRAVITY] = /* XXX:2.0 - remove */
7181     g_param_spec_enum ("rotation-center-z-gravity",
7182                        P_("Rotation Center Z Gravity"),
7183                        P_("Center point for rotation around the Z axis"),
7184                        CLUTTER_TYPE_GRAVITY,
7185                        CLUTTER_GRAVITY_NONE,
7186                        G_PARAM_READWRITE |
7187                        G_PARAM_STATIC_STRINGS |
7188                        G_PARAM_DEPRECATED);
7189 
7190   /**
7191    * ClutterActor:anchor-x:
7192    *
7193    * The X coordinate of an actor's anchor point, relative to
7194    * the actor coordinate space, in pixels.
7195    *
7196    * It is highly recommended not to use #ClutterActor:anchor-x,
7197    * #ClutterActor:anchor-y, and #ClutterActor:anchor-gravity in newly
7198    * written code; the anchor point adds an additional translation that
7199    * will affect the actor's relative position with regards to its
7200    * parent, as well as the position of its children. This change needs
7201    * to always be taken into account when positioning the actor. It is
7202    * recommended to use the #ClutterActor:pivot-point property instead,
7203    * as it will affect only the transformations.
7204    *
7205    * Since: 0.8
7206    *
7207    * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7208    */
7209   obj_props[PROP_ANCHOR_X] = /* XXX:2.0 - remove */
7210     g_param_spec_float ("anchor-x",
7211                         P_("Anchor X"),
7212                         P_("X coordinate of the anchor point"),
7213                         -G_MAXFLOAT, G_MAXFLOAT,
7214                         0,
7215                         G_PARAM_READWRITE |
7216                         G_PARAM_STATIC_STRINGS |
7217                         G_PARAM_DEPRECATED);
7218 
7219   /**
7220    * ClutterActor:anchor-y:
7221    *
7222    * The Y coordinate of an actor's anchor point, relative to
7223    * the actor coordinate space, in pixels
7224    *
7225    * It is highly recommended not to use #ClutterActor:anchor-x,
7226    * #ClutterActor:anchor-y, and #ClutterActor:anchor-gravity in newly
7227    * written code; the anchor point adds an additional translation that
7228    * will affect the actor's relative position with regards to its
7229    * parent, as well as the position of its children. This change needs
7230    * to always be taken into account when positioning the actor. It is
7231    * recommended to use the #ClutterActor:pivot-point property instead,
7232    * as it will affect only the transformations.
7233    *
7234    * Since: 0.8
7235    *
7236    * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7237    */
7238   obj_props[PROP_ANCHOR_Y] = /* XXX:2.0 - remove */
7239     g_param_spec_float ("anchor-y",
7240                         P_("Anchor Y"),
7241                         P_("Y coordinate of the anchor point"),
7242                         -G_MAXFLOAT, G_MAXFLOAT,
7243                         0,
7244                         G_PARAM_READWRITE |
7245                         G_PARAM_STATIC_STRINGS |
7246                         G_PARAM_DEPRECATED);
7247 
7248   /**
7249    * ClutterActor:anchor-gravity:
7250    *
7251    * The anchor point expressed as a #ClutterGravity
7252    *
7253    * It is highly recommended not to use #ClutterActor:anchor-x,
7254    * #ClutterActor:anchor-y, and #ClutterActor:anchor-gravity in newly
7255    * written code; the anchor point adds an additional translation that
7256    * will affect the actor's relative position with regards to its
7257    * parent, as well as the position of its children. This change needs
7258    * to always be taken into account when positioning the actor. It is
7259    * recommended to use the #ClutterActor:pivot-point property instead,
7260    * as it will affect only the transformations.
7261    *
7262    * Since: 1.0
7263    *
7264    * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
7265    */
7266   obj_props[PROP_ANCHOR_GRAVITY] = /* XXX:2.0 - remove */
7267     g_param_spec_enum ("anchor-gravity",
7268                        P_("Anchor Gravity"),
7269                        P_("The anchor point as a ClutterGravity"),
7270                        CLUTTER_TYPE_GRAVITY,
7271                        CLUTTER_GRAVITY_NONE,
7272                        G_PARAM_READWRITE |
7273                        G_PARAM_STATIC_STRINGS |
7274                        G_PARAM_DEPRECATED);
7275 
7276   /**
7277    * ClutterActor:translation-x:
7278    *
7279    * An additional translation applied along the X axis, relative
7280    * to the actor's #ClutterActor:pivot-point.
7281    *
7282    * The #ClutterActor:translation-x property is animatable.
7283    *
7284    * Since: 1.12
7285    */
7286   obj_props[PROP_TRANSLATION_X] =
7287     g_param_spec_float ("translation-x",
7288                         P_("Translation X"),
7289                         P_("Translation along the X axis"),
7290                         -G_MAXFLOAT, G_MAXFLOAT,
7291                         0.f,
7292                         G_PARAM_READWRITE |
7293                         G_PARAM_STATIC_STRINGS |
7294                         CLUTTER_PARAM_ANIMATABLE);
7295 
7296   /**
7297    * ClutterActor:translation-y:
7298    *
7299    * An additional translation applied along the Y axis, relative
7300    * to the actor's #ClutterActor:pivot-point.
7301    *
7302    * The #ClutterActor:translation-y property is animatable.
7303    *
7304    * Since: 1.12
7305    */
7306   obj_props[PROP_TRANSLATION_Y] =
7307     g_param_spec_float ("translation-y",
7308                         P_("Translation Y"),
7309                         P_("Translation along the Y axis"),
7310                         -G_MAXFLOAT, G_MAXFLOAT,
7311                         0.f,
7312                         G_PARAM_READWRITE |
7313                         G_PARAM_STATIC_STRINGS |
7314                         CLUTTER_PARAM_ANIMATABLE);
7315 
7316   /**
7317    * ClutterActor:translation-z:
7318    *
7319    * An additional translation applied along the Z axis, relative
7320    * to the actor's #ClutterActor:pivot-point.
7321    *
7322    * The #ClutterActor:translation-z property is animatable.
7323    *
7324    * Since: 1.12
7325    */
7326   obj_props[PROP_TRANSLATION_Z] =
7327     g_param_spec_float ("translation-z",
7328                         P_("Translation Z"),
7329                         P_("Translation along the Z axis"),
7330                         -G_MAXFLOAT, G_MAXFLOAT,
7331                         0.f,
7332                         G_PARAM_READWRITE |
7333                         G_PARAM_STATIC_STRINGS |
7334                         CLUTTER_PARAM_ANIMATABLE);
7335 
7336   /**
7337    * ClutterActor:transform:
7338    *
7339    * Overrides the transformations of a #ClutterActor with a custom
7340    * matrix.
7341    *
7342    * The matrix specified by the #ClutterActor:transform property is
7343    * applied to the actor and its children relative to the actor's
7344    * #ClutterActor:allocation and #ClutterActor:pivot-point.
7345    *
7346    * Application code should rarely need to use this function directly.
7347    *
7348    * Setting this property with a #ClutterMatrix will set the
7349    * #ClutterActor:transform-set property to %TRUE as a side effect;
7350    * setting this property with %NULL will set the
7351    * #ClutterActor:transform-set property to %FALSE.
7352    *
7353    * The #ClutterActor:transform property is animatable.
7354    *
7355    * Since: 1.12
7356    */
7357   obj_props[PROP_TRANSFORM] =
7358     g_param_spec_boxed ("transform",
7359                         P_("Transform"),
7360                         P_("Transformation matrix"),
7361                         CLUTTER_TYPE_MATRIX,
7362                         G_PARAM_READWRITE |
7363                         G_PARAM_STATIC_STRINGS |
7364                         CLUTTER_PARAM_ANIMATABLE);
7365 
7366   /**
7367    * ClutterActor:transform-set:
7368    *
7369    * Whether the #ClutterActor:transform property is set.
7370    *
7371    * Since: 1.12
7372    */
7373   obj_props[PROP_TRANSFORM_SET] =
7374     g_param_spec_boolean ("transform-set",
7375                           P_("Transform Set"),
7376                           P_("Whether the transform property is set"),
7377                           FALSE,
7378                           G_PARAM_READABLE |
7379                           G_PARAM_STATIC_STRINGS);
7380 
7381   /**
7382    * ClutterActor:child-transform:
7383    *
7384    * Applies a transformation matrix on each child of an actor.
7385    *
7386    * Setting this property with a #ClutterMatrix will set the
7387    * #ClutterActor:child-transform-set property to %TRUE as a side effect;
7388    * setting this property with %NULL will set the
7389    * #ClutterActor:child-transform-set property to %FALSE.
7390    *
7391    * The #ClutterActor:child-transform property is animatable.
7392    *
7393    * Since: 1.12
7394    */
7395   obj_props[PROP_CHILD_TRANSFORM] =
7396     g_param_spec_boxed ("child-transform",
7397                         P_("Child Transform"),
7398                         P_("Children transformation matrix"),
7399                         CLUTTER_TYPE_MATRIX,
7400                         G_PARAM_READWRITE |
7401                         G_PARAM_STATIC_STRINGS |
7402                         CLUTTER_PARAM_ANIMATABLE);
7403 
7404   /**
7405    * ClutterActor:child-transform-set:
7406    *
7407    * Whether the #ClutterActor:child-transform property is set.
7408    *
7409    * Since: 1.12
7410    */
7411   obj_props[PROP_CHILD_TRANSFORM_SET] =
7412     g_param_spec_boolean ("child-transform-set",
7413                           P_("Child Transform Set"),
7414                           P_("Whether the child-transform property is set"),
7415                           FALSE,
7416                           G_PARAM_READABLE |
7417                           G_PARAM_STATIC_STRINGS);
7418 
7419   /**
7420    * ClutterActor:show-on-set-parent:
7421    *
7422    * If %TRUE, the actor is automatically shown when parented.
7423    *
7424    * Calling clutter_actor_hide() on an actor which has not been
7425    * parented will set this property to %FALSE as a side effect.
7426    *
7427    * Since: 0.8
7428    */
7429   obj_props[PROP_SHOW_ON_SET_PARENT] = /* XXX:2.0 - remove */
7430     g_param_spec_boolean ("show-on-set-parent",
7431                           P_("Show on set parent"),
7432                           P_("Whether the actor is shown when parented"),
7433                           TRUE,
7434                           CLUTTER_PARAM_READWRITE);
7435 
7436   /**
7437    * ClutterActor:clip-to-allocation:
7438    *
7439    * Whether the clip region should track the allocated area
7440    * of the actor.
7441    *
7442    * This property is ignored if a clip area has been explicitly
7443    * set using clutter_actor_set_clip().
7444    *
7445    * Since: 1.0
7446    */
7447   obj_props[PROP_CLIP_TO_ALLOCATION] =
7448     g_param_spec_boolean ("clip-to-allocation",
7449                           P_("Clip to Allocation"),
7450                           P_("Sets the clip region to track the actor’s allocation"),
7451                           FALSE,
7452                           CLUTTER_PARAM_READWRITE);
7453 
7454   /**
7455    * ClutterActor:text-direction:
7456    *
7457    * The direction of the text inside a #ClutterActor.
7458    *
7459    * Since: 1.0
7460    */
7461   obj_props[PROP_TEXT_DIRECTION] =
7462     g_param_spec_enum ("text-direction",
7463                        P_("Text Direction"),
7464                        P_("Direction of the text"),
7465                        CLUTTER_TYPE_TEXT_DIRECTION,
7466                        CLUTTER_TEXT_DIRECTION_LTR,
7467                        CLUTTER_PARAM_READWRITE);
7468 
7469   /**
7470    * ClutterActor:has-pointer:
7471    *
7472    * Whether the actor contains the pointer of a #ClutterInputDevice
7473    * or not.
7474    *
7475    * Since: 1.2
7476    */
7477   obj_props[PROP_HAS_POINTER] =
7478     g_param_spec_boolean ("has-pointer",
7479                           P_("Has Pointer"),
7480                           P_("Whether the actor contains the pointer of an input device"),
7481                           FALSE,
7482                           CLUTTER_PARAM_READABLE);
7483 
7484   /**
7485    * ClutterActor:actions:
7486    *
7487    * Adds a #ClutterAction to the actor
7488    *
7489    * Since: 1.4
7490    */
7491   obj_props[PROP_ACTIONS] =
7492     g_param_spec_object ("actions",
7493                          P_("Actions"),
7494                          P_("Adds an action to the actor"),
7495                          CLUTTER_TYPE_ACTION,
7496                          CLUTTER_PARAM_WRITABLE);
7497 
7498   /**
7499    * ClutterActor:constraints:
7500    *
7501    * Adds a #ClutterConstraint to the actor
7502    *
7503    * Since: 1.4
7504    */
7505   obj_props[PROP_CONSTRAINTS] =
7506     g_param_spec_object ("constraints",
7507                          P_("Constraints"),
7508                          P_("Adds a constraint to the actor"),
7509                          CLUTTER_TYPE_CONSTRAINT,
7510                          CLUTTER_PARAM_WRITABLE);
7511 
7512   /**
7513    * ClutterActor:effect:
7514    *
7515    * Adds #ClutterEffect to the list of effects be applied on a #ClutterActor
7516    *
7517    * Since: 1.4
7518    */
7519   obj_props[PROP_EFFECT] =
7520     g_param_spec_object ("effect",
7521                          P_("Effect"),
7522                          P_("Add an effect to be applied on the actor"),
7523                          CLUTTER_TYPE_EFFECT,
7524                          CLUTTER_PARAM_WRITABLE);
7525 
7526   /**
7527    * ClutterActor:layout-manager:
7528    *
7529    * A delegate object for controlling the layout of the children of
7530    * an actor.
7531    *
7532    * Since: 1.10
7533    */
7534   obj_props[PROP_LAYOUT_MANAGER] =
7535     g_param_spec_object ("layout-manager",
7536                          P_("Layout Manager"),
7537                          P_("The object controlling the layout of an actor’s children"),
7538                          CLUTTER_TYPE_LAYOUT_MANAGER,
7539                          CLUTTER_PARAM_READWRITE);
7540 
7541   /**
7542    * ClutterActor:x-expand:
7543    *
7544    * Whether a layout manager should assign more space to the actor on
7545    * the X axis.
7546    *
7547    * Since: 1.12
7548    */
7549   obj_props[PROP_X_EXPAND] =
7550     g_param_spec_boolean ("x-expand",
7551                           P_("X Expand"),
7552                           P_("Whether extra horizontal space should be assigned to the actor"),
7553                           FALSE,
7554                           G_PARAM_READWRITE |
7555                           G_PARAM_STATIC_STRINGS);
7556 
7557   /**
7558    * ClutterActor:y-expand:
7559    *
7560    * Whether a layout manager should assign more space to the actor on
7561    * the Y axis.
7562    *
7563    * Since: 1.12
7564    */
7565   obj_props[PROP_Y_EXPAND] =
7566     g_param_spec_boolean ("y-expand",
7567                           P_("Y Expand"),
7568                           P_("Whether extra vertical space should be assigned to the actor"),
7569                           FALSE,
7570                           G_PARAM_READWRITE |
7571                           G_PARAM_STATIC_STRINGS);
7572 
7573   /**
7574    * ClutterActor:x-align:
7575    *
7576    * The alignment of an actor on the X axis, if the actor has been given
7577    * extra space for its allocation. See also the #ClutterActor:x-expand
7578    * property.
7579    *
7580    * Since: 1.10
7581    */
7582   obj_props[PROP_X_ALIGN] =
7583     g_param_spec_enum ("x-align",
7584                        P_("X Alignment"),
7585                        P_("The alignment of the actor on the X axis within its allocation"),
7586                        CLUTTER_TYPE_ACTOR_ALIGN,
7587                        CLUTTER_ACTOR_ALIGN_FILL,
7588                        CLUTTER_PARAM_READWRITE);
7589 
7590   /**
7591    * ClutterActor:y-align:
7592    *
7593    * The alignment of an actor on the Y axis, if the actor has been given
7594    * extra space for its allocation.
7595    *
7596    * Since: 1.10
7597    */
7598   obj_props[PROP_Y_ALIGN] =
7599     g_param_spec_enum ("y-align",
7600                        P_("Y Alignment"),
7601                        P_("The alignment of the actor on the Y axis within its allocation"),
7602                        CLUTTER_TYPE_ACTOR_ALIGN,
7603                        CLUTTER_ACTOR_ALIGN_FILL,
7604                        CLUTTER_PARAM_READWRITE);
7605 
7606   /**
7607    * ClutterActor:margin-top:
7608    *
7609    * The margin (in pixels) from the top of the actor.
7610    *
7611    * This property adds a margin to the actor's preferred size; the margin
7612    * will be automatically taken into account when allocating the actor.
7613    *
7614    * The #ClutterActor:margin-top property is animatable.
7615    *
7616    * Since: 1.10
7617    */
7618   obj_props[PROP_MARGIN_TOP] =
7619     g_param_spec_float ("margin-top",
7620                         P_("Margin Top"),
7621                         P_("Extra space at the top"),
7622                         0.0, G_MAXFLOAT,
7623                         0.0,
7624                         G_PARAM_READWRITE |
7625                         G_PARAM_STATIC_STRINGS |
7626                         CLUTTER_PARAM_ANIMATABLE);
7627 
7628   /**
7629    * ClutterActor:margin-bottom:
7630    *
7631    * The margin (in pixels) from the bottom of the actor.
7632    *
7633    * This property adds a margin to the actor's preferred size; the margin
7634    * will be automatically taken into account when allocating the actor.
7635    *
7636    * The #ClutterActor:margin-bottom property is animatable.
7637    *
7638    * Since: 1.10
7639    */
7640   obj_props[PROP_MARGIN_BOTTOM] =
7641     g_param_spec_float ("margin-bottom",
7642                         P_("Margin Bottom"),
7643                         P_("Extra space at the bottom"),
7644                         0.0, G_MAXFLOAT,
7645                         0.0,
7646                         G_PARAM_READWRITE |
7647                         G_PARAM_STATIC_STRINGS |
7648                         CLUTTER_PARAM_ANIMATABLE);
7649 
7650   /**
7651    * ClutterActor:margin-left:
7652    *
7653    * The margin (in pixels) from the left of the actor.
7654    *
7655    * This property adds a margin to the actor's preferred size; the margin
7656    * will be automatically taken into account when allocating the actor.
7657    *
7658    * The #ClutterActor:margin-left property is animatable.
7659    *
7660    * Since: 1.10
7661    */
7662   obj_props[PROP_MARGIN_LEFT] =
7663     g_param_spec_float ("margin-left",
7664                         P_("Margin Left"),
7665                         P_("Extra space at the left"),
7666                         0.0, G_MAXFLOAT,
7667                         0.0,
7668                         G_PARAM_READWRITE |
7669                         G_PARAM_STATIC_STRINGS |
7670                         CLUTTER_PARAM_ANIMATABLE);
7671 
7672   /**
7673    * ClutterActor:margin-right:
7674    *
7675    * The margin (in pixels) from the right of the actor.
7676    *
7677    * This property adds a margin to the actor's preferred size; the margin
7678    * will be automatically taken into account when allocating the actor.
7679    *
7680    * The #ClutterActor:margin-right property is animatable.
7681    *
7682    * Since: 1.10
7683    */
7684   obj_props[PROP_MARGIN_RIGHT] =
7685     g_param_spec_float ("margin-right",
7686                         P_("Margin Right"),
7687                         P_("Extra space at the right"),
7688                         0.0, G_MAXFLOAT,
7689                         0.0,
7690                         G_PARAM_READWRITE |
7691                         G_PARAM_STATIC_STRINGS |
7692                         CLUTTER_PARAM_ANIMATABLE);
7693 
7694   /**
7695    * ClutterActor:background-color-set:
7696    *
7697    * Whether the #ClutterActor:background-color property has been set.
7698    *
7699    * Since: 1.10
7700    */
7701   obj_props[PROP_BACKGROUND_COLOR_SET] =
7702     g_param_spec_boolean ("background-color-set",
7703                           P_("Background Color Set"),
7704                           P_("Whether the background color is set"),
7705                           FALSE,
7706                           CLUTTER_PARAM_READABLE);
7707 
7708   /**
7709    * ClutterActor:background-color:
7710    *
7711    * Paints a solid fill of the actor's allocation using the specified
7712    * color.
7713    *
7714    * The #ClutterActor:background-color property is animatable.
7715    *
7716    * Since: 1.10
7717    */
7718   obj_props[PROP_BACKGROUND_COLOR] =
7719     clutter_param_spec_color ("background-color",
7720                               P_("Background color"),
7721                               P_("The actor’s background color"),
7722                               CLUTTER_COLOR_Transparent,
7723                               G_PARAM_READWRITE |
7724                               G_PARAM_STATIC_STRINGS |
7725                               CLUTTER_PARAM_ANIMATABLE);
7726 
7727   /**
7728    * ClutterActor:first-child:
7729    *
7730    * The actor's first child.
7731    *
7732    * Since: 1.10
7733    */
7734   obj_props[PROP_FIRST_CHILD] =
7735     g_param_spec_object ("first-child",
7736                          P_("First Child"),
7737                          P_("The actor’s first child"),
7738                          CLUTTER_TYPE_ACTOR,
7739                          CLUTTER_PARAM_READABLE);
7740 
7741   /**
7742    * ClutterActor:last-child:
7743    *
7744    * The actor's last child.
7745    *
7746    * Since: 1.10
7747    */
7748   obj_props[PROP_LAST_CHILD] =
7749     g_param_spec_object ("last-child",
7750                          P_("Last Child"),
7751                          P_("The actor’s last child"),
7752                          CLUTTER_TYPE_ACTOR,
7753                          CLUTTER_PARAM_READABLE);
7754 
7755   /**
7756    * ClutterActor:content:
7757    *
7758    * The #ClutterContent implementation that controls the content
7759    * of the actor.
7760    *
7761    * Since: 1.10
7762    */
7763   obj_props[PROP_CONTENT] =
7764     g_param_spec_object ("content",
7765                          P_("Content"),
7766                          P_("Delegate object for painting the actor’s content"),
7767                          CLUTTER_TYPE_CONTENT,
7768                          CLUTTER_PARAM_READWRITE);
7769 
7770   /**
7771    * ClutterActor:content-gravity:
7772    *
7773    * The alignment that should be honoured by the #ClutterContent
7774    * set with the #ClutterActor:content property.
7775    *
7776    * Changing the value of this property will change the bounding box of
7777    * the content; you can use the #ClutterActor:content-box property to
7778    * get the position and size of the content within the actor's
7779    * allocation.
7780    *
7781    * This property is meaningful only for #ClutterContent implementations
7782    * that have a preferred size, and if the preferred size is smaller than
7783    * the actor's allocation.
7784    *
7785    * The #ClutterActor:content-gravity property is animatable.
7786    *
7787    * Since: 1.10
7788    */
7789   obj_props[PROP_CONTENT_GRAVITY] =
7790     g_param_spec_enum ("content-gravity",
7791                        P_("Content Gravity"),
7792                        P_("Alignment of the actor’s content"),
7793                        CLUTTER_TYPE_CONTENT_GRAVITY,
7794                        CLUTTER_CONTENT_GRAVITY_RESIZE_FILL,
7795                        CLUTTER_PARAM_READWRITE);
7796 
7797   /**
7798    * ClutterActor:content-box:
7799    *
7800    * The bounding box for the #ClutterContent used by the actor.
7801    *
7802    * The value of this property is controlled by the #ClutterActor:allocation
7803    * and #ClutterActor:content-gravity properties of #ClutterActor.
7804    *
7805    * The bounding box for the content is guaranteed to never exceed the
7806    * allocation's of the actor.
7807    *
7808    * Since: 1.10
7809    */
7810   obj_props[PROP_CONTENT_BOX] =
7811     g_param_spec_boxed ("content-box",
7812                         P_("Content Box"),
7813                         P_("The bounding box of the actor’s content"),
7814                         CLUTTER_TYPE_ACTOR_BOX,
7815                         G_PARAM_READABLE |
7816                         G_PARAM_STATIC_STRINGS |
7817                         CLUTTER_PARAM_ANIMATABLE);
7818 
7819   obj_props[PROP_MINIFICATION_FILTER] =
7820     g_param_spec_enum ("minification-filter",
7821                        P_("Minification Filter"),
7822                        P_("The filter used when reducing the size of the content"),
7823                        CLUTTER_TYPE_SCALING_FILTER,
7824                        CLUTTER_SCALING_FILTER_LINEAR,
7825                        CLUTTER_PARAM_READWRITE);
7826 
7827   obj_props[PROP_MAGNIFICATION_FILTER] =
7828     g_param_spec_enum ("magnification-filter",
7829                        P_("Magnification Filter"),
7830                        P_("The filter used when increasing the size of the content"),
7831                        CLUTTER_TYPE_SCALING_FILTER,
7832                        CLUTTER_SCALING_FILTER_LINEAR,
7833                        CLUTTER_PARAM_READWRITE);
7834 
7835   /**
7836    * ClutterActor:content-repeat:
7837    *
7838    * The repeat policy for the actor's #ClutterActor:content.
7839    *
7840    * Since: 1.12
7841    */
7842   obj_props[PROP_CONTENT_REPEAT] =
7843     g_param_spec_flags ("content-repeat",
7844                         P_("Content Repeat"),
7845                         P_("The repeat policy for the actor’s content"),
7846                         CLUTTER_TYPE_CONTENT_REPEAT,
7847                         CLUTTER_REPEAT_NONE,
7848                         G_PARAM_READWRITE |
7849                         G_PARAM_STATIC_STRINGS);
7850 
7851   g_object_class_install_properties (object_class, PROP_LAST, obj_props);
7852 
7853   /**
7854    * ClutterActor::destroy:
7855    * @actor: the #ClutterActor which emitted the signal
7856    *
7857    * The ::destroy signal notifies that all references held on the
7858    * actor which emitted it should be released.
7859    *
7860    * The ::destroy signal should be used by all holders of a reference
7861    * on @actor.
7862    *
7863    * This signal might result in the finalization of the #ClutterActor
7864    * if all references are released.
7865    *
7866    * Composite actors and actors implementing the #ClutterContainer
7867    * interface should override the default implementation of the
7868    * class handler of this signal and call clutter_actor_destroy() on
7869    * their children. When overriding the default class handler, it is
7870    * required to chain up to the parent's implementation.
7871    *
7872    * Since: 0.2
7873    */
7874   actor_signals[DESTROY] =
7875     g_signal_new (I_("destroy"),
7876 		  G_TYPE_FROM_CLASS (object_class),
7877                   G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
7878 		  G_STRUCT_OFFSET (ClutterActorClass, destroy),
7879 		  NULL, NULL,
7880 		  _clutter_marshal_VOID__VOID,
7881 		  G_TYPE_NONE, 0);
7882   /**
7883    * ClutterActor::show:
7884    * @actor: the object which received the signal
7885    *
7886    * The ::show signal is emitted when an actor is visible and
7887    * rendered on the stage.
7888    *
7889    * Since: 0.2
7890    */
7891   actor_signals[SHOW] =
7892     g_signal_new (I_("show"),
7893 		  G_TYPE_FROM_CLASS (object_class),
7894 		  G_SIGNAL_RUN_FIRST,
7895 		  G_STRUCT_OFFSET (ClutterActorClass, show),
7896 		  NULL, NULL,
7897 		  _clutter_marshal_VOID__VOID,
7898 		  G_TYPE_NONE, 0);
7899   /**
7900    * ClutterActor::hide:
7901    * @actor: the object which received the signal
7902    *
7903    * The ::hide signal is emitted when an actor is no longer rendered
7904    * on the stage.
7905    *
7906    * Since: 0.2
7907    */
7908   actor_signals[HIDE] =
7909     g_signal_new (I_("hide"),
7910 		  G_TYPE_FROM_CLASS (object_class),
7911 		  G_SIGNAL_RUN_FIRST,
7912 		  G_STRUCT_OFFSET (ClutterActorClass, hide),
7913 		  NULL, NULL,
7914 		  _clutter_marshal_VOID__VOID,
7915 		  G_TYPE_NONE, 0);
7916   /**
7917    * ClutterActor::parent-set:
7918    * @actor: the object which received the signal
7919    * @old_parent: (allow-none): the previous parent of the actor, or %NULL
7920    *
7921    * This signal is emitted when the parent of the actor changes.
7922    *
7923    * Since: 0.2
7924    */
7925   actor_signals[PARENT_SET] =
7926     g_signal_new (I_("parent-set"),
7927                   G_TYPE_FROM_CLASS (object_class),
7928                   G_SIGNAL_RUN_LAST,
7929                   G_STRUCT_OFFSET (ClutterActorClass, parent_set),
7930                   NULL, NULL,
7931                   _clutter_marshal_VOID__OBJECT,
7932                   G_TYPE_NONE, 1,
7933                   CLUTTER_TYPE_ACTOR);
7934 
7935   /**
7936    * ClutterActor::queue-redraw:
7937    * @actor: the actor we're bubbling the redraw request through
7938    * @origin: the actor which initiated the redraw request
7939    *
7940    * The ::queue_redraw signal is emitted when clutter_actor_queue_redraw()
7941    * is called on @origin.
7942    *
7943    * The default implementation for #ClutterActor chains up to the
7944    * parent actor and queues a redraw on the parent, thus "bubbling"
7945    * the redraw queue up through the actor graph. The default
7946    * implementation for #ClutterStage queues a clutter_stage_ensure_redraw()
7947    * in a main loop idle handler.
7948    *
7949    * Note that the @origin actor may be the stage, or a container; it
7950    * does not have to be a leaf node in the actor graph.
7951    *
7952    * Toolkits embedding a #ClutterStage which require a redraw and
7953    * relayout cycle can stop the emission of this signal using the
7954    * GSignal API, redraw the UI and then call clutter_stage_ensure_redraw()
7955    * themselves, like:
7956    *
7957    * |[<!-- language="C" -->
7958    *   static void
7959    *   on_redraw_complete (gpointer data)
7960    *   {
7961    *     ClutterStage *stage = data;
7962    *
7963    *     // execute the Clutter drawing pipeline
7964    *     clutter_stage_ensure_redraw (stage);
7965    *   }
7966    *
7967    *   static void
7968    *   on_stage_queue_redraw (ClutterStage *stage)
7969    *   {
7970    *     // this prevents the default handler to run
7971    *     g_signal_stop_emission_by_name (stage, "queue-redraw");
7972    *
7973    *     // queue a redraw with the host toolkit and call
7974    *     // a function when the redraw has been completed
7975    *     queue_a_redraw (G_CALLBACK (on_redraw_complete), stage);
7976    *   }
7977    * ]|
7978    *
7979    * Note: This signal is emitted before the Clutter paint
7980    * pipeline is executed. If you want to know when the pipeline has
7981    * been completed you should use clutter_threads_add_repaint_func()
7982    * or clutter_threads_add_repaint_func_full().
7983    *
7984    * Since: 1.0
7985    */
7986   actor_signals[QUEUE_REDRAW] =
7987     g_signal_new (I_("queue-redraw"),
7988 		  G_TYPE_FROM_CLASS (object_class),
7989 		  G_SIGNAL_RUN_LAST |
7990                   G_SIGNAL_NO_HOOKS,
7991 		  G_STRUCT_OFFSET (ClutterActorClass, queue_redraw),
7992 		  NULL, NULL,
7993 		  _clutter_marshal_VOID__OBJECT,
7994 		  G_TYPE_NONE, 1,
7995                   CLUTTER_TYPE_ACTOR);
7996 
7997   /**
7998    * ClutterActor::queue-relayout:
7999    * @actor: the actor being queued for relayout
8000    *
8001    * The ::queue_layout signal is emitted when clutter_actor_queue_relayout()
8002    * is called on an actor.
8003    *
8004    * The default implementation for #ClutterActor chains up to the
8005    * parent actor and queues a relayout on the parent, thus "bubbling"
8006    * the relayout queue up through the actor graph.
8007    *
8008    * The main purpose of this signal is to allow relayout to be propagated
8009    * properly in the presence of #ClutterClone actors. Applications will
8010    * not normally need to connect to this signal.
8011    *
8012    * Since: 1.2
8013    */
8014   actor_signals[QUEUE_RELAYOUT] =
8015     g_signal_new (I_("queue-relayout"),
8016 		  G_TYPE_FROM_CLASS (object_class),
8017 		  G_SIGNAL_RUN_LAST |
8018                   G_SIGNAL_NO_HOOKS,
8019 		  G_STRUCT_OFFSET (ClutterActorClass, queue_relayout),
8020 		  NULL, NULL,
8021 		  _clutter_marshal_VOID__VOID,
8022 		  G_TYPE_NONE, 0);
8023 
8024   /**
8025    * ClutterActor::event:
8026    * @actor: the actor which received the event
8027    * @event: a #ClutterEvent
8028    *
8029    * The ::event signal is emitted each time an event is received
8030    * by the @actor. This signal will be emitted on every actor,
8031    * following the hierarchy chain, until it reaches the top-level
8032    * container (the #ClutterStage).
8033    *
8034    * Return value: %TRUE if the event has been handled by the actor,
8035    *   or %FALSE to continue the emission.
8036    *
8037    * Since: 0.6
8038    */
8039   actor_signals[EVENT] =
8040     g_signal_new (I_("event"),
8041 		  G_TYPE_FROM_CLASS (object_class),
8042 		  G_SIGNAL_RUN_LAST,
8043 		  G_STRUCT_OFFSET (ClutterActorClass, event),
8044 		  _clutter_boolean_handled_accumulator, NULL,
8045 		  _clutter_marshal_BOOLEAN__BOXED,
8046 		  G_TYPE_BOOLEAN, 1,
8047 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8048   /**
8049    * ClutterActor::button-press-event:
8050    * @actor: the actor which received the event
8051    * @event: (type ClutterButtonEvent): a #ClutterButtonEvent
8052    *
8053    * The ::button-press-event signal is emitted each time a mouse button
8054    * is pressed on @actor.
8055    *
8056    * Return value: %TRUE if the event has been handled by the actor,
8057    *   or %FALSE to continue the emission.
8058    *
8059    * Since: 0.6
8060    */
8061   actor_signals[BUTTON_PRESS_EVENT] =
8062     g_signal_new (I_("button-press-event"),
8063 		  G_TYPE_FROM_CLASS (object_class),
8064 		  G_SIGNAL_RUN_LAST,
8065 		  G_STRUCT_OFFSET (ClutterActorClass, button_press_event),
8066 		  _clutter_boolean_handled_accumulator, NULL,
8067 		  _clutter_marshal_BOOLEAN__BOXED,
8068 		  G_TYPE_BOOLEAN, 1,
8069 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8070   /**
8071    * ClutterActor::button-release-event:
8072    * @actor: the actor which received the event
8073    * @event: (type ClutterButtonEvent): a #ClutterButtonEvent
8074    *
8075    * The ::button-release-event signal is emitted each time a mouse button
8076    * is released on @actor.
8077    *
8078    * Return value: %TRUE if the event has been handled by the actor,
8079    *   or %FALSE to continue the emission.
8080    *
8081    * Since: 0.6
8082    */
8083   actor_signals[BUTTON_RELEASE_EVENT] =
8084     g_signal_new (I_("button-release-event"),
8085 		  G_TYPE_FROM_CLASS (object_class),
8086 		  G_SIGNAL_RUN_LAST,
8087 		  G_STRUCT_OFFSET (ClutterActorClass, button_release_event),
8088 		  _clutter_boolean_handled_accumulator, NULL,
8089 		  _clutter_marshal_BOOLEAN__BOXED,
8090 		  G_TYPE_BOOLEAN, 1,
8091 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8092   /**
8093    * ClutterActor::scroll-event:
8094    * @actor: the actor which received the event
8095    * @event: (type ClutterScrollEvent): a #ClutterScrollEvent
8096    *
8097    * The ::scroll-event signal is emitted each time the mouse is
8098    * scrolled on @actor
8099    *
8100    * Return value: %TRUE if the event has been handled by the actor,
8101    *   or %FALSE to continue the emission.
8102    *
8103    * Since: 0.6
8104    */
8105   actor_signals[SCROLL_EVENT] =
8106     g_signal_new (I_("scroll-event"),
8107 		  G_TYPE_FROM_CLASS (object_class),
8108 		  G_SIGNAL_RUN_LAST,
8109 		  G_STRUCT_OFFSET (ClutterActorClass, scroll_event),
8110 		  _clutter_boolean_handled_accumulator, NULL,
8111 		  _clutter_marshal_BOOLEAN__BOXED,
8112 		  G_TYPE_BOOLEAN, 1,
8113 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8114   /**
8115    * ClutterActor::key-press-event:
8116    * @actor: the actor which received the event
8117    * @event: (type ClutterKeyEvent): a #ClutterKeyEvent
8118    *
8119    * The ::key-press-event signal is emitted each time a keyboard button
8120    * is pressed while @actor has key focus (see clutter_stage_set_key_focus()).
8121    *
8122    * Return value: %TRUE if the event has been handled by the actor,
8123    *   or %FALSE to continue the emission.
8124    *
8125    * Since: 0.6
8126    */
8127   actor_signals[KEY_PRESS_EVENT] =
8128     g_signal_new (I_("key-press-event"),
8129 		  G_TYPE_FROM_CLASS (object_class),
8130 		  G_SIGNAL_RUN_LAST,
8131 		  G_STRUCT_OFFSET (ClutterActorClass, key_press_event),
8132 		  _clutter_boolean_handled_accumulator, NULL,
8133 		  _clutter_marshal_BOOLEAN__BOXED,
8134 		  G_TYPE_BOOLEAN, 1,
8135 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8136   /**
8137    * ClutterActor::key-release-event:
8138    * @actor: the actor which received the event
8139    * @event: (type ClutterKeyEvent): a #ClutterKeyEvent
8140    *
8141    * The ::key-release-event signal is emitted each time a keyboard button
8142    * is released while @actor has key focus (see
8143    * clutter_stage_set_key_focus()).
8144    *
8145    * Return value: %TRUE if the event has been handled by the actor,
8146    *   or %FALSE to continue the emission.
8147    *
8148    * Since: 0.6
8149    */
8150   actor_signals[KEY_RELEASE_EVENT] =
8151     g_signal_new (I_("key-release-event"),
8152 		  G_TYPE_FROM_CLASS (object_class),
8153 		  G_SIGNAL_RUN_LAST,
8154 		  G_STRUCT_OFFSET (ClutterActorClass, key_release_event),
8155 		  _clutter_boolean_handled_accumulator, NULL,
8156 		  _clutter_marshal_BOOLEAN__BOXED,
8157 		  G_TYPE_BOOLEAN, 1,
8158 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8159   /**
8160    * ClutterActor::motion-event:
8161    * @actor: the actor which received the event
8162    * @event: (type ClutterMotionEvent): a #ClutterMotionEvent
8163    *
8164    * The ::motion-event signal is emitted each time the mouse pointer is
8165    * moved over @actor.
8166    *
8167    * Return value: %TRUE if the event has been handled by the actor,
8168    *   or %FALSE to continue the emission.
8169    *
8170    * Since: 0.6
8171    */
8172   actor_signals[MOTION_EVENT] =
8173     g_signal_new (I_("motion-event"),
8174 		  G_TYPE_FROM_CLASS (object_class),
8175 		  G_SIGNAL_RUN_LAST,
8176 		  G_STRUCT_OFFSET (ClutterActorClass, motion_event),
8177 		  _clutter_boolean_handled_accumulator, NULL,
8178 		  _clutter_marshal_BOOLEAN__BOXED,
8179 		  G_TYPE_BOOLEAN, 1,
8180 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8181 
8182   /**
8183    * ClutterActor::key-focus-in:
8184    * @actor: the actor which now has key focus
8185    *
8186    * The ::key-focus-in signal is emitted when @actor receives key focus.
8187    *
8188    * Since: 0.6
8189    */
8190   actor_signals[KEY_FOCUS_IN] =
8191     g_signal_new (I_("key-focus-in"),
8192 		  G_TYPE_FROM_CLASS (object_class),
8193 		  G_SIGNAL_RUN_LAST,
8194 		  G_STRUCT_OFFSET (ClutterActorClass, key_focus_in),
8195 		  NULL, NULL,
8196 		  _clutter_marshal_VOID__VOID,
8197 		  G_TYPE_NONE, 0);
8198 
8199   /**
8200    * ClutterActor::key-focus-out:
8201    * @actor: the actor which now has key focus
8202    *
8203    * The ::key-focus-out signal is emitted when @actor loses key focus.
8204    *
8205    * Since: 0.6
8206    */
8207   actor_signals[KEY_FOCUS_OUT] =
8208     g_signal_new (I_("key-focus-out"),
8209 		  G_TYPE_FROM_CLASS (object_class),
8210 		  G_SIGNAL_RUN_LAST,
8211 		  G_STRUCT_OFFSET (ClutterActorClass, key_focus_out),
8212 		  NULL, NULL,
8213 		  _clutter_marshal_VOID__VOID,
8214 		  G_TYPE_NONE, 0);
8215 
8216   /**
8217    * ClutterActor::enter-event:
8218    * @actor: the actor which the pointer has entered.
8219    * @event: (type ClutterCrossingEvent): a #ClutterCrossingEvent
8220    *
8221    * The ::enter-event signal is emitted when the pointer enters the @actor
8222    *
8223    * Return value: %TRUE if the event has been handled by the actor,
8224    *   or %FALSE to continue the emission.
8225    *
8226    * Since: 0.6
8227    */
8228   actor_signals[ENTER_EVENT] =
8229     g_signal_new (I_("enter-event"),
8230 		  G_TYPE_FROM_CLASS (object_class),
8231 		  G_SIGNAL_RUN_LAST,
8232 		  G_STRUCT_OFFSET (ClutterActorClass, enter_event),
8233 		  _clutter_boolean_handled_accumulator, NULL,
8234 		  _clutter_marshal_BOOLEAN__BOXED,
8235 		  G_TYPE_BOOLEAN, 1,
8236 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8237 
8238   /**
8239    * ClutterActor::leave-event:
8240    * @actor: the actor which the pointer has left
8241    * @event: (type ClutterCrossingEvent): a #ClutterCrossingEvent
8242    *
8243    * The ::leave-event signal is emitted when the pointer leaves the @actor.
8244    *
8245    * Return value: %TRUE if the event has been handled by the actor,
8246    *   or %FALSE to continue the emission.
8247    *
8248    * Since: 0.6
8249    */
8250   actor_signals[LEAVE_EVENT] =
8251     g_signal_new (I_("leave-event"),
8252 		  G_TYPE_FROM_CLASS (object_class),
8253 		  G_SIGNAL_RUN_LAST,
8254 		  G_STRUCT_OFFSET (ClutterActorClass, leave_event),
8255 		  _clutter_boolean_handled_accumulator, NULL,
8256 		  _clutter_marshal_BOOLEAN__BOXED,
8257 		  G_TYPE_BOOLEAN, 1,
8258 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8259 
8260   /**
8261    * ClutterActor::captured-event:
8262    * @actor: the actor which received the signal
8263    * @event: a #ClutterEvent
8264    *
8265    * The ::captured-event signal is emitted when an event is captured
8266    * by Clutter. This signal will be emitted starting from the top-level
8267    * container (the #ClutterStage) to the actor which received the event
8268    * going down the hierarchy. This signal can be used to intercept every
8269    * event before the specialized events (like
8270    * ClutterActor::button-press-event or ::key-released-event) are
8271    * emitted.
8272    *
8273    * Return value: %TRUE if the event has been handled by the actor,
8274    *   or %FALSE to continue the emission.
8275    *
8276    * Since: 0.6
8277    */
8278   actor_signals[CAPTURED_EVENT] =
8279     g_signal_new (I_("captured-event"),
8280 		  G_TYPE_FROM_CLASS (object_class),
8281 		  G_SIGNAL_RUN_LAST,
8282 		  G_STRUCT_OFFSET (ClutterActorClass, captured_event),
8283 		  _clutter_boolean_handled_accumulator, NULL,
8284 		  _clutter_marshal_BOOLEAN__BOXED,
8285 		  G_TYPE_BOOLEAN, 1,
8286 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8287 
8288   /**
8289    * ClutterActor::paint:
8290    * @actor: the #ClutterActor that received the signal
8291    *
8292    * The ::paint signal is emitted each time an actor is being painted.
8293    *
8294    * Subclasses of #ClutterActor should override the #ClutterActorClass.paint
8295    * virtual function paint themselves in that function.
8296    *
8297    * It is strongly discouraged to connect a signal handler to
8298    * the #ClutterActor::paint signal; if you want to change the paint
8299    * sequence of an existing #ClutterActor instance, either create a new
8300    * #ClutterActor class and override the #ClutterActorClass.paint virtual
8301    * function, or use a #ClutterEffect. The #ClutterActor::paint signal
8302    * will be removed in a future version of Clutter.
8303    *
8304    * Since: 0.8
8305    *
8306    * Deprecated: 1.12: Override the #ClutterActorClass.paint virtual
8307    *   function, use a #ClutterContent implementation, or a #ClutterEffect
8308    *   instead of connecting to this signal.
8309    */
8310   actor_signals[PAINT] =
8311     g_signal_new (I_("paint"),
8312                   G_TYPE_FROM_CLASS (object_class),
8313                   G_SIGNAL_RUN_LAST |
8314                   G_SIGNAL_NO_HOOKS |
8315                   G_SIGNAL_DEPRECATED,
8316                   G_STRUCT_OFFSET (ClutterActorClass, paint),
8317                   NULL, NULL,
8318                   _clutter_marshal_VOID__VOID,
8319                   G_TYPE_NONE, 0);
8320   /**
8321    * ClutterActor::realize:
8322    * @actor: the #ClutterActor that received the signal
8323    *
8324    * The ::realize signal is emitted each time an actor is being
8325    * realized.
8326    *
8327    * Since: 0.8
8328    *
8329    * Deprecated: 1.16: The signal should not be used in newly
8330    *   written code
8331    */
8332   actor_signals[REALIZE] =
8333     g_signal_new (I_("realize"),
8334                   G_TYPE_FROM_CLASS (object_class),
8335                   G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
8336                   G_STRUCT_OFFSET (ClutterActorClass, realize),
8337                   NULL, NULL,
8338                   _clutter_marshal_VOID__VOID,
8339                   G_TYPE_NONE, 0);
8340   /**
8341    * ClutterActor::unrealize:
8342    * @actor: the #ClutterActor that received the signal
8343    *
8344    * The ::unrealize signal is emitted each time an actor is being
8345    * unrealized.
8346    *
8347    * Since: 0.8
8348    *
8349    * Deprecated: 1.16: The signal should not be used in newly
8350    *   written code
8351    */
8352   actor_signals[UNREALIZE] =
8353     g_signal_new (I_("unrealize"),
8354                   G_TYPE_FROM_CLASS (object_class),
8355                   G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
8356                   G_STRUCT_OFFSET (ClutterActorClass, unrealize),
8357                   NULL, NULL,
8358                   _clutter_marshal_VOID__VOID,
8359                   G_TYPE_NONE, 0);
8360 
8361   /**
8362    * ClutterActor::pick:
8363    * @actor: the #ClutterActor that received the signal
8364    * @color: the #ClutterColor to be used when picking
8365    *
8366    * The ::pick signal is emitted each time an actor is being painted
8367    * in "pick mode". The pick mode is used to identify the actor during
8368    * the event handling phase, or by clutter_stage_get_actor_at_pos().
8369    * The actor should paint its shape using the passed @pick_color.
8370    *
8371    * Subclasses of #ClutterActor should override the class signal handler
8372    * and paint themselves in that function.
8373    *
8374    * It is possible to connect a handler to the ::pick signal in order
8375    * to set up some custom aspect of a paint in pick mode.
8376    *
8377    * Since: 1.0
8378    * Deprecated: 1.12: Override the #ClutterActorClass.pick virtual function
8379    *   instead.
8380    */
8381   actor_signals[PICK] =
8382     g_signal_new (I_("pick"),
8383                   G_TYPE_FROM_CLASS (object_class),
8384                   G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
8385                   G_STRUCT_OFFSET (ClutterActorClass, pick),
8386                   NULL, NULL,
8387                   _clutter_marshal_VOID__BOXED,
8388                   G_TYPE_NONE, 1,
8389                   CLUTTER_TYPE_COLOR | G_SIGNAL_TYPE_STATIC_SCOPE);
8390 
8391   /**
8392    * ClutterActor::allocation-changed:
8393    * @actor: the #ClutterActor that emitted the signal
8394    * @box: a #ClutterActorBox with the new allocation
8395    * @flags: #ClutterAllocationFlags for the allocation
8396    *
8397    * The ::allocation-changed signal is emitted when the
8398    * #ClutterActor:allocation property changes. Usually, application
8399    * code should just use the notifications for the :allocation property
8400    * but if you want to track the allocation flags as well, for instance
8401    * to know whether the absolute origin of @actor changed, then you might
8402    * want use this signal instead.
8403    *
8404    * Since: 1.0
8405    */
8406   actor_signals[ALLOCATION_CHANGED] =
8407     g_signal_new (I_("allocation-changed"),
8408                   G_TYPE_FROM_CLASS (object_class),
8409                   G_SIGNAL_RUN_LAST,
8410                   0,
8411                   NULL, NULL,
8412                   _clutter_marshal_VOID__BOXED_FLAGS,
8413                   G_TYPE_NONE, 2,
8414                   CLUTTER_TYPE_ACTOR_BOX | G_SIGNAL_TYPE_STATIC_SCOPE,
8415                   CLUTTER_TYPE_ALLOCATION_FLAGS);
8416 
8417   /**
8418    * ClutterActor::transitions-completed:
8419    * @actor: a #ClutterActor
8420    *
8421    * The ::transitions-completed signal is emitted once all transitions
8422    * involving @actor are complete.
8423    *
8424    * Since: 1.10
8425    */
8426   actor_signals[TRANSITIONS_COMPLETED] =
8427     g_signal_new (I_("transitions-completed"),
8428                   G_TYPE_FROM_CLASS (object_class),
8429                   G_SIGNAL_RUN_LAST,
8430                   0,
8431                   NULL, NULL,
8432                   _clutter_marshal_VOID__VOID,
8433                   G_TYPE_NONE, 0);
8434 
8435   /**
8436    * ClutterActor::transition-stopped:
8437    * @actor: a #ClutterActor
8438    * @name: the name of the transition
8439    * @is_finished: whether the transition was finished, or stopped
8440    *
8441    * The ::transition-stopped signal is emitted once a transition
8442    * is stopped; a transition is stopped once it reached its total
8443    * duration (including eventual repeats), it has been stopped
8444    * using clutter_timeline_stop(), or it has been removed from the
8445    * transitions applied on @actor, using clutter_actor_remove_transition().
8446    *
8447    * Since: 1.12
8448    */
8449   actor_signals[TRANSITION_STOPPED] =
8450     g_signal_new (I_("transition-stopped"),
8451                   G_TYPE_FROM_CLASS (object_class),
8452                   G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE |
8453                   G_SIGNAL_NO_HOOKS | G_SIGNAL_DETAILED,
8454                   0,
8455                   NULL, NULL,
8456                   _clutter_marshal_VOID__STRING_BOOLEAN,
8457                   G_TYPE_NONE, 2,
8458                   G_TYPE_STRING,
8459                   G_TYPE_BOOLEAN);
8460 
8461   /**
8462    * ClutterActor::touch-event:
8463    * @actor: a #ClutterActor
8464    * @event: a #ClutterEvent
8465    *
8466    * The ::touch-event signal is emitted each time a touch
8467    * begin/end/update/cancel event.
8468    *
8469    * Return value: %CLUTTER_EVENT_STOP if the event has been handled by
8470    *   the actor, or %CLUTTER_EVENT_PROPAGATE to continue the emission.
8471    *
8472    * Since: 1.12
8473    */
8474   actor_signals[TOUCH_EVENT] =
8475     g_signal_new (I_("touch-event"),
8476 		  G_TYPE_FROM_CLASS (object_class),
8477 		  G_SIGNAL_RUN_LAST,
8478 		  G_STRUCT_OFFSET (ClutterActorClass, touch_event),
8479 		  _clutter_boolean_handled_accumulator, NULL,
8480 		  _clutter_marshal_BOOLEAN__BOXED,
8481 		  G_TYPE_BOOLEAN, 1,
8482 		  CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
8483 }
8484 
8485 static void
clutter_actor_init(ClutterActor * self)8486 clutter_actor_init (ClutterActor *self)
8487 {
8488   ClutterActorPrivate *priv;
8489 
8490   self->priv = priv = clutter_actor_get_instance_private (self);
8491 
8492   priv->pick_id = -1;
8493 
8494   priv->opacity = 0xff;
8495   priv->show_on_set_parent = TRUE;
8496 
8497   priv->needs_width_request = TRUE;
8498   priv->needs_height_request = TRUE;
8499   priv->needs_allocation = TRUE;
8500 
8501   priv->cached_width_age = 1;
8502   priv->cached_height_age = 1;
8503 
8504   priv->opacity_override = -1;
8505   priv->enable_model_view_transform = TRUE;
8506 
8507   /* Initialize an empty paint volume to start with */
8508   _clutter_paint_volume_init_static (&priv->last_paint_volume, NULL);
8509   priv->last_paint_volume_valid = TRUE;
8510 
8511   priv->transform_valid = FALSE;
8512 
8513   /* the default is to stretch the content, to match the
8514    * current behaviour of basically all actors. also, it's
8515    * the easiest thing to compute.
8516    */
8517   priv->content_gravity = CLUTTER_CONTENT_GRAVITY_RESIZE_FILL;
8518   priv->min_filter = CLUTTER_SCALING_FILTER_LINEAR;
8519   priv->mag_filter = CLUTTER_SCALING_FILTER_LINEAR;
8520 
8521   /* this flag will be set to TRUE if the actor gets a child
8522    * or if the [xy]-expand flags are explicitly set; until
8523    * then, the actor does not need to expand.
8524    *
8525    * this also allows us to avoid computing the expand flag
8526    * when building up a scene.
8527    */
8528   priv->needs_compute_expand = FALSE;
8529 
8530   /* we start with an easing state with duration forcibly set
8531    * to 0, for backward compatibility.
8532    */
8533   clutter_actor_save_easing_state (self);
8534   clutter_actor_set_easing_duration (self, 0);
8535 }
8536 
8537 /**
8538  * clutter_actor_new:
8539  *
8540  * Creates a new #ClutterActor.
8541  *
8542  * A newly created actor has a floating reference, which will be sunk
8543  * when it is added to another actor.
8544  *
8545  * Return value: the newly created #ClutterActor
8546  *
8547  * Since: 1.10
8548  */
8549 ClutterActor *
clutter_actor_new(void)8550 clutter_actor_new (void)
8551 {
8552   return g_object_new (CLUTTER_TYPE_ACTOR, NULL);
8553 }
8554 
8555 /**
8556  * clutter_actor_destroy:
8557  * @self: a #ClutterActor
8558  *
8559  * Destroys an actor.  When an actor is destroyed, it will break any
8560  * references it holds to other objects.  If the actor is inside a
8561  * container, the actor will be removed.
8562  *
8563  * When you destroy a container, its children will be destroyed as well.
8564  *
8565  * Note: you cannot destroy the #ClutterStage returned by
8566  * clutter_stage_get_default().
8567  */
8568 void
clutter_actor_destroy(ClutterActor * self)8569 clutter_actor_destroy (ClutterActor *self)
8570 {
8571   g_return_if_fail (CLUTTER_IS_ACTOR (self));
8572 
8573   g_object_ref (self);
8574 
8575   /* avoid recursion while destroying */
8576   if (!CLUTTER_ACTOR_IN_DESTRUCTION (self))
8577     {
8578       CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_DESTRUCTION);
8579 
8580       g_object_run_dispose (G_OBJECT (self));
8581 
8582       CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_DESTRUCTION);
8583     }
8584 
8585   g_object_unref (self);
8586 }
8587 
8588 void
_clutter_actor_finish_queue_redraw(ClutterActor * self,ClutterPaintVolume * clip)8589 _clutter_actor_finish_queue_redraw (ClutterActor *self,
8590                                     ClutterPaintVolume *clip)
8591 {
8592   ClutterActorPrivate *priv = self->priv;
8593   ClutterPaintVolume *pv;
8594   gboolean clipped;
8595 
8596   /* Remove queue entry early in the process, otherwise a new
8597      queue_redraw() during signal handling could put back this
8598      object in the stage redraw list (but the entry is freed as
8599      soon as we return from this function, causing a segfault
8600      later)
8601   */
8602   priv->queue_redraw_entry = NULL;
8603 
8604   /* If we've been explicitly passed a clip volume then there's
8605    * nothing more to calculate, but otherwise the only thing we know
8606    * is that the change is constrained to the given actor.
8607    *
8608    * The idea is that if we know the paint volume for where the actor
8609    * was last drawn (in eye coordinates) and we also have the paint
8610    * volume for where it will be drawn next (in actor coordinates)
8611    * then if we queue a redraw for both these volumes that will cover
8612    * everything that needs to be redrawn to clear the old view and
8613    * show the latest view of the actor.
8614    *
8615    * Don't clip this redraw if we don't know what position we had for
8616    * the previous redraw since we don't know where to set the clip so
8617    * it will clear the actor as it is currently.
8618    */
8619   if (clip)
8620     {
8621       _clutter_actor_set_queue_redraw_clip (self, clip);
8622       clipped = TRUE;
8623     }
8624   else if (G_LIKELY (priv->last_paint_volume_valid))
8625     {
8626       pv = _clutter_actor_get_paint_volume_mutable (self);
8627       if (pv)
8628         {
8629           ClutterActor *stage = _clutter_actor_get_stage_internal (self);
8630 
8631           /* make sure we redraw the actors old position... */
8632           _clutter_actor_set_queue_redraw_clip (stage,
8633                                                 &priv->last_paint_volume);
8634           _clutter_actor_signal_queue_redraw (stage, stage);
8635           _clutter_actor_set_queue_redraw_clip (stage, NULL);
8636 
8637           /* XXX: Ideally the redraw signal would take a clip volume
8638            * argument, but that would be an ABI break. Until we can
8639            * break the ABI we pass the argument out-of-band
8640            */
8641 
8642           /* setup the clip for the actors new position... */
8643           _clutter_actor_set_queue_redraw_clip (self, pv);
8644           clipped = TRUE;
8645         }
8646       else
8647         clipped = FALSE;
8648     }
8649   else
8650     clipped = FALSE;
8651 
8652   _clutter_actor_signal_queue_redraw (self, self);
8653 
8654   /* Just in case anyone is manually firing redraw signals without
8655    * using the public queue_redraw() API we are careful to ensure that
8656    * our out-of-band clip member is cleared before returning...
8657    *
8658    * Note: A NULL clip denotes a full-stage, un-clipped redraw
8659    */
8660   if (G_LIKELY (clipped))
8661     _clutter_actor_set_queue_redraw_clip (self, NULL);
8662 }
8663 
8664 static void
_clutter_actor_get_allocation_clip(ClutterActor * self,ClutterActorBox * clip)8665 _clutter_actor_get_allocation_clip (ClutterActor *self,
8666                                     ClutterActorBox *clip)
8667 {
8668   ClutterActorBox allocation;
8669 
8670   /* XXX: we don't care if we get an out of date allocation here
8671    * because clutter_actor_queue_redraw_with_clip knows to ignore
8672    * the clip if the actor's allocation is invalid.
8673    *
8674    * This is noted because clutter_actor_get_allocation_box does some
8675    * unnecessary work to support buggy code with a comment suggesting
8676    * that it could be changed later which would be good for this use
8677    * case!
8678    */
8679   clutter_actor_get_allocation_box (self, &allocation);
8680 
8681   /* NB: clutter_actor_queue_redraw_with_clip expects a box in the
8682    * actor's own coordinate space but the allocation is in parent
8683    * coordinates */
8684   clip->x1 = 0;
8685   clip->y1 = 0;
8686   clip->x2 = allocation.x2 - allocation.x1;
8687   clip->y2 = allocation.y2 - allocation.y1;
8688 }
8689 
8690 void
_clutter_actor_queue_redraw_full(ClutterActor * self,ClutterRedrawFlags flags,ClutterPaintVolume * volume,ClutterEffect * effect)8691 _clutter_actor_queue_redraw_full (ClutterActor       *self,
8692                                   ClutterRedrawFlags  flags,
8693                                   ClutterPaintVolume *volume,
8694                                   ClutterEffect      *effect)
8695 {
8696   ClutterActorPrivate *priv = self->priv;
8697   ClutterPaintVolume allocation_pv;
8698   ClutterPaintVolume *pv;
8699   gboolean should_free_pv;
8700   ClutterActor *stage;
8701 
8702   /* Here's an outline of the actor queue redraw mechanism:
8703    *
8704    * The process starts in one of the following two functions which
8705    * are wrappers for this function:
8706    *
8707    *   clutter_actor_queue_redraw()
8708    *   _clutter_actor_queue_redraw_with_clip()
8709    *
8710    * additionally, an effect can queue a redraw by wrapping this
8711    * function in clutter_effect_queue_repaint().
8712    *
8713    * This functions queues an entry in a list associated with the
8714    * stage which is a list of actors that queued a redraw while
8715    * updating the timelines, performing layouting and processing other
8716    * mainloop sources before the next paint starts.
8717    *
8718    * We aim to minimize the processing done at this point because
8719    * there is a good chance other events will happen while updating
8720    * the scenegraph that would invalidate any expensive work we might
8721    * otherwise try to do here. For example we don't try and resolve
8722    * the screen space bounding box of an actor at this stage so as to
8723    * minimize how much of the screen redraw because it's possible
8724    * something else will happen which will force a full redraw anyway.
8725    *
8726    * When all updates are complete and we come to paint the stage then
8727    * we iterate this list and actually emit the "queue-redraw" signals
8728    * for each of the listed actors which will bubble up to the stage
8729    * for each actor and at that point we will transform the actors
8730    * paint volume into screen coordinates to determine the clip region
8731    * for what needs to be redrawn in the next paint.
8732    *
8733    * Besides minimizing redundant work another reason for this
8734    * deferred design is that it's more likely we will be able to
8735    * determine the paint volume of an actor once we've finished
8736    * updating the scenegraph because its allocation should be up to
8737    * date. NB: If we can't determine an actors paint volume then we
8738    * can't automatically queue a clipped redraw which can make a big
8739    * difference to performance.
8740    *
8741    * So the control flow goes like this:
8742    * One of clutter_actor_queue_redraw(),
8743    *        _clutter_actor_queue_redraw_with_clip(),
8744    *     or clutter_effect_queue_repaint()
8745    *
8746    * then control moves to:
8747    *   _clutter_stage_queue_actor_redraw()
8748    *
8749    * later during _clutter_stage_do_update(), once relayouting is done
8750    * and the scenegraph has been updated we will call:
8751    * _clutter_stage_finish_queue_redraws().
8752    *
8753    * _clutter_stage_finish_queue_redraws() will call
8754    * _clutter_actor_finish_queue_redraw() for each listed actor.
8755    *
8756    * Note: actors *are* allowed to queue further redraws during this
8757    * process (considering clone actors or texture_new_from_actor which
8758    * respond to their source queueing a redraw by queuing a redraw
8759    * themselves). We repeat the process until the list is empty.
8760    *
8761    * This will result in the "queue-redraw" signal being fired for
8762    * each actor which will pass control to the default signal handler:
8763    * clutter_actor_real_queue_redraw()
8764    *
8765    * This will bubble up to the stages handler:
8766    * clutter_stage_real_queue_redraw()
8767    *
8768    * clutter_stage_real_queue_redraw() will transform the actors paint
8769    * volume into screen space and add it as a clip region for the next
8770    * paint.
8771    */
8772 
8773   /* ignore queueing a redraw for actors being destroyed */
8774   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
8775     return;
8776 
8777   /* we can ignore unmapped actors, unless they have at least one
8778    * mapped clone or they are inside a cloned branch of the scene
8779    * graph, as unmapped actors will simply be left unpainted.
8780    *
8781    * this allows us to ignore redraws queued on leaf nodes when one
8782    * of their parents has been hidden
8783    */
8784   if (!CLUTTER_ACTOR_IS_MAPPED (self) &&
8785       self->priv->in_cloned_branch == 0 &&
8786       !clutter_actor_has_mapped_clones (self))
8787     {
8788       CLUTTER_NOTE (PAINT,
8789                     "Skipping queue_redraw('%s'): mapped=%s, "
8790                     "mapped_clones=%s, "
8791                     "in_cloned_branch=%s",
8792                     _clutter_actor_get_debug_name (self),
8793                     CLUTTER_ACTOR_IS_MAPPED (self) ? "yes" : "no",
8794                     clutter_actor_has_mapped_clones (self) ? "yes" : "no",
8795                     self->priv->in_cloned_branch != 0 ? "yes" : "no");
8796       return;
8797     }
8798 
8799   /* given the check above we could end up queueing a redraw on an
8800    * unmapped actor with mapped clones, so we cannot assume that
8801    * get_stage() will return a Stage
8802    */
8803   stage = _clutter_actor_get_stage_internal (self);
8804   if (stage == NULL)
8805     return;
8806 
8807   /* ignore queueing a redraw on stages that are being destroyed */
8808   if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
8809     return;
8810 
8811   if (flags & CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION)
8812     {
8813       ClutterActorBox allocation_clip;
8814       ClutterVertex origin;
8815 
8816       /* If the actor doesn't have a valid allocation then we will
8817        * queue a full stage redraw. */
8818       if (priv->needs_allocation)
8819         {
8820           /* NB: NULL denotes an undefined clip which will result in a
8821            * full redraw... */
8822           _clutter_actor_set_queue_redraw_clip (self, NULL);
8823           _clutter_actor_signal_queue_redraw (self, self);
8824           return;
8825         }
8826 
8827       _clutter_paint_volume_init_static (&allocation_pv, self);
8828       pv = &allocation_pv;
8829 
8830       _clutter_actor_get_allocation_clip (self, &allocation_clip);
8831 
8832       origin.x = allocation_clip.x1;
8833       origin.y = allocation_clip.y1;
8834       origin.z = 0;
8835       clutter_paint_volume_set_origin (pv, &origin);
8836       clutter_paint_volume_set_width (pv,
8837                                       allocation_clip.x2 - allocation_clip.x1);
8838       clutter_paint_volume_set_height (pv,
8839                                        allocation_clip.y2 -
8840                                        allocation_clip.y1);
8841       should_free_pv = TRUE;
8842     }
8843   else
8844     {
8845       pv = volume;
8846       should_free_pv = FALSE;
8847     }
8848 
8849   self->priv->queue_redraw_entry =
8850     _clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage),
8851                                        priv->queue_redraw_entry,
8852                                        self,
8853                                        pv);
8854 
8855   if (should_free_pv)
8856     clutter_paint_volume_free (pv);
8857 
8858   /* If this is the first redraw queued then we can directly use the
8859      effect parameter */
8860   if (!priv->is_dirty)
8861     priv->effect_to_redraw = effect;
8862   /* Otherwise we need to merge it with the existing effect parameter */
8863   else if (effect != NULL)
8864     {
8865       /* If there's already an effect then we need to use whichever is
8866          later in the chain of actors. Otherwise a full redraw has
8867          already been queued on the actor so we need to ignore the
8868          effect parameter */
8869       if (priv->effect_to_redraw != NULL)
8870         {
8871           if (priv->effects == NULL)
8872             g_warning ("Redraw queued with an effect that is "
8873                        "not applied to the actor");
8874           else
8875             {
8876               const GList *l;
8877 
8878               for (l = _clutter_meta_group_peek_metas (priv->effects);
8879                    l != NULL;
8880                    l = l->next)
8881                 {
8882                   if (l->data == priv->effect_to_redraw ||
8883                       l->data == effect)
8884                     priv->effect_to_redraw = l->data;
8885                 }
8886             }
8887         }
8888     }
8889   else
8890     {
8891       /* If no effect is specified then we need to redraw the whole
8892          actor */
8893       priv->effect_to_redraw = NULL;
8894     }
8895 
8896   priv->is_dirty = TRUE;
8897 }
8898 
8899 /**
8900  * clutter_actor_queue_redraw:
8901  * @self: A #ClutterActor
8902  *
8903  * Queues up a redraw of an actor and any children. The redraw occurs
8904  * once the main loop becomes idle (after the current batch of events
8905  * has been processed, roughly).
8906  *
8907  * Applications rarely need to call this, as redraws are handled
8908  * automatically by modification functions.
8909  *
8910  * This function will not do anything if @self is not visible, or
8911  * if the actor is inside an invisible part of the scenegraph.
8912  *
8913  * Also be aware that painting is a NOP for actors with an opacity of
8914  * 0
8915  *
8916  * When you are implementing a custom actor you must queue a redraw
8917  * whenever some private state changes that will affect painting or
8918  * picking of your actor.
8919  */
8920 void
clutter_actor_queue_redraw(ClutterActor * self)8921 clutter_actor_queue_redraw (ClutterActor *self)
8922 {
8923   g_return_if_fail (CLUTTER_IS_ACTOR (self));
8924 
8925   _clutter_actor_queue_redraw_full (self,
8926                                     0, /* flags */
8927                                     NULL, /* clip volume */
8928                                     NULL /* effect */);
8929 }
8930 
8931 /*< private >
8932  * _clutter_actor_queue_redraw_with_clip:
8933  * @self: A #ClutterActor
8934  * @flags: A mask of #ClutterRedrawFlags controlling the behaviour of
8935  *   this queue redraw.
8936  * @volume: A #ClutterPaintVolume describing the bounds of what needs to be
8937  *   redrawn or %NULL if you are just using a @flag to state your
8938  *   desired clipping.
8939  *
8940  * Queues up a clipped redraw of an actor and any children. The redraw
8941  * occurs once the main loop becomes idle (after the current batch of
8942  * events has been processed, roughly).
8943  *
8944  * If no flags are given the clip volume is defined by @volume
8945  * specified in actor coordinates and tells Clutter that only content
8946  * within this volume has been changed so Clutter can optionally
8947  * optimize the redraw.
8948  *
8949  * If the %CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION @flag is used, @volume
8950  * should be %NULL and this tells Clutter to use the actor's current
8951  * allocation as a clip box. This flag can only be used for 2D actors,
8952  * because any actor with depth may be projected outside its
8953  * allocation.
8954  *
8955  * Applications rarely need to call this, as redraws are handled
8956  * automatically by modification functions.
8957  *
8958  * This function will not do anything if @self is not visible, or if
8959  * the actor is inside an invisible part of the scenegraph.
8960  *
8961  * Also be aware that painting is a NOP for actors with an opacity of
8962  * 0
8963  *
8964  * When you are implementing a custom actor you must queue a redraw
8965  * whenever some private state changes that will affect painting or
8966  * picking of your actor.
8967  */
8968 void
_clutter_actor_queue_redraw_with_clip(ClutterActor * self,ClutterRedrawFlags flags,ClutterPaintVolume * volume)8969 _clutter_actor_queue_redraw_with_clip (ClutterActor       *self,
8970                                        ClutterRedrawFlags  flags,
8971                                        ClutterPaintVolume *volume)
8972 {
8973   _clutter_actor_queue_redraw_full (self,
8974                                     flags, /* flags */
8975                                     volume, /* clip volume */
8976                                     NULL /* effect */);
8977 }
8978 
8979 void
_clutter_actor_queue_only_relayout(ClutterActor * self)8980 _clutter_actor_queue_only_relayout (ClutterActor *self)
8981 {
8982   ClutterActorPrivate *priv = self->priv;
8983 
8984   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
8985     return;
8986 
8987   if (priv->needs_width_request &&
8988       priv->needs_height_request &&
8989       priv->needs_allocation)
8990     return; /* save some cpu cycles */
8991 
8992 #if CLUTTER_ENABLE_DEBUG
8993   if (!CLUTTER_ACTOR_IS_TOPLEVEL (self) && CLUTTER_ACTOR_IN_RELAYOUT (self))
8994     {
8995       g_warning ("The actor '%s' is currently inside an allocation "
8996                  "cycle; calling clutter_actor_queue_relayout() is "
8997                  "not recommended",
8998                  _clutter_actor_get_debug_name (self));
8999     }
9000 #endif /* CLUTTER_ENABLE_DEBUG */
9001 
9002   _clutter_actor_queue_relayout_on_clones (self);
9003 
9004   g_signal_emit (self, actor_signals[QUEUE_RELAYOUT], 0);
9005 }
9006 
9007 /**
9008  * clutter_actor_queue_redraw_with_clip:
9009  * @self: a #ClutterActor
9010  * @clip: (allow-none): a rectangular clip region, or %NULL
9011  *
9012  * Queues a redraw on @self limited to a specific, actor-relative
9013  * rectangular area.
9014  *
9015  * If @clip is %NULL this function is equivalent to
9016  * clutter_actor_queue_redraw().
9017  *
9018  * Since: 1.10
9019  */
9020 void
clutter_actor_queue_redraw_with_clip(ClutterActor * self,const cairo_rectangle_int_t * clip)9021 clutter_actor_queue_redraw_with_clip (ClutterActor                *self,
9022                                       const cairo_rectangle_int_t *clip)
9023 {
9024   ClutterPaintVolume volume;
9025   ClutterVertex origin;
9026 
9027   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9028 
9029   if (clip == NULL)
9030     {
9031       clutter_actor_queue_redraw (self);
9032       return;
9033     }
9034 
9035   _clutter_paint_volume_init_static (&volume, self);
9036 
9037   origin.x = clip->x;
9038   origin.y = clip->y;
9039   origin.z = 0.0f;
9040 
9041   clutter_paint_volume_set_origin (&volume, &origin);
9042   clutter_paint_volume_set_width (&volume, clip->width);
9043   clutter_paint_volume_set_height (&volume, clip->height);
9044 
9045   _clutter_actor_queue_redraw_full (self, 0, &volume, NULL);
9046 
9047   clutter_paint_volume_free (&volume);
9048 }
9049 
9050 /**
9051  * clutter_actor_queue_relayout:
9052  * @self: A #ClutterActor
9053  *
9054  * Indicates that the actor's size request or other layout-affecting
9055  * properties may have changed. This function is used inside #ClutterActor
9056  * subclass implementations, not by applications directly.
9057  *
9058  * Queueing a new layout automatically queues a redraw as well.
9059  *
9060  * Since: 0.8
9061  */
9062 void
clutter_actor_queue_relayout(ClutterActor * self)9063 clutter_actor_queue_relayout (ClutterActor *self)
9064 {
9065   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9066 
9067   _clutter_actor_queue_only_relayout (self);
9068   clutter_actor_queue_redraw (self);
9069 }
9070 
9071 /**
9072  * clutter_actor_get_preferred_size:
9073  * @self: a #ClutterActor
9074  * @min_width_p: (out) (allow-none): return location for the minimum
9075  *   width, or %NULL
9076  * @min_height_p: (out) (allow-none): return location for the minimum
9077  *   height, or %NULL
9078  * @natural_width_p: (out) (allow-none): return location for the natural
9079  *   width, or %NULL
9080  * @natural_height_p: (out) (allow-none): return location for the natural
9081  *   height, or %NULL
9082  *
9083  * Computes the preferred minimum and natural size of an actor, taking into
9084  * account the actor's geometry management (either height-for-width
9085  * or width-for-height).
9086  *
9087  * The width and height used to compute the preferred height and preferred
9088  * width are the actor's natural ones.
9089  *
9090  * If you need to control the height for the preferred width, or the width for
9091  * the preferred height, you should use clutter_actor_get_preferred_width()
9092  * and clutter_actor_get_preferred_height(), and check the actor's preferred
9093  * geometry management using the #ClutterActor:request-mode property.
9094  *
9095  * Since: 0.8
9096  */
9097 void
clutter_actor_get_preferred_size(ClutterActor * self,gfloat * min_width_p,gfloat * min_height_p,gfloat * natural_width_p,gfloat * natural_height_p)9098 clutter_actor_get_preferred_size (ClutterActor *self,
9099                                   gfloat       *min_width_p,
9100                                   gfloat       *min_height_p,
9101                                   gfloat       *natural_width_p,
9102                                   gfloat       *natural_height_p)
9103 {
9104   ClutterActorPrivate *priv;
9105   gfloat min_width, min_height;
9106   gfloat natural_width, natural_height;
9107 
9108   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9109 
9110   priv = self->priv;
9111 
9112   min_width = min_height = 0;
9113   natural_width = natural_height = 0;
9114 
9115   if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
9116     {
9117       CLUTTER_NOTE (LAYOUT, "Preferred size (height-for-width)");
9118       clutter_actor_get_preferred_width (self, -1,
9119                                          &min_width,
9120                                          &natural_width);
9121       clutter_actor_get_preferred_height (self, natural_width,
9122                                           &min_height,
9123                                           &natural_height);
9124     }
9125   else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
9126     {
9127       CLUTTER_NOTE (LAYOUT, "Preferred size (width-for-height)");
9128       clutter_actor_get_preferred_height (self, -1,
9129                                           &min_height,
9130                                           &natural_height);
9131       clutter_actor_get_preferred_width (self, natural_height,
9132                                          &min_width,
9133                                          &natural_width);
9134     }
9135   else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
9136     {
9137       CLUTTER_NOTE (LAYOUT, "Preferred size (content-size)");
9138 
9139       if (priv->content != NULL)
9140         clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height);
9141     }
9142   else
9143     {
9144       CLUTTER_NOTE (LAYOUT, "Unknown request mode");
9145     }
9146 
9147   if (min_width_p)
9148     *min_width_p = min_width;
9149 
9150   if (min_height_p)
9151     *min_height_p = min_height;
9152 
9153   if (natural_width_p)
9154     *natural_width_p = natural_width;
9155 
9156   if (natural_height_p)
9157     *natural_height_p = natural_height;
9158 }
9159 
9160 /*< private >
9161  * effective_align:
9162  * @align: a #ClutterActorAlign
9163  * @direction: a #ClutterTextDirection
9164  *
9165  * Retrieves the correct alignment depending on the text direction
9166  *
9167  * Return value: the effective alignment
9168  */
9169 static ClutterActorAlign
effective_align(ClutterActorAlign align,ClutterTextDirection direction)9170 effective_align (ClutterActorAlign    align,
9171                  ClutterTextDirection direction)
9172 {
9173   ClutterActorAlign res;
9174 
9175   switch (align)
9176     {
9177     case CLUTTER_ACTOR_ALIGN_START:
9178       res = (direction == CLUTTER_TEXT_DIRECTION_RTL)
9179           ? CLUTTER_ACTOR_ALIGN_END
9180           : CLUTTER_ACTOR_ALIGN_START;
9181       break;
9182 
9183     case CLUTTER_ACTOR_ALIGN_END:
9184       res = (direction == CLUTTER_TEXT_DIRECTION_RTL)
9185           ? CLUTTER_ACTOR_ALIGN_START
9186           : CLUTTER_ACTOR_ALIGN_END;
9187       break;
9188 
9189     default:
9190       res = align;
9191       break;
9192     }
9193 
9194   return res;
9195 }
9196 
9197 /*< private >
9198  * _clutter_actor_get_effective_x_align:
9199  * @self: a #ClutterActor
9200  *
9201  * Retrieves the effective horizontal alignment, taking into
9202  * consideration the text direction of @self.
9203  *
9204  * Return value: the effective horizontal alignment
9205  */
9206 ClutterActorAlign
_clutter_actor_get_effective_x_align(ClutterActor * self)9207 _clutter_actor_get_effective_x_align (ClutterActor *self)
9208 {
9209   return effective_align (clutter_actor_get_x_align (self),
9210                           clutter_actor_get_text_direction (self));
9211 }
9212 
9213 static inline void
adjust_for_margin(float margin_start,float margin_end,float * minimum_size,float * natural_size,float * allocated_start,float * allocated_end)9214 adjust_for_margin (float  margin_start,
9215                    float  margin_end,
9216                    float *minimum_size,
9217                    float *natural_size,
9218                    float *allocated_start,
9219                    float *allocated_end)
9220 {
9221   float min_size = *minimum_size;
9222   float nat_size = *natural_size;
9223   float start = *allocated_start;
9224   float end = *allocated_end;
9225 
9226   min_size = MAX (min_size - (margin_start + margin_end), 0);
9227   nat_size = MAX (nat_size - (margin_start + margin_end), 0);
9228 
9229   *minimum_size = min_size;
9230   *natural_size = nat_size;
9231 
9232   start += margin_start;
9233   end -= margin_end;
9234 
9235   if (end - start >= 0)
9236     {
9237       *allocated_start = start;
9238       *allocated_end = end;
9239     }
9240 }
9241 
9242 static inline void
adjust_for_alignment(ClutterActorAlign alignment,float natural_size,float * allocated_start,float * allocated_end)9243 adjust_for_alignment (ClutterActorAlign  alignment,
9244                       float              natural_size,
9245                       float             *allocated_start,
9246                       float             *allocated_end)
9247 {
9248   float allocated_size = *allocated_end - *allocated_start;
9249 
9250   if (allocated_size <= 0.f)
9251     return;
9252 
9253   switch (alignment)
9254     {
9255     case CLUTTER_ACTOR_ALIGN_FILL:
9256       /* do nothing */
9257       break;
9258 
9259     case CLUTTER_ACTOR_ALIGN_START:
9260       /* keep start */
9261       *allocated_end = *allocated_start + MIN (natural_size, allocated_size);
9262       break;
9263 
9264     case CLUTTER_ACTOR_ALIGN_END:
9265       if (allocated_size > natural_size)
9266         {
9267           *allocated_start += (allocated_size - natural_size);
9268           *allocated_end = *allocated_start + natural_size;
9269         }
9270       break;
9271 
9272     case CLUTTER_ACTOR_ALIGN_CENTER:
9273       if (allocated_size > natural_size)
9274         {
9275           *allocated_start += floorf ((allocated_size - natural_size) / 2);
9276           *allocated_end = *allocated_start + MIN (allocated_size, natural_size);
9277         }
9278       break;
9279     }
9280 }
9281 
9282 /*< private >
9283  * clutter_actor_adjust_width:
9284  * @self: a #ClutterActor
9285  * @minimum_width: (inout): the actor's preferred minimum width, which
9286  *   will be adjusted depending on the margin
9287  * @natural_width: (inout): the actor's preferred natural width, which
9288  *   will be adjusted depending on the margin
9289  * @adjusted_x1: (out): the adjusted x1 for the actor's bounding box
9290  * @adjusted_x2: (out): the adjusted x2 for the actor's bounding box
9291  *
9292  * Adjusts the preferred and allocated position and size of an actor,
9293  * depending on the margin and alignment properties.
9294  */
9295 static void
clutter_actor_adjust_width(ClutterActor * self,gfloat * minimum_width,gfloat * natural_width,gfloat * adjusted_x1,gfloat * adjusted_x2)9296 clutter_actor_adjust_width (ClutterActor *self,
9297                             gfloat       *minimum_width,
9298                             gfloat       *natural_width,
9299                             gfloat       *adjusted_x1,
9300                             gfloat       *adjusted_x2)
9301 {
9302   ClutterTextDirection text_dir;
9303   const ClutterLayoutInfo *info;
9304 
9305   info = _clutter_actor_get_layout_info_or_defaults (self);
9306   text_dir = clutter_actor_get_text_direction (self);
9307 
9308   CLUTTER_NOTE (LAYOUT, "Adjusting allocated X and width");
9309 
9310   /* this will tweak natural_width to remove the margin, so that
9311    * adjust_for_alignment() will use the correct size
9312    */
9313   adjust_for_margin (info->margin.left, info->margin.right,
9314                      minimum_width, natural_width,
9315                      adjusted_x1, adjusted_x2);
9316 
9317   adjust_for_alignment (effective_align (info->x_align, text_dir),
9318                         *natural_width,
9319                         adjusted_x1, adjusted_x2);
9320 }
9321 
9322 /*< private >
9323  * clutter_actor_adjust_height:
9324  * @self: a #ClutterActor
9325  * @minimum_height: (inout): the actor's preferred minimum height, which
9326  *   will be adjusted depending on the margin
9327  * @natural_height: (inout): the actor's preferred natural height, which
9328  *   will be adjusted depending on the margin
9329  * @adjusted_y1: (out): the adjusted y1 for the actor's bounding box
9330  * @adjusted_y2: (out): the adjusted y2 for the actor's bounding box
9331  *
9332  * Adjusts the preferred and allocated position and size of an actor,
9333  * depending on the margin and alignment properties.
9334  */
9335 static void
clutter_actor_adjust_height(ClutterActor * self,gfloat * minimum_height,gfloat * natural_height,gfloat * adjusted_y1,gfloat * adjusted_y2)9336 clutter_actor_adjust_height (ClutterActor *self,
9337                              gfloat       *minimum_height,
9338                              gfloat       *natural_height,
9339                              gfloat       *adjusted_y1,
9340                              gfloat       *adjusted_y2)
9341 {
9342   const ClutterLayoutInfo *info;
9343 
9344   info = _clutter_actor_get_layout_info_or_defaults (self);
9345 
9346   CLUTTER_NOTE (LAYOUT, "Adjusting allocated Y and height");
9347 
9348   /* this will tweak natural_height to remove the margin, so that
9349    * adjust_for_alignment() will use the correct size
9350    */
9351   adjust_for_margin (info->margin.top, info->margin.bottom,
9352                      minimum_height, natural_height,
9353                      adjusted_y1,
9354                      adjusted_y2);
9355 
9356   /* we don't use effective_align() here, because text direction
9357    * only affects the horizontal axis
9358    */
9359   adjust_for_alignment (info->y_align,
9360                         *natural_height,
9361                         adjusted_y1,
9362                         adjusted_y2);
9363 
9364 }
9365 
9366 /* looks for a cached size request for this for_size. If not
9367  * found, returns the oldest entry so it can be overwritten */
9368 static gboolean
_clutter_actor_get_cached_size_request(gfloat for_size,SizeRequest * cached_size_requests,SizeRequest ** result)9369 _clutter_actor_get_cached_size_request (gfloat         for_size,
9370                                         SizeRequest   *cached_size_requests,
9371                                         SizeRequest  **result)
9372 {
9373   guint i;
9374 
9375   *result = &cached_size_requests[0];
9376 
9377   for (i = 0; i < N_CACHED_SIZE_REQUESTS; i++)
9378     {
9379       SizeRequest *sr;
9380 
9381       sr = &cached_size_requests[i];
9382 
9383       if (sr->age > 0 &&
9384           sr->for_size == for_size)
9385         {
9386           CLUTTER_NOTE (LAYOUT, "Size cache hit for size: %.2f", for_size);
9387           *result = sr;
9388           return TRUE;
9389         }
9390       else if (sr->age < (*result)->age)
9391         {
9392           *result = sr;
9393         }
9394     }
9395 
9396   CLUTTER_NOTE (LAYOUT, "Size cache miss for size: %.2f", for_size);
9397 
9398   return FALSE;
9399 }
9400 
9401 static void
clutter_actor_update_preferred_size_for_constraints(ClutterActor * self,ClutterOrientation direction,float for_size,float * minimum_size,float * natural_size)9402 clutter_actor_update_preferred_size_for_constraints (ClutterActor *self,
9403                                                      ClutterOrientation direction,
9404                                                      float for_size,
9405                                                      float *minimum_size,
9406                                                      float *natural_size)
9407 {
9408   ClutterActorPrivate *priv = self->priv;
9409   const GList *constraints, *l;
9410 
9411   if (priv->constraints == NULL)
9412     return;
9413 
9414   constraints = _clutter_meta_group_peek_metas (priv->constraints);
9415   for (l = constraints; l != NULL; l = l->next)
9416     {
9417       ClutterConstraint *constraint = l->data;
9418       ClutterActorMeta *meta = l->data;
9419 
9420       if (!clutter_actor_meta_get_enabled (meta))
9421         continue;
9422 
9423       clutter_constraint_update_preferred_size (constraint, self,
9424                                                 direction,
9425                                                 for_size,
9426                                                 minimum_size,
9427                                                 natural_size);
9428 
9429       CLUTTER_NOTE (LAYOUT,
9430                     "Preferred %s of '%s' after constraint '%s': "
9431                     "{ min:%.2f, nat:%.2f }",
9432                     direction == CLUTTER_ORIENTATION_HORIZONTAL
9433                       ? "width"
9434                       : "height",
9435                     _clutter_actor_get_debug_name (self),
9436                     _clutter_actor_meta_get_debug_name (meta),
9437                     *minimum_size, *natural_size);
9438     }
9439 }
9440 
9441 /**
9442  * clutter_actor_get_preferred_width:
9443  * @self: A #ClutterActor
9444  * @for_height: available height when computing the preferred width,
9445  *   or a negative value to indicate that no height is defined
9446  * @min_width_p: (out) (allow-none): return location for minimum width,
9447  *   or %NULL
9448  * @natural_width_p: (out) (allow-none): return location for the natural
9449  *   width, or %NULL
9450  *
9451  * Computes the requested minimum and natural widths for an actor,
9452  * optionally depending on the specified height, or if they are
9453  * already computed, returns the cached values.
9454  *
9455  * An actor may not get its request - depending on the layout
9456  * manager that's in effect.
9457  *
9458  * A request should not incorporate the actor's scale or anchor point;
9459  * those transformations do not affect layout, only rendering.
9460  *
9461  * Since: 0.8
9462  */
9463 void
clutter_actor_get_preferred_width(ClutterActor * self,gfloat for_height,gfloat * min_width_p,gfloat * natural_width_p)9464 clutter_actor_get_preferred_width (ClutterActor *self,
9465                                    gfloat        for_height,
9466                                    gfloat       *min_width_p,
9467                                    gfloat       *natural_width_p)
9468 {
9469   float request_min_width, request_natural_width;
9470   SizeRequest *cached_size_request;
9471   const ClutterLayoutInfo *info;
9472   ClutterActorPrivate *priv;
9473   gboolean found_in_cache;
9474 
9475   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9476 
9477   priv = self->priv;
9478 
9479   info = _clutter_actor_get_layout_info_or_defaults (self);
9480 
9481   /* we shortcircuit the case of a fixed size set using set_width() */
9482   if (priv->min_width_set && priv->natural_width_set)
9483     {
9484       if (min_width_p != NULL)
9485         *min_width_p = info->minimum.width + (info->margin.left + info->margin.right);
9486 
9487       if (natural_width_p != NULL)
9488         *natural_width_p = info->natural.width + (info->margin.left + info->margin.right);
9489 
9490       return;
9491     }
9492 
9493   /* the remaining cases are:
9494    *
9495    *   - either min_width or natural_width have been set
9496    *   - neither min_width or natural_width have been set
9497    *
9498    * in both cases, we go through the cache (and through the actor in case
9499    * of cache misses) and determine the authoritative value depending on
9500    * the *_set flags.
9501    */
9502 
9503   if (!priv->needs_width_request)
9504     {
9505       found_in_cache =
9506         _clutter_actor_get_cached_size_request (for_height,
9507                                                 priv->width_requests,
9508                                                 &cached_size_request);
9509     }
9510   else
9511     {
9512       /* if the actor needs a width request we use the first slot */
9513       found_in_cache = FALSE;
9514       cached_size_request = &priv->width_requests[0];
9515     }
9516 
9517   if (!found_in_cache)
9518     {
9519       gfloat minimum_width, natural_width;
9520       ClutterActorClass *klass;
9521 
9522       minimum_width = natural_width = 0;
9523 
9524       /* adjust for the margin */
9525       if (for_height >= 0)
9526         {
9527           for_height -= (info->margin.top + info->margin.bottom);
9528           if (for_height < 0)
9529             for_height = 0;
9530         }
9531 
9532       CLUTTER_NOTE (LAYOUT, "Width request for %.2f px", for_height);
9533 
9534       klass = CLUTTER_ACTOR_GET_CLASS (self);
9535       klass->get_preferred_width (self, for_height,
9536                                   &minimum_width,
9537                                   &natural_width);
9538 
9539       /* adjust for constraints */
9540       clutter_actor_update_preferred_size_for_constraints (self,
9541                                                            CLUTTER_ORIENTATION_HORIZONTAL,
9542                                                            for_height,
9543                                                            &minimum_width,
9544                                                            &natural_width);
9545 
9546       /* adjust for the margin */
9547       minimum_width += (info->margin.left + info->margin.right);
9548       natural_width += (info->margin.left + info->margin.right);
9549 
9550       /* Due to accumulated float errors, it's better not to warn
9551        * on this, but just fix it.
9552        */
9553       if (natural_width < minimum_width)
9554 	natural_width = minimum_width;
9555 
9556       cached_size_request->min_size = minimum_width;
9557       cached_size_request->natural_size = natural_width;
9558       cached_size_request->for_size = for_height;
9559       cached_size_request->age = priv->cached_width_age;
9560 
9561       priv->cached_width_age += 1;
9562       priv->needs_width_request = FALSE;
9563     }
9564 
9565   if (!priv->min_width_set)
9566     request_min_width = cached_size_request->min_size;
9567   else
9568     request_min_width = info->margin.left
9569                       + info->minimum.width
9570                       + info->margin.right;
9571 
9572   if (!priv->natural_width_set)
9573     request_natural_width = cached_size_request->natural_size;
9574   else
9575     request_natural_width = info->margin.left
9576                           + info->natural.width
9577                           + info->margin.right;
9578 
9579   if (min_width_p)
9580     *min_width_p = request_min_width;
9581 
9582   if (natural_width_p)
9583     *natural_width_p = request_natural_width;
9584 }
9585 
9586 /**
9587  * clutter_actor_get_preferred_height:
9588  * @self: A #ClutterActor
9589  * @for_width: available width to assume in computing desired height,
9590  *   or a negative value to indicate that no width is defined
9591  * @min_height_p: (out) (allow-none): return location for minimum height,
9592  *   or %NULL
9593  * @natural_height_p: (out) (allow-none): return location for natural
9594  *   height, or %NULL
9595  *
9596  * Computes the requested minimum and natural heights for an actor,
9597  * or if they are already computed, returns the cached values.
9598  *
9599  * An actor may not get its request - depending on the layout
9600  * manager that's in effect.
9601  *
9602  * A request should not incorporate the actor's scale or anchor point;
9603  * those transformations do not affect layout, only rendering.
9604  *
9605  * Since: 0.8
9606  */
9607 void
clutter_actor_get_preferred_height(ClutterActor * self,gfloat for_width,gfloat * min_height_p,gfloat * natural_height_p)9608 clutter_actor_get_preferred_height (ClutterActor *self,
9609                                     gfloat        for_width,
9610                                     gfloat       *min_height_p,
9611                                     gfloat       *natural_height_p)
9612 {
9613   float request_min_height, request_natural_height;
9614   SizeRequest *cached_size_request;
9615   const ClutterLayoutInfo *info;
9616   ClutterActorPrivate *priv;
9617   gboolean found_in_cache;
9618 
9619   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9620 
9621   priv = self->priv;
9622 
9623   info = _clutter_actor_get_layout_info_or_defaults (self);
9624 
9625   /* we shortcircuit the case of a fixed size set using set_height() */
9626   if (priv->min_height_set && priv->natural_height_set)
9627     {
9628       if (min_height_p != NULL)
9629         *min_height_p = info->minimum.height + (info->margin.top + info->margin.bottom);
9630 
9631       if (natural_height_p != NULL)
9632         *natural_height_p = info->natural.height + (info->margin.top + info->margin.bottom);
9633 
9634       return;
9635     }
9636 
9637   /* the remaining cases are:
9638    *
9639    *   - either min_height or natural_height have been set
9640    *   - neither min_height or natural_height have been set
9641    *
9642    * in both cases, we go through the cache (and through the actor in case
9643    * of cache misses) and determine the authoritative value depending on
9644    * the *_set flags.
9645    */
9646 
9647   if (!priv->needs_height_request)
9648     {
9649       found_in_cache =
9650         _clutter_actor_get_cached_size_request (for_width,
9651                                                 priv->height_requests,
9652                                                 &cached_size_request);
9653     }
9654   else
9655     {
9656       found_in_cache = FALSE;
9657       cached_size_request = &priv->height_requests[0];
9658     }
9659 
9660   if (!found_in_cache)
9661     {
9662       gfloat minimum_height, natural_height;
9663       ClutterActorClass *klass;
9664 
9665       minimum_height = natural_height = 0;
9666 
9667       CLUTTER_NOTE (LAYOUT, "Height request for %.2f px", for_width);
9668 
9669       /* adjust for margin */
9670       if (for_width >= 0)
9671         {
9672           for_width -= (info->margin.left + info->margin.right);
9673           if (for_width < 0)
9674             for_width = 0;
9675         }
9676 
9677       klass = CLUTTER_ACTOR_GET_CLASS (self);
9678       klass->get_preferred_height (self, for_width,
9679                                    &minimum_height,
9680                                    &natural_height);
9681 
9682       /* adjust for constraints */
9683       clutter_actor_update_preferred_size_for_constraints (self,
9684                                                            CLUTTER_ORIENTATION_VERTICAL,
9685                                                            for_width,
9686                                                            &minimum_height,
9687                                                            &natural_height);
9688 
9689       /* adjust for margin */
9690       minimum_height += (info->margin.top + info->margin.bottom);
9691       natural_height += (info->margin.top + info->margin.bottom);
9692 
9693       /* Due to accumulated float errors, it's better not to warn
9694        * on this, but just fix it.
9695        */
9696       if (natural_height < minimum_height)
9697 	natural_height = minimum_height;
9698 
9699       cached_size_request->min_size = minimum_height;
9700       cached_size_request->natural_size = natural_height;
9701       cached_size_request->for_size = for_width;
9702       cached_size_request->age = priv->cached_height_age;
9703 
9704       priv->cached_height_age += 1;
9705       priv->needs_height_request = FALSE;
9706     }
9707 
9708   if (!priv->min_height_set)
9709     request_min_height = cached_size_request->min_size;
9710   else
9711     request_min_height = info->margin.top
9712                        + info->minimum.height
9713                        + info->margin.bottom;
9714 
9715   if (!priv->natural_height_set)
9716     request_natural_height = cached_size_request->natural_size;
9717   else
9718     request_natural_height = info->margin.top
9719                            + info->natural.height
9720                            + info->margin.bottom;
9721 
9722   if (min_height_p)
9723     *min_height_p = request_min_height;
9724 
9725   if (natural_height_p)
9726     *natural_height_p = request_natural_height;
9727 }
9728 
9729 /**
9730  * clutter_actor_get_allocation_box:
9731  * @self: A #ClutterActor
9732  * @box: (out): the function fills this in with the actor's allocation
9733  *
9734  * Gets the layout box an actor has been assigned. The allocation can
9735  * only be assumed valid inside a paint() method; anywhere else, it
9736  * may be out-of-date.
9737  *
9738  * An allocation does not incorporate the actor's scale or anchor point;
9739  * those transformations do not affect layout, only rendering.
9740  *
9741  * Do not call any of the clutter_actor_get_allocation_*() family
9742  * of functions inside the implementation of the get_preferred_width()
9743  * or get_preferred_height() virtual functions.
9744  *
9745  * Since: 0.8
9746  */
9747 void
clutter_actor_get_allocation_box(ClutterActor * self,ClutterActorBox * box)9748 clutter_actor_get_allocation_box (ClutterActor    *self,
9749                                   ClutterActorBox *box)
9750 {
9751   g_return_if_fail (CLUTTER_IS_ACTOR (self));
9752 
9753   /* XXX - if needs_allocation=TRUE, we can either 1) g_return_if_fail,
9754    * which limits calling get_allocation to inside paint() basically; or
9755    * we can 2) force a layout, which could be expensive if someone calls
9756    * get_allocation somewhere silly; or we can 3) just return the latest
9757    * value, allowing it to be out-of-date, and assume people know what
9758    * they are doing.
9759    *
9760    * The least-surprises approach that keeps existing code working is
9761    * likely to be 2). People can end up doing some inefficient things,
9762    * though, and in general code that requires 2) is probably broken.
9763    */
9764 
9765   /* this implements 2) */
9766   if (G_UNLIKELY (self->priv->needs_allocation))
9767     {
9768       ClutterActor *stage = _clutter_actor_get_stage_internal (self);
9769 
9770       /* do not queue a relayout on an unparented actor */
9771       if (stage)
9772         _clutter_stage_maybe_relayout (stage);
9773     }
9774 
9775   /* commenting out the code above and just keeping this assigment
9776    * implements 3)
9777    */
9778   *box = self->priv->allocation;
9779 }
9780 
9781 static void
clutter_actor_update_constraints(ClutterActor * self,ClutterActorBox * allocation)9782 clutter_actor_update_constraints (ClutterActor    *self,
9783                                   ClutterActorBox *allocation)
9784 {
9785   ClutterActorPrivate *priv = self->priv;
9786   const GList *constraints, *l;
9787 
9788   if (priv->constraints == NULL)
9789     return;
9790 
9791   constraints = _clutter_meta_group_peek_metas (priv->constraints);
9792   for (l = constraints; l != NULL; l = l->next)
9793     {
9794       ClutterConstraint *constraint = l->data;
9795       ClutterActorMeta *meta = l->data;
9796       gboolean changed = FALSE;
9797 
9798       if (clutter_actor_meta_get_enabled (meta))
9799         {
9800           changed |=
9801             clutter_constraint_update_allocation (constraint,
9802                                                   self,
9803                                                   allocation);
9804 
9805           CLUTTER_NOTE (LAYOUT,
9806                         "Allocation of '%s' after constraint '%s': "
9807                         "{ %.2f, %.2f, %.2f, %.2f } (changed:%s)",
9808                         _clutter_actor_get_debug_name (self),
9809                         _clutter_actor_meta_get_debug_name (meta),
9810                         allocation->x1,
9811                         allocation->y1,
9812                         allocation->x2,
9813                         allocation->y2,
9814                         changed ? "yes" : "no");
9815         }
9816     }
9817 }
9818 
9819 /*< private >
9820  * clutter_actor_adjust_allocation:
9821  * @self: a #ClutterActor
9822  * @allocation: (inout): the allocation to adjust
9823  *
9824  * Adjusts the passed allocation box taking into account the actor's
9825  * layout information, like alignment, expansion, and margin.
9826  */
9827 static void
clutter_actor_adjust_allocation(ClutterActor * self,ClutterActorBox * allocation)9828 clutter_actor_adjust_allocation (ClutterActor    *self,
9829                                  ClutterActorBox *allocation)
9830 {
9831   ClutterActorBox adj_allocation;
9832   float alloc_width, alloc_height;
9833   float min_width, min_height;
9834   float nat_width, nat_height;
9835   ClutterRequestMode req_mode;
9836 
9837   adj_allocation = *allocation;
9838 
9839   clutter_actor_box_get_size (allocation, &alloc_width, &alloc_height);
9840 
9841   /* There's no point in trying to adjust a zero-sized actor */
9842   if (alloc_width == 0.f && alloc_height == 0.f)
9843     return;
9844 
9845   /* we want to hit the cache, so we use the public API */
9846   req_mode = clutter_actor_get_request_mode (self);
9847 
9848   if (req_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
9849     {
9850       clutter_actor_get_preferred_width (self, -1,
9851                                          &min_width,
9852                                          &nat_width);
9853       clutter_actor_get_preferred_height (self, alloc_width,
9854                                           &min_height,
9855                                           &nat_height);
9856     }
9857   else if (req_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
9858     {
9859       clutter_actor_get_preferred_height (self, -1,
9860                                           &min_height,
9861                                           &nat_height);
9862       clutter_actor_get_preferred_width (self, alloc_height,
9863                                          &min_width,
9864                                          &nat_width);
9865     }
9866   else if (req_mode == CLUTTER_REQUEST_CONTENT_SIZE)
9867     {
9868       min_width = min_height = 0;
9869       nat_width = nat_height = 0;
9870 
9871       if (self->priv->content != NULL)
9872         clutter_content_get_preferred_size (self->priv->content, &nat_width, &nat_height);
9873     }
9874 
9875 #ifdef CLUTTER_ENABLE_DEBUG
9876   /* warn about underallocations */
9877   if (_clutter_diagnostic_enabled () &&
9878       (floorf (min_width - alloc_width) > 0 ||
9879        floorf (min_height - alloc_height) > 0))
9880     {
9881       ClutterActor *parent = clutter_actor_get_parent (self);
9882 
9883       /* the only actors that are allowed to be underallocated are the Stage,
9884        * as it doesn't have an implicit size, and Actors that specifically
9885        * told us that they want to opt-out from layout control mechanisms
9886        * through the NO_LAYOUT escape hatch.
9887        */
9888       if (parent != NULL &&
9889           !(self->flags & CLUTTER_ACTOR_NO_LAYOUT) != 0)
9890         {
9891           g_warning (G_STRLOC ": The actor '%s' is getting an allocation "
9892                      "of %.2f x %.2f from its parent actor '%s', but its "
9893                      "requested minimum size is of %.2f x %.2f",
9894                      _clutter_actor_get_debug_name (self),
9895                      alloc_width, alloc_height,
9896                      _clutter_actor_get_debug_name (parent),
9897                      min_width, min_height);
9898         }
9899     }
9900 #endif
9901 
9902   clutter_actor_adjust_width (self,
9903                               &min_width,
9904                               &nat_width,
9905                               &adj_allocation.x1,
9906                               &adj_allocation.x2);
9907 
9908   clutter_actor_adjust_height (self,
9909                                &min_height,
9910                                &nat_height,
9911                                &adj_allocation.y1,
9912                                &adj_allocation.y2);
9913 
9914   /* we maintain the invariant that an allocation cannot be adjusted
9915    * to be outside the parent-given box
9916    */
9917   if (adj_allocation.x1 < allocation->x1 ||
9918       adj_allocation.y1 < allocation->y1 ||
9919       adj_allocation.x2 > allocation->x2 ||
9920       adj_allocation.y2 > allocation->y2)
9921     {
9922       g_warning (G_STRLOC ": The actor '%s' tried to adjust its allocation "
9923                  "to { %.2f, %.2f, %.2f, %.2f }, which is outside of its "
9924                  "original allocation of { %.2f, %.2f, %.2f, %.2f }",
9925                  _clutter_actor_get_debug_name (self),
9926                  adj_allocation.x1, adj_allocation.y1,
9927                  adj_allocation.x2 - adj_allocation.x1,
9928                  adj_allocation.y2 - adj_allocation.y1,
9929                  allocation->x1, allocation->y1,
9930                  allocation->x2 - allocation->x1,
9931                  allocation->y2 - allocation->y1);
9932       return;
9933     }
9934 
9935   *allocation = adj_allocation;
9936 }
9937 
9938 static void
clutter_actor_allocate_internal(ClutterActor * self,const ClutterActorBox * allocation,ClutterAllocationFlags flags)9939 clutter_actor_allocate_internal (ClutterActor           *self,
9940                                  const ClutterActorBox  *allocation,
9941                                  ClutterAllocationFlags  flags)
9942 {
9943   ClutterActorClass *klass;
9944 
9945   CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
9946 
9947   CLUTTER_NOTE (LAYOUT, "Calling %s::allocate()",
9948                 _clutter_actor_get_debug_name (self));
9949 
9950   klass = CLUTTER_ACTOR_GET_CLASS (self);
9951   klass->allocate (self, allocation, flags);
9952 
9953   CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT);
9954 
9955   /* Caller should call clutter_actor_queue_redraw() if needed
9956    * for that particular case.
9957    */
9958 }
9959 
9960 /**
9961  * clutter_actor_allocate:
9962  * @self: A #ClutterActor
9963  * @box: new allocation of the actor, in parent-relative coordinates
9964  * @flags: flags that control the allocation
9965  *
9966  * Assigns the size of a #ClutterActor from the given @box.
9967  *
9968  * This function should only be called on the children of an actor when
9969  * overriding the #ClutterActorClass.allocate() virtual function.
9970  *
9971  * This function will adjust the stored allocation to take into account
9972  * the alignment flags set in the #ClutterActor:x-align and
9973  * #ClutterActor:y-align properties, as well as the margin values set in
9974  * the #ClutterActor:margin-top, #ClutterActor:margin-right,
9975  * #ClutterActor:margin-bottom, and #ClutterActor:margin-left properties.
9976  *
9977  * This function will respect the easing state of the #ClutterActor and
9978  * interpolate between the current allocation and the new one if the
9979  * easing state duration is a positive value.
9980  *
9981  * Actors can know from their allocation box whether they have moved
9982  * with respect to their parent actor. The @flags parameter describes
9983  * additional information about the allocation, for instance whether
9984  * the parent has moved with respect to the stage, for example because
9985  * a grandparent's origin has moved.
9986  *
9987  * Since: 0.8
9988  */
9989 void
clutter_actor_allocate(ClutterActor * self,const ClutterActorBox * box,ClutterAllocationFlags flags)9990 clutter_actor_allocate (ClutterActor           *self,
9991                         const ClutterActorBox  *box,
9992                         ClutterAllocationFlags  flags)
9993 {
9994   ClutterActorBox old_allocation, real_allocation;
9995   gboolean origin_changed, child_moved, size_changed;
9996   gboolean stage_allocation_changed;
9997   ClutterActorPrivate *priv;
9998 
9999   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10000   if (G_UNLIKELY (_clutter_actor_get_stage_internal (self) == NULL))
10001     {
10002       g_warning ("Spurious clutter_actor_allocate called for actor %p/%s "
10003                  "which isn't a descendent of the stage!\n",
10004                  self, _clutter_actor_get_debug_name (self));
10005       return;
10006     }
10007 
10008   priv = self->priv;
10009 
10010   old_allocation = priv->allocation;
10011   real_allocation = *box;
10012 
10013   /* constraints are allowed to modify the allocation only here; we do
10014    * this prior to all the other checks so that we can bail out if the
10015    * allocation did not change
10016    */
10017   clutter_actor_update_constraints (self, &real_allocation);
10018 
10019   /* adjust the allocation depending on the align/margin properties */
10020   clutter_actor_adjust_allocation (self, &real_allocation);
10021 
10022   if (real_allocation.x2 < real_allocation.x1 ||
10023       real_allocation.y2 < real_allocation.y1)
10024     {
10025       g_warning (G_STRLOC ": Actor '%s' tried to allocate a size of %.2f x %.2f",
10026                  _clutter_actor_get_debug_name (self),
10027                  real_allocation.x2 - real_allocation.x1,
10028                  real_allocation.y2 - real_allocation.y1);
10029     }
10030 
10031   /* we allow 0-sized actors, but not negative-sized ones */
10032   real_allocation.x2 = MAX (real_allocation.x2, real_allocation.x1);
10033   real_allocation.y2 = MAX (real_allocation.y2, real_allocation.y1);
10034 
10035   origin_changed = (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED);
10036 
10037   child_moved = (real_allocation.x1 != old_allocation.x1 ||
10038                  real_allocation.y1 != old_allocation.y1);
10039 
10040   size_changed = (real_allocation.x2 != old_allocation.x2 ||
10041                   real_allocation.y2 != old_allocation.y2);
10042 
10043   if (origin_changed || child_moved || size_changed)
10044     stage_allocation_changed = TRUE;
10045   else
10046     stage_allocation_changed = FALSE;
10047 
10048   /* If we get an allocation "out of the blue"
10049    * (we did not queue relayout), then we want to
10050    * ignore it. But if we have needs_allocation set,
10051    * we want to guarantee that allocate() virtual
10052    * method is always called, i.e. that queue_relayout()
10053    * always results in an allocate() invocation on
10054    * an actor.
10055    *
10056    * The optimization here is to avoid re-allocating
10057    * actors that did not queue relayout and were
10058    * not moved.
10059    */
10060   if (!priv->needs_allocation && !stage_allocation_changed)
10061     {
10062       CLUTTER_NOTE (LAYOUT, "No allocation needed");
10063       return;
10064     }
10065 
10066   if (!stage_allocation_changed)
10067     {
10068       /* If the actor didn't move but needs_allocation is set, we just
10069        * need to allocate the children */
10070       clutter_actor_allocate_internal (self, &real_allocation, flags);
10071       return;
10072     }
10073 
10074   /* When ABSOLUTE_ORIGIN_CHANGED is passed in to
10075    * clutter_actor_allocate(), it indicates whether the parent has its
10076    * absolute origin moved; when passed in to ClutterActor::allocate()
10077    * virtual method though, it indicates whether the child has its
10078    * absolute origin moved.  So we set it when child_moved is TRUE
10079    */
10080   if (child_moved)
10081     flags |= CLUTTER_ABSOLUTE_ORIGIN_CHANGED;
10082 
10083   /* store the flags here, so that they can be propagated by the
10084    * transition code
10085    */
10086   self->priv->allocation_flags = flags;
10087 
10088   _clutter_actor_create_transition (self, obj_props[PROP_ALLOCATION],
10089                                     &priv->allocation,
10090                                     &real_allocation);
10091 }
10092 
10093 /**
10094  * clutter_actor_set_allocation:
10095  * @self: a #ClutterActor
10096  * @box: a #ClutterActorBox
10097  * @flags: allocation flags
10098  *
10099  * Stores the allocation of @self as defined by @box.
10100  *
10101  * This function can only be called from within the implementation of
10102  * the #ClutterActorClass.allocate() virtual function.
10103  *
10104  * The allocation should have been adjusted to take into account constraints,
10105  * alignment, and margin properties. If you are implementing a #ClutterActor
10106  * subclass that provides its own layout management policy for its children
10107  * instead of using a #ClutterLayoutManager delegate, you should not call
10108  * this function on the children of @self; instead, you should call
10109  * clutter_actor_allocate(), which will adjust the allocation box for
10110  * you.
10111  *
10112  * This function should only be used by subclasses of #ClutterActor
10113  * that wish to store their allocation but cannot chain up to the
10114  * parent's implementation; the default implementation of the
10115  * #ClutterActorClass.allocate() virtual function will call this
10116  * function.
10117  *
10118  * It is important to note that, while chaining up was the recommended
10119  * behaviour for #ClutterActor subclasses prior to the introduction of
10120  * this function, it is recommended to call clutter_actor_set_allocation()
10121  * instead.
10122  *
10123  * If the #ClutterActor is using a #ClutterLayoutManager delegate object
10124  * to handle the allocation of its children, this function will call
10125  * the clutter_layout_manager_allocate() function only if the
10126  * %CLUTTER_DELEGATE_LAYOUT flag is set on @flags, otherwise it is
10127  * expected that the subclass will call clutter_layout_manager_allocate()
10128  * by itself. For instance, the following code:
10129  *
10130  * |[<!-- language="C" -->
10131  * static void
10132  * my_actor_allocate (ClutterActor *actor,
10133  *                    const ClutterActorBox *allocation,
10134  *                    ClutterAllocationFlags flags)
10135  * {
10136  *   ClutterActorBox new_alloc;
10137  *   ClutterAllocationFlags new_flags;
10138  *
10139  *   adjust_allocation (allocation, &new_alloc);
10140  *
10141  *   new_flags = flags | CLUTTER_DELEGATE_LAYOUT;
10142  *
10143  *   // this will use the layout manager set on the actor
10144  *   clutter_actor_set_allocation (actor, &new_alloc, new_flags);
10145  * }
10146  * ]|
10147  *
10148  * is equivalent to this:
10149  *
10150  * |[<!-- language="C" -->
10151  * static void
10152  * my_actor_allocate (ClutterActor *actor,
10153  *                    const ClutterActorBox *allocation,
10154  *                    ClutterAllocationFlags flags)
10155  * {
10156  *   ClutterLayoutManager *layout;
10157  *   ClutterActorBox new_alloc;
10158  *
10159  *   adjust_allocation (allocation, &new_alloc);
10160  *
10161  *   clutter_actor_set_allocation (actor, &new_alloc, flags);
10162  *
10163  *   layout = clutter_actor_get_layout_manager (actor);
10164  *   clutter_layout_manager_allocate (layout,
10165  *                                    CLUTTER_CONTAINER (actor),
10166  *                                    &new_alloc,
10167  *                                    flags);
10168  * }
10169  * ]|
10170  *
10171  * Since: 1.10
10172  */
10173 void
clutter_actor_set_allocation(ClutterActor * self,const ClutterActorBox * box,ClutterAllocationFlags flags)10174 clutter_actor_set_allocation (ClutterActor           *self,
10175                               const ClutterActorBox  *box,
10176                               ClutterAllocationFlags  flags)
10177 {
10178   ClutterActorPrivate *priv;
10179   gboolean changed;
10180 
10181   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10182   g_return_if_fail (box != NULL);
10183 
10184   if (G_UNLIKELY (!CLUTTER_ACTOR_IN_RELAYOUT (self)))
10185     {
10186       g_critical (G_STRLOC ": The clutter_actor_set_allocation() function "
10187                   "can only be called from within the implementation of "
10188                   "the ClutterActor::allocate() virtual function.");
10189       return;
10190     }
10191 
10192   priv = self->priv;
10193 
10194   g_object_freeze_notify (G_OBJECT (self));
10195 
10196   changed = clutter_actor_set_allocation_internal (self, box, flags);
10197 
10198   /* we allocate our children before we notify changes in our geometry,
10199    * so that people connecting to properties will be able to get valid
10200    * data out of the sub-tree of the scene graph that has this actor at
10201    * the root.
10202    */
10203   clutter_actor_maybe_layout_children (self, box, flags);
10204 
10205   if (changed)
10206     {
10207       ClutterActorBox signal_box = priv->allocation;
10208       ClutterAllocationFlags signal_flags = priv->allocation_flags;
10209 
10210       g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0,
10211                      &signal_box,
10212                      signal_flags);
10213     }
10214 
10215   g_object_thaw_notify (G_OBJECT (self));
10216 }
10217 
10218 /**
10219  * clutter_actor_set_position:
10220  * @self: A #ClutterActor
10221  * @x: New left position of actor in pixels.
10222  * @y: New top position of actor in pixels.
10223  *
10224  * Sets the actor's fixed position in pixels relative to any parent
10225  * actor.
10226  *
10227  * If a layout manager is in use, this position will override the
10228  * layout manager and force a fixed position.
10229  */
10230 void
clutter_actor_set_position(ClutterActor * self,gfloat x,gfloat y)10231 clutter_actor_set_position (ClutterActor *self,
10232 			    gfloat        x,
10233 			    gfloat        y)
10234 {
10235   ClutterPoint new_position;
10236   ClutterPoint cur_position;
10237 
10238   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10239 
10240   clutter_point_init (&new_position, x, y);
10241 
10242   cur_position.x = clutter_actor_get_x (self);
10243   cur_position.y = clutter_actor_get_y (self);
10244 
10245   _clutter_actor_create_transition (self, obj_props[PROP_POSITION],
10246                                     &cur_position,
10247                                     &new_position);
10248 }
10249 
10250 /**
10251  * clutter_actor_get_fixed_position_set:
10252  * @self: A #ClutterActor
10253  *
10254  * Checks whether an actor has a fixed position set (and will thus be
10255  * unaffected by any layout manager).
10256  *
10257  * Return value: %TRUE if the fixed position is set on the actor
10258  *
10259  * Since: 0.8
10260  */
10261 gboolean
clutter_actor_get_fixed_position_set(ClutterActor * self)10262 clutter_actor_get_fixed_position_set (ClutterActor *self)
10263 {
10264   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
10265 
10266   return self->priv->position_set;
10267 }
10268 
10269 /**
10270  * clutter_actor_set_fixed_position_set:
10271  * @self: A #ClutterActor
10272  * @is_set: whether to use fixed position
10273  *
10274  * Sets whether an actor has a fixed position set (and will thus be
10275  * unaffected by any layout manager).
10276  *
10277  * Since: 0.8
10278  */
10279 void
clutter_actor_set_fixed_position_set(ClutterActor * self,gboolean is_set)10280 clutter_actor_set_fixed_position_set (ClutterActor *self,
10281                                       gboolean      is_set)
10282 {
10283   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10284 
10285   if (self->priv->position_set == (is_set != FALSE))
10286     return;
10287 
10288   if (!is_set)
10289     {
10290       ClutterLayoutInfo *info;
10291 
10292       /* Ensure we set back the default fixed position of 0,0 so that setting
10293 	 just one of x/y always atomically gets 0 for the other */
10294       info = _clutter_actor_peek_layout_info (self);
10295       if (info != NULL)
10296 	{
10297 	  info->fixed_pos.x = 0;
10298 	  info->fixed_pos.y = 0;
10299 	}
10300     }
10301 
10302   self->priv->position_set = is_set != FALSE;
10303   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIXED_POSITION_SET]);
10304 
10305   clutter_actor_queue_relayout (self);
10306 }
10307 
10308 /**
10309  * clutter_actor_move_by:
10310  * @self: A #ClutterActor
10311  * @dx: Distance to move Actor on X axis.
10312  * @dy: Distance to move Actor on Y axis.
10313  *
10314  * Moves an actor by the specified distance relative to its current
10315  * position in pixels.
10316  *
10317  * This function modifies the fixed position of an actor and thus removes
10318  * it from any layout management. Another way to move an actor is with an
10319  * anchor point, see clutter_actor_set_anchor_point(), or with an additional
10320  * translation, using clutter_actor_set_translation().
10321  *
10322  * Since: 0.2
10323  */
10324 void
clutter_actor_move_by(ClutterActor * self,gfloat dx,gfloat dy)10325 clutter_actor_move_by (ClutterActor *self,
10326 		       gfloat        dx,
10327 		       gfloat        dy)
10328 {
10329   const ClutterLayoutInfo *info;
10330   gfloat x, y;
10331 
10332   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10333 
10334   info = _clutter_actor_get_layout_info_or_defaults (self);
10335   x = info->fixed_pos.x;
10336   y = info->fixed_pos.y;
10337 
10338   clutter_actor_set_position (self, x + dx, y + dy);
10339 }
10340 
10341 static void
clutter_actor_set_min_width(ClutterActor * self,gfloat min_width)10342 clutter_actor_set_min_width (ClutterActor *self,
10343                              gfloat        min_width)
10344 {
10345   ClutterActorPrivate *priv = self->priv;
10346   ClutterActorBox old = { 0, };
10347   ClutterLayoutInfo *info;
10348 
10349   /* if we are setting the size on a top-level actor and the
10350    * backend only supports static top-levels (e.g. framebuffers)
10351    * then we ignore the passed value and we override it with
10352    * the stage implementation's preferred size.
10353    */
10354   if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
10355       clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
10356     return;
10357 
10358   info = _clutter_actor_get_layout_info (self);
10359 
10360   if (priv->min_width_set && min_width == info->minimum.width)
10361     return;
10362 
10363   g_object_freeze_notify (G_OBJECT (self));
10364 
10365   clutter_actor_store_old_geometry (self, &old);
10366 
10367   info->minimum.width = min_width;
10368   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH]);
10369   clutter_actor_set_min_width_set (self, TRUE);
10370 
10371   clutter_actor_notify_if_geometry_changed (self, &old);
10372 
10373   g_object_thaw_notify (G_OBJECT (self));
10374 
10375   clutter_actor_queue_relayout (self);
10376 }
10377 
10378 static void
clutter_actor_set_min_height(ClutterActor * self,gfloat min_height)10379 clutter_actor_set_min_height (ClutterActor *self,
10380                               gfloat        min_height)
10381 
10382 {
10383   ClutterActorPrivate *priv = self->priv;
10384   ClutterActorBox old = { 0, };
10385   ClutterLayoutInfo *info;
10386 
10387   /* if we are setting the size on a top-level actor and the
10388    * backend only supports static top-levels (e.g. framebuffers)
10389    * then we ignore the passed value and we override it with
10390    * the stage implementation's preferred size.
10391    */
10392   if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
10393       clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
10394     return;
10395 
10396   info = _clutter_actor_get_layout_info (self);
10397 
10398   if (priv->min_height_set && min_height == info->minimum.height)
10399     return;
10400 
10401   g_object_freeze_notify (G_OBJECT (self));
10402 
10403   clutter_actor_store_old_geometry (self, &old);
10404 
10405   info->minimum.height = min_height;
10406   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT]);
10407   clutter_actor_set_min_height_set (self, TRUE);
10408 
10409   clutter_actor_notify_if_geometry_changed (self, &old);
10410 
10411   g_object_thaw_notify (G_OBJECT (self));
10412 
10413   clutter_actor_queue_relayout (self);
10414 }
10415 
10416 static void
clutter_actor_set_natural_width(ClutterActor * self,gfloat natural_width)10417 clutter_actor_set_natural_width (ClutterActor *self,
10418                                  gfloat        natural_width)
10419 {
10420   ClutterActorPrivate *priv = self->priv;
10421   ClutterActorBox old = { 0, };
10422   ClutterLayoutInfo *info;
10423 
10424   /* if we are setting the size on a top-level actor and the
10425    * backend only supports static top-levels (e.g. framebuffers)
10426    * then we ignore the passed value and we override it with
10427    * the stage implementation's preferred size.
10428    */
10429   if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
10430       clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
10431     return;
10432 
10433   info = _clutter_actor_get_layout_info (self);
10434 
10435   if (priv->natural_width_set && natural_width == info->natural.width)
10436     return;
10437 
10438   g_object_freeze_notify (G_OBJECT (self));
10439 
10440   clutter_actor_store_old_geometry (self, &old);
10441 
10442   info->natural.width = natural_width;
10443   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH]);
10444   clutter_actor_set_natural_width_set (self, TRUE);
10445 
10446   clutter_actor_notify_if_geometry_changed (self, &old);
10447 
10448   g_object_thaw_notify (G_OBJECT (self));
10449 
10450   clutter_actor_queue_relayout (self);
10451 }
10452 
10453 static void
clutter_actor_set_natural_height(ClutterActor * self,gfloat natural_height)10454 clutter_actor_set_natural_height (ClutterActor *self,
10455                                   gfloat        natural_height)
10456 {
10457   ClutterActorPrivate *priv = self->priv;
10458   ClutterActorBox old = { 0, };
10459   ClutterLayoutInfo *info;
10460 
10461   /* if we are setting the size on a top-level actor and the
10462    * backend only supports static top-levels (e.g. framebuffers)
10463    * then we ignore the passed value and we override it with
10464    * the stage implementation's preferred size.
10465    */
10466   if (CLUTTER_ACTOR_IS_TOPLEVEL (self) &&
10467       clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC))
10468     return;
10469 
10470   info = _clutter_actor_get_layout_info (self);
10471 
10472   if (priv->natural_height_set && natural_height == info->natural.height)
10473     return;
10474 
10475   g_object_freeze_notify (G_OBJECT (self));
10476 
10477   clutter_actor_store_old_geometry (self, &old);
10478 
10479   info->natural.height = natural_height;
10480   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT]);
10481   clutter_actor_set_natural_height_set (self, TRUE);
10482 
10483   clutter_actor_notify_if_geometry_changed (self, &old);
10484 
10485   g_object_thaw_notify (G_OBJECT (self));
10486 
10487   clutter_actor_queue_relayout (self);
10488 }
10489 
10490 static void
clutter_actor_set_min_width_set(ClutterActor * self,gboolean use_min_width)10491 clutter_actor_set_min_width_set (ClutterActor *self,
10492                                  gboolean      use_min_width)
10493 {
10494   ClutterActorPrivate *priv = self->priv;
10495   ClutterActorBox old = { 0, };
10496 
10497   if (priv->min_width_set == (use_min_width != FALSE))
10498     return;
10499 
10500   clutter_actor_store_old_geometry (self, &old);
10501 
10502   priv->min_width_set = use_min_width != FALSE;
10503   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH_SET]);
10504 
10505   clutter_actor_notify_if_geometry_changed (self, &old);
10506 
10507   clutter_actor_queue_relayout (self);
10508 }
10509 
10510 static void
clutter_actor_set_min_height_set(ClutterActor * self,gboolean use_min_height)10511 clutter_actor_set_min_height_set (ClutterActor *self,
10512                                   gboolean      use_min_height)
10513 {
10514   ClutterActorPrivate *priv = self->priv;
10515   ClutterActorBox old = { 0, };
10516 
10517   if (priv->min_height_set == (use_min_height != FALSE))
10518     return;
10519 
10520   clutter_actor_store_old_geometry (self, &old);
10521 
10522   priv->min_height_set = use_min_height != FALSE;
10523   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT_SET]);
10524 
10525   clutter_actor_notify_if_geometry_changed (self, &old);
10526 
10527   clutter_actor_queue_relayout (self);
10528 }
10529 
10530 static void
clutter_actor_set_natural_width_set(ClutterActor * self,gboolean use_natural_width)10531 clutter_actor_set_natural_width_set (ClutterActor *self,
10532                                      gboolean      use_natural_width)
10533 {
10534   ClutterActorPrivate *priv = self->priv;
10535   ClutterActorBox old = { 0, };
10536 
10537   if (priv->natural_width_set == (use_natural_width != FALSE))
10538     return;
10539 
10540   clutter_actor_store_old_geometry (self, &old);
10541 
10542   priv->natural_width_set = use_natural_width != FALSE;
10543   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH_SET]);
10544 
10545   clutter_actor_notify_if_geometry_changed (self, &old);
10546 
10547   clutter_actor_queue_relayout (self);
10548 }
10549 
10550 static void
clutter_actor_set_natural_height_set(ClutterActor * self,gboolean use_natural_height)10551 clutter_actor_set_natural_height_set (ClutterActor *self,
10552                                       gboolean      use_natural_height)
10553 {
10554   ClutterActorPrivate *priv = self->priv;
10555   ClutterActorBox old = { 0, };
10556 
10557   if (priv->natural_height_set == (use_natural_height != FALSE))
10558     return;
10559 
10560   clutter_actor_store_old_geometry (self, &old);
10561 
10562   priv->natural_height_set = use_natural_height != FALSE;
10563   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT_SET]);
10564 
10565   clutter_actor_notify_if_geometry_changed (self, &old);
10566 
10567   clutter_actor_queue_relayout (self);
10568 }
10569 
10570 /**
10571  * clutter_actor_set_request_mode:
10572  * @self: a #ClutterActor
10573  * @mode: the request mode
10574  *
10575  * Sets the geometry request mode of @self.
10576  *
10577  * The @mode determines the order for invoking
10578  * clutter_actor_get_preferred_width() and
10579  * clutter_actor_get_preferred_height()
10580  *
10581  * Since: 1.2
10582  */
10583 void
clutter_actor_set_request_mode(ClutterActor * self,ClutterRequestMode mode)10584 clutter_actor_set_request_mode (ClutterActor       *self,
10585                                 ClutterRequestMode  mode)
10586 {
10587   ClutterActorPrivate *priv;
10588 
10589   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10590 
10591   priv = self->priv;
10592 
10593   if (priv->request_mode == mode)
10594     return;
10595 
10596   priv->request_mode = mode;
10597 
10598   priv->needs_width_request = TRUE;
10599   priv->needs_height_request = TRUE;
10600 
10601   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REQUEST_MODE]);
10602 
10603   clutter_actor_queue_relayout (self);
10604 }
10605 
10606 /**
10607  * clutter_actor_get_request_mode:
10608  * @self: a #ClutterActor
10609  *
10610  * Retrieves the geometry request mode of @self
10611  *
10612  * Return value: the request mode for the actor
10613  *
10614  * Since: 1.2
10615  */
10616 ClutterRequestMode
clutter_actor_get_request_mode(ClutterActor * self)10617 clutter_actor_get_request_mode (ClutterActor *self)
10618 {
10619   g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
10620                         CLUTTER_REQUEST_HEIGHT_FOR_WIDTH);
10621 
10622   return self->priv->request_mode;
10623 }
10624 
10625 /* variant of set_width() without checks and without notification
10626  * freeze+thaw, for internal usage only
10627  */
10628 static inline void
clutter_actor_set_width_internal(ClutterActor * self,gfloat width)10629 clutter_actor_set_width_internal (ClutterActor *self,
10630                                   gfloat        width)
10631 {
10632   if (width >= 0)
10633     {
10634       /* the Stage will use the :min-width to control the minimum
10635        * width to be resized to, so we should not be setting it
10636        * along with the :natural-width
10637        */
10638       if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
10639         clutter_actor_set_min_width (self, width);
10640 
10641       clutter_actor_set_natural_width (self, width);
10642     }
10643   else
10644     {
10645       /* we only unset the :natural-width for the Stage */
10646       if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
10647         clutter_actor_set_min_width_set (self, FALSE);
10648 
10649       clutter_actor_set_natural_width_set (self, FALSE);
10650     }
10651 }
10652 
10653 /* variant of set_height() without checks and without notification
10654  * freeze+thaw, for internal usage only
10655  */
10656 static inline void
clutter_actor_set_height_internal(ClutterActor * self,gfloat height)10657 clutter_actor_set_height_internal (ClutterActor *self,
10658                                    gfloat        height)
10659 {
10660   if (height >= 0)
10661     {
10662       /* see the comment above in set_width_internal() */
10663       if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
10664         clutter_actor_set_min_height (self, height);
10665 
10666       clutter_actor_set_natural_height (self, height);
10667     }
10668   else
10669     {
10670       /* see the comment above in set_width_internal() */
10671       if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
10672         clutter_actor_set_min_height_set (self, FALSE);
10673 
10674       clutter_actor_set_natural_height_set (self, FALSE);
10675     }
10676 }
10677 
10678 static void
clutter_actor_set_size_internal(ClutterActor * self,const ClutterSize * size)10679 clutter_actor_set_size_internal (ClutterActor      *self,
10680                                  const ClutterSize *size)
10681 {
10682   if (size != NULL)
10683     {
10684       clutter_actor_set_width_internal (self, size->width);
10685       clutter_actor_set_height_internal (self, size->height);
10686     }
10687   else
10688     {
10689       clutter_actor_set_width_internal (self, -1);
10690       clutter_actor_set_height_internal (self, -1);
10691     }
10692 }
10693 
10694 /**
10695  * clutter_actor_set_size:
10696  * @self: A #ClutterActor
10697  * @width: New width of actor in pixels, or -1
10698  * @height: New height of actor in pixels, or -1
10699  *
10700  * Sets the actor's size request in pixels. This overrides any
10701  * "normal" size request the actor would have. For example
10702  * a text actor might normally request the size of the text;
10703  * this function would force a specific size instead.
10704  *
10705  * If @width and/or @height are -1 the actor will use its
10706  * "normal" size request instead of overriding it, i.e.
10707  * you can "unset" the size with -1.
10708  *
10709  * This function sets or unsets both the minimum and natural size.
10710  */
10711 void
clutter_actor_set_size(ClutterActor * self,gfloat width,gfloat height)10712 clutter_actor_set_size (ClutterActor *self,
10713 			gfloat        width,
10714 			gfloat        height)
10715 {
10716   ClutterSize new_size;
10717 
10718   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10719 
10720   clutter_size_init (&new_size, width, height);
10721 
10722   /* minor optimization: if we don't have a duration then we can
10723    * skip the get_size() below, to avoid the chance of going through
10724    * get_preferred_width() and get_preferred_height() just to jump to
10725    * a new desired size
10726    */
10727   if (clutter_actor_get_easing_duration (self) == 0)
10728     {
10729       g_object_freeze_notify (G_OBJECT (self));
10730 
10731       clutter_actor_set_size_internal (self, &new_size);
10732 
10733       g_object_thaw_notify (G_OBJECT (self));
10734 
10735       return;
10736     }
10737   else
10738     {
10739       ClutterSize cur_size;
10740 
10741       clutter_size_init (&cur_size,
10742                          clutter_actor_get_width (self),
10743                          clutter_actor_get_height (self));
10744 
10745       _clutter_actor_create_transition (self,
10746                                         obj_props[PROP_SIZE],
10747                                         &cur_size,
10748                                         &new_size);
10749     }
10750 }
10751 
10752 /**
10753  * clutter_actor_get_size:
10754  * @self: A #ClutterActor
10755  * @width: (out) (allow-none): return location for the width, or %NULL.
10756  * @height: (out) (allow-none): return location for the height, or %NULL.
10757  *
10758  * This function tries to "do what you mean" and return
10759  * the size an actor will have. If the actor has a valid
10760  * allocation, the allocation will be returned; otherwise,
10761  * the actors natural size request will be returned.
10762  *
10763  * If you care whether you get the request vs. the allocation, you
10764  * should probably call a different function like
10765  * clutter_actor_get_allocation_box() or
10766  * clutter_actor_get_preferred_width().
10767  *
10768  * Since: 0.2
10769  */
10770 void
clutter_actor_get_size(ClutterActor * self,gfloat * width,gfloat * height)10771 clutter_actor_get_size (ClutterActor *self,
10772 			gfloat       *width,
10773 			gfloat       *height)
10774 {
10775   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10776 
10777   if (width)
10778     *width = clutter_actor_get_width (self);
10779 
10780   if (height)
10781     *height = clutter_actor_get_height (self);
10782 }
10783 
10784 /**
10785  * clutter_actor_get_position:
10786  * @self: a #ClutterActor
10787  * @x: (out) (allow-none): return location for the X coordinate, or %NULL
10788  * @y: (out) (allow-none): return location for the Y coordinate, or %NULL
10789  *
10790  * This function tries to "do what you mean" and tell you where the
10791  * actor is, prior to any transformations. Retrieves the fixed
10792  * position of an actor in pixels, if one has been set; otherwise, if
10793  * the allocation is valid, returns the actor's allocated position;
10794  * otherwise, returns 0,0.
10795  *
10796  * The returned position is in pixels.
10797  *
10798  * Since: 0.6
10799  */
10800 void
clutter_actor_get_position(ClutterActor * self,gfloat * x,gfloat * y)10801 clutter_actor_get_position (ClutterActor *self,
10802                             gfloat       *x,
10803                             gfloat       *y)
10804 {
10805   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10806 
10807   if (x)
10808     *x = clutter_actor_get_x (self);
10809 
10810   if (y)
10811     *y = clutter_actor_get_y (self);
10812 }
10813 
10814 /**
10815  * clutter_actor_get_transformed_position:
10816  * @self: A #ClutterActor
10817  * @x: (out) (allow-none): return location for the X coordinate, or %NULL
10818  * @y: (out) (allow-none): return location for the Y coordinate, or %NULL
10819  *
10820  * Gets the absolute position of an actor, in pixels relative to the stage.
10821  *
10822  * Since: 0.8
10823  */
10824 void
clutter_actor_get_transformed_position(ClutterActor * self,gfloat * x,gfloat * y)10825 clutter_actor_get_transformed_position (ClutterActor *self,
10826                                         gfloat       *x,
10827                                         gfloat       *y)
10828 {
10829   ClutterVertex v1;
10830   ClutterVertex v2;
10831 
10832   v1.x = v1.y = v1.z = 0;
10833   clutter_actor_apply_transform_to_point (self, &v1, &v2);
10834 
10835   if (x)
10836     *x = v2.x;
10837 
10838   if (y)
10839     *y = v2.y;
10840 }
10841 
10842 /**
10843  * clutter_actor_get_transformed_size:
10844  * @self: A #ClutterActor
10845  * @width: (out) (allow-none): return location for the width, or %NULL
10846  * @height: (out) (allow-none): return location for the height, or %NULL
10847  *
10848  * Gets the absolute size of an actor in pixels, taking into account the
10849  * scaling factors.
10850  *
10851  * If the actor has a valid allocation, the allocated size will be used.
10852  * If the actor has not a valid allocation then the preferred size will
10853  * be transformed and returned.
10854  *
10855  * If you want the transformed allocation, see
10856  * clutter_actor_get_abs_allocation_vertices() instead.
10857  *
10858  * When the actor (or one of its ancestors) is rotated around the
10859  * X or Y axis, it no longer appears as on the stage as a rectangle, but
10860  * as a generic quadrangle; in that case this function returns the size
10861  * of the smallest rectangle that encapsulates the entire quad. Please
10862  * note that in this case no assumptions can be made about the relative
10863  * position of this envelope to the absolute position of the actor, as
10864  * returned by clutter_actor_get_transformed_position(); if you need this
10865  * information, you need to use clutter_actor_get_abs_allocation_vertices()
10866  * to get the coords of the actual quadrangle.
10867  *
10868  * Since: 0.8
10869  */
10870 void
clutter_actor_get_transformed_size(ClutterActor * self,gfloat * width,gfloat * height)10871 clutter_actor_get_transformed_size (ClutterActor *self,
10872                                     gfloat       *width,
10873                                     gfloat       *height)
10874 {
10875   ClutterActorPrivate *priv;
10876   ClutterVertex v[4];
10877   gfloat x_min, x_max, y_min, y_max;
10878   gint i;
10879 
10880   g_return_if_fail (CLUTTER_IS_ACTOR (self));
10881 
10882   priv = self->priv;
10883 
10884   /* if the actor hasn't been allocated yet, get the preferred
10885    * size and transform that
10886    */
10887   if (priv->needs_allocation)
10888     {
10889       gfloat natural_width, natural_height;
10890       ClutterActorBox box;
10891 
10892       /* Make a fake allocation to transform.
10893        *
10894        * NB: _clutter_actor_transform_and_project_box expects a box in
10895        * the actor's coordinate space... */
10896 
10897       box.x1 = 0;
10898       box.y1 = 0;
10899 
10900       natural_width = natural_height = 0;
10901       clutter_actor_get_preferred_size (self, NULL, NULL,
10902                                         &natural_width,
10903                                         &natural_height);
10904 
10905       box.x2 = natural_width;
10906       box.y2 = natural_height;
10907 
10908       _clutter_actor_transform_and_project_box (self, &box, v);
10909     }
10910   else
10911     clutter_actor_get_abs_allocation_vertices (self, v);
10912 
10913   x_min = x_max = v[0].x;
10914   y_min = y_max = v[0].y;
10915 
10916   for (i = 1; i < G_N_ELEMENTS (v); ++i)
10917     {
10918       if (v[i].x < x_min)
10919 	x_min = v[i].x;
10920 
10921       if (v[i].x > x_max)
10922 	x_max = v[i].x;
10923 
10924       if (v[i].y < y_min)
10925 	y_min = v[i].y;
10926 
10927       if (v[i].y > y_max)
10928 	y_max = v[i].y;
10929     }
10930 
10931   if (width)
10932     *width  = x_max - x_min;
10933 
10934   if (height)
10935     *height = y_max - y_min;
10936 }
10937 
10938 /**
10939  * clutter_actor_get_width:
10940  * @self: A #ClutterActor
10941  *
10942  * Retrieves the width of a #ClutterActor.
10943  *
10944  * If the actor has a valid allocation, this function will return the
10945  * width of the allocated area given to the actor.
10946  *
10947  * If the actor does not have a valid allocation, this function will
10948  * return the actor's natural width, that is the preferred width of
10949  * the actor.
10950  *
10951  * If you care whether you get the preferred width or the width that
10952  * has been assigned to the actor, you should probably call a different
10953  * function like clutter_actor_get_allocation_box() to retrieve the
10954  * allocated size or clutter_actor_get_preferred_width() to retrieve the
10955  * preferred width.
10956  *
10957  * If an actor has a fixed width, for instance a width that has been
10958  * assigned using clutter_actor_set_width(), the width returned will
10959  * be the same value.
10960  *
10961  * Return value: the width of the actor, in pixels
10962  */
10963 gfloat
clutter_actor_get_width(ClutterActor * self)10964 clutter_actor_get_width (ClutterActor *self)
10965 {
10966   ClutterActorPrivate *priv;
10967 
10968   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
10969 
10970   priv = self->priv;
10971 
10972   if (priv->needs_allocation)
10973     {
10974       gfloat natural_width = 0;
10975 
10976       if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
10977         {
10978           clutter_actor_get_preferred_width (self, -1, NULL, &natural_width);
10979         }
10980       else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
10981         {
10982           gfloat natural_height = 0;
10983 
10984           clutter_actor_get_preferred_height (self, -1, NULL, &natural_height);
10985           clutter_actor_get_preferred_width (self, natural_height,
10986                                              NULL,
10987                                              &natural_width);
10988         }
10989       else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL)
10990         {
10991           clutter_content_get_preferred_size (priv->content, &natural_width, NULL);
10992         }
10993 
10994       return natural_width;
10995     }
10996   else
10997     return priv->allocation.x2 - priv->allocation.x1;
10998 }
10999 
11000 /**
11001  * clutter_actor_get_height:
11002  * @self: A #ClutterActor
11003  *
11004  * Retrieves the height of a #ClutterActor.
11005  *
11006  * If the actor has a valid allocation, this function will return the
11007  * height of the allocated area given to the actor.
11008  *
11009  * If the actor does not have a valid allocation, this function will
11010  * return the actor's natural height, that is the preferred height of
11011  * the actor.
11012  *
11013  * If you care whether you get the preferred height or the height that
11014  * has been assigned to the actor, you should probably call a different
11015  * function like clutter_actor_get_allocation_box() to retrieve the
11016  * allocated size or clutter_actor_get_preferred_height() to retrieve the
11017  * preferred height.
11018  *
11019  * If an actor has a fixed height, for instance a height that has been
11020  * assigned using clutter_actor_set_height(), the height returned will
11021  * be the same value.
11022  *
11023  * Return value: the height of the actor, in pixels
11024  */
11025 gfloat
clutter_actor_get_height(ClutterActor * self)11026 clutter_actor_get_height (ClutterActor *self)
11027 {
11028   ClutterActorPrivate *priv;
11029 
11030   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11031 
11032   priv = self->priv;
11033 
11034   if (priv->needs_allocation)
11035     {
11036       gfloat natural_height = 0;
11037 
11038       if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
11039         {
11040           gfloat natural_width = 0;
11041 
11042           clutter_actor_get_preferred_width (self, -1, NULL, &natural_width);
11043           clutter_actor_get_preferred_height (self, natural_width,
11044                                               NULL, &natural_height);
11045         }
11046       else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
11047         {
11048           clutter_actor_get_preferred_height (self, -1, NULL, &natural_height);
11049         }
11050       else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL)
11051         {
11052           clutter_content_get_preferred_size (priv->content, NULL, &natural_height);
11053         }
11054 
11055       return natural_height;
11056     }
11057   else
11058     return priv->allocation.y2 - priv->allocation.y1;
11059 }
11060 
11061 /**
11062  * clutter_actor_set_width:
11063  * @self: A #ClutterActor
11064  * @width: Requested new width for the actor, in pixels, or -1
11065  *
11066  * Forces a width on an actor, causing the actor's preferred width
11067  * and height (if any) to be ignored.
11068  *
11069  * If @width is -1 the actor will use its preferred width request
11070  * instead of overriding it, i.e. you can "unset" the width with -1.
11071  *
11072  * This function sets both the minimum and natural size of the actor.
11073  *
11074  * since: 0.2
11075  */
11076 void
clutter_actor_set_width(ClutterActor * self,gfloat width)11077 clutter_actor_set_width (ClutterActor *self,
11078                          gfloat        width)
11079 {
11080   float cur_size;
11081 
11082   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11083 
11084   /* minor optimization: if we don't have a duration
11085    * then we can skip the get_width() below, to avoid
11086    * the chance of going through get_preferred_width()
11087    * just to jump to a new desired width.
11088    */
11089   if (clutter_actor_get_easing_duration (self) == 0)
11090     {
11091       g_object_freeze_notify (G_OBJECT (self));
11092 
11093       clutter_actor_set_width_internal (self, width);
11094 
11095       g_object_thaw_notify (G_OBJECT (self));
11096 
11097       return;
11098     }
11099   else
11100     cur_size = clutter_actor_get_width (self);
11101 
11102   _clutter_actor_create_transition (self,
11103                                     obj_props[PROP_WIDTH],
11104                                     cur_size,
11105                                     width);
11106 }
11107 
11108 /**
11109  * clutter_actor_set_height:
11110  * @self: A #ClutterActor
11111  * @height: Requested new height for the actor, in pixels, or -1
11112  *
11113  * Forces a height on an actor, causing the actor's preferred width
11114  * and height (if any) to be ignored.
11115  *
11116  * If @height is -1 the actor will use its preferred height instead of
11117  * overriding it, i.e. you can "unset" the height with -1.
11118  *
11119  * This function sets both the minimum and natural size of the actor.
11120  *
11121  * since: 0.2
11122  */
11123 void
clutter_actor_set_height(ClutterActor * self,gfloat height)11124 clutter_actor_set_height (ClutterActor *self,
11125                           gfloat        height)
11126 {
11127   float cur_size;
11128 
11129   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11130 
11131   /* see the comment in clutter_actor_set_width() above */
11132   if (clutter_actor_get_easing_duration (self) == 0)
11133     {
11134       g_object_freeze_notify (G_OBJECT (self));
11135 
11136       clutter_actor_set_height_internal (self, height);
11137 
11138       g_object_thaw_notify (G_OBJECT (self));
11139 
11140       return;
11141     }
11142   else
11143     cur_size = clutter_actor_get_height (self);
11144 
11145   _clutter_actor_create_transition (self,
11146                                     obj_props[PROP_HEIGHT],
11147                                     cur_size,
11148                                     height);
11149 }
11150 
11151 static inline void
clutter_actor_set_x_internal(ClutterActor * self,float x)11152 clutter_actor_set_x_internal (ClutterActor *self,
11153                               float         x)
11154 {
11155   ClutterActorPrivate *priv = self->priv;
11156   ClutterLayoutInfo *linfo;
11157   ClutterActorBox old = { 0, };
11158 
11159   linfo = _clutter_actor_get_layout_info (self);
11160 
11161   if (priv->position_set && linfo->fixed_pos.x == x)
11162     return;
11163 
11164   clutter_actor_store_old_geometry (self, &old);
11165 
11166   linfo->fixed_pos.x = x;
11167   clutter_actor_set_fixed_position_set (self, TRUE);
11168 
11169   clutter_actor_notify_if_geometry_changed (self, &old);
11170 
11171   clutter_actor_queue_relayout (self);
11172 }
11173 
11174 static inline void
clutter_actor_set_y_internal(ClutterActor * self,float y)11175 clutter_actor_set_y_internal (ClutterActor *self,
11176                               float         y)
11177 {
11178   ClutterActorPrivate *priv = self->priv;
11179   ClutterLayoutInfo *linfo;
11180   ClutterActorBox old = { 0, };
11181 
11182   linfo = _clutter_actor_get_layout_info (self);
11183 
11184   if (priv->position_set && linfo->fixed_pos.y == y)
11185     return;
11186 
11187   clutter_actor_store_old_geometry (self, &old);
11188 
11189   linfo->fixed_pos.y = y;
11190   clutter_actor_set_fixed_position_set (self, TRUE);
11191 
11192   clutter_actor_notify_if_geometry_changed (self, &old);
11193 
11194   clutter_actor_queue_relayout (self);
11195 }
11196 
11197 static void
clutter_actor_set_position_internal(ClutterActor * self,const ClutterPoint * position)11198 clutter_actor_set_position_internal (ClutterActor       *self,
11199                                      const ClutterPoint *position)
11200 {
11201   ClutterActorPrivate *priv = self->priv;
11202   ClutterLayoutInfo *linfo;
11203   ClutterActorBox old = { 0, };
11204 
11205   linfo = _clutter_actor_get_layout_info (self);
11206 
11207   if (priv->position_set &&
11208       clutter_point_equals (position, &linfo->fixed_pos))
11209     return;
11210 
11211   clutter_actor_store_old_geometry (self, &old);
11212 
11213   if (position != NULL)
11214     {
11215       linfo->fixed_pos = *position;
11216       clutter_actor_set_fixed_position_set (self, TRUE);
11217     }
11218   else
11219     clutter_actor_set_fixed_position_set (self, FALSE);
11220 
11221   clutter_actor_notify_if_geometry_changed (self, &old);
11222 
11223   clutter_actor_queue_relayout (self);
11224 }
11225 
11226 /**
11227  * clutter_actor_set_x:
11228  * @self: a #ClutterActor
11229  * @x: the actor's position on the X axis
11230  *
11231  * Sets the actor's X coordinate, relative to its parent, in pixels.
11232  *
11233  * Overrides any layout manager and forces a fixed position for
11234  * the actor.
11235  *
11236  * The #ClutterActor:x property is animatable.
11237  *
11238  * Since: 0.6
11239  */
11240 void
clutter_actor_set_x(ClutterActor * self,gfloat x)11241 clutter_actor_set_x (ClutterActor *self,
11242                      gfloat        x)
11243 {
11244   float cur_position = clutter_actor_get_x (self);
11245 
11246   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11247 
11248   _clutter_actor_create_transition (self, obj_props[PROP_X],
11249                                     cur_position,
11250                                     x);
11251 }
11252 
11253 /**
11254  * clutter_actor_set_y:
11255  * @self: a #ClutterActor
11256  * @y: the actor's position on the Y axis
11257  *
11258  * Sets the actor's Y coordinate, relative to its parent, in pixels.#
11259  *
11260  * Overrides any layout manager and forces a fixed position for
11261  * the actor.
11262  *
11263  * The #ClutterActor:y property is animatable.
11264  *
11265  * Since: 0.6
11266  */
11267 void
clutter_actor_set_y(ClutterActor * self,gfloat y)11268 clutter_actor_set_y (ClutterActor *self,
11269                      gfloat        y)
11270 {
11271   float cur_position = clutter_actor_get_y (self);
11272 
11273   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11274 
11275   _clutter_actor_create_transition (self, obj_props[PROP_Y],
11276                                     cur_position,
11277                                     y);
11278 }
11279 
11280 /**
11281  * clutter_actor_get_x:
11282  * @self: A #ClutterActor
11283  *
11284  * Retrieves the X coordinate of a #ClutterActor.
11285  *
11286  * This function tries to "do what you mean", by returning the
11287  * correct value depending on the actor's state.
11288  *
11289  * If the actor has a valid allocation, this function will return
11290  * the X coordinate of the origin of the allocation box.
11291  *
11292  * If the actor has any fixed coordinate set using clutter_actor_set_x(),
11293  * clutter_actor_set_position() or clutter_actor_set_geometry(), this
11294  * function will return that coordinate.
11295  *
11296  * If both the allocation and a fixed position are missing, this function
11297  * will return 0.
11298  *
11299  * Return value: the X coordinate, in pixels, ignoring any
11300  *   transformation (i.e. scaling, rotation)
11301  */
11302 gfloat
clutter_actor_get_x(ClutterActor * self)11303 clutter_actor_get_x (ClutterActor *self)
11304 {
11305   ClutterActorPrivate *priv;
11306 
11307   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11308 
11309   priv = self->priv;
11310 
11311   if (priv->needs_allocation)
11312     {
11313       if (priv->position_set)
11314         {
11315           const ClutterLayoutInfo *info;
11316 
11317           info = _clutter_actor_get_layout_info_or_defaults (self);
11318 
11319           return info->fixed_pos.x;
11320         }
11321       else
11322         return 0;
11323     }
11324   else
11325     return priv->allocation.x1;
11326 }
11327 
11328 /**
11329  * clutter_actor_get_y:
11330  * @self: A #ClutterActor
11331  *
11332  * Retrieves the Y coordinate of a #ClutterActor.
11333  *
11334  * This function tries to "do what you mean", by returning the
11335  * correct value depending on the actor's state.
11336  *
11337  * If the actor has a valid allocation, this function will return
11338  * the Y coordinate of the origin of the allocation box.
11339  *
11340  * If the actor has any fixed coordinate set using clutter_actor_set_y(),
11341  * clutter_actor_set_position() or clutter_actor_set_geometry(), this
11342  * function will return that coordinate.
11343  *
11344  * If both the allocation and a fixed position are missing, this function
11345  * will return 0.
11346  *
11347  * Return value: the Y coordinate, in pixels, ignoring any
11348  *   transformation (i.e. scaling, rotation)
11349  */
11350 gfloat
clutter_actor_get_y(ClutterActor * self)11351 clutter_actor_get_y (ClutterActor *self)
11352 {
11353   ClutterActorPrivate *priv;
11354 
11355   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11356 
11357   priv = self->priv;
11358 
11359   if (priv->needs_allocation)
11360     {
11361       if (priv->position_set)
11362         {
11363           const ClutterLayoutInfo *info;
11364 
11365           info = _clutter_actor_get_layout_info_or_defaults (self);
11366 
11367           return info->fixed_pos.y;
11368         }
11369       else
11370         return 0;
11371     }
11372   else
11373     return priv->allocation.y1;
11374 }
11375 
11376 /**
11377  * clutter_actor_set_scale:
11378  * @self: A #ClutterActor
11379  * @scale_x: double factor to scale actor by horizontally.
11380  * @scale_y: double factor to scale actor by vertically.
11381  *
11382  * Scales an actor with the given factors.
11383  *
11384  * The scale transformation is relative the the #ClutterActor:pivot-point.
11385  *
11386  * The #ClutterActor:scale-x and #ClutterActor:scale-y properties are
11387  * animatable.
11388  *
11389  * Since: 0.2
11390  */
11391 void
clutter_actor_set_scale(ClutterActor * self,gdouble scale_x,gdouble scale_y)11392 clutter_actor_set_scale (ClutterActor *self,
11393                          gdouble       scale_x,
11394                          gdouble       scale_y)
11395 {
11396   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11397 
11398   g_object_freeze_notify (G_OBJECT (self));
11399 
11400   clutter_actor_set_scale_factor (self, CLUTTER_X_AXIS, scale_x);
11401   clutter_actor_set_scale_factor (self, CLUTTER_Y_AXIS, scale_y);
11402 
11403   g_object_thaw_notify (G_OBJECT (self));
11404 }
11405 
11406 /**
11407  * clutter_actor_set_scale_z:
11408  * @self: a #ClutterActor
11409  * @scale_z: the scaling factor along the Z axis
11410  *
11411  * Scales an actor on the Z axis by the given @scale_z factor.
11412  *
11413  * The scale transformation is relative the the #ClutterActor:pivot-point.
11414  *
11415  * The #ClutterActor:scale-z property is animatable.
11416  *
11417  * Since: 1.12
11418  */
11419 void
clutter_actor_set_scale_z(ClutterActor * self,gdouble scale_z)11420 clutter_actor_set_scale_z (ClutterActor *self,
11421                            gdouble       scale_z)
11422 {
11423   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11424 
11425   clutter_actor_set_scale_factor (self, CLUTTER_Z_AXIS, scale_z);
11426 }
11427 
11428 /**
11429  * clutter_actor_set_scale_full:
11430  * @self: A #ClutterActor
11431  * @scale_x: double factor to scale actor by horizontally.
11432  * @scale_y: double factor to scale actor by vertically.
11433  * @center_x: X coordinate of the center of the scaling
11434  * @center_y: Y coordinate of the center of the scaling
11435  *
11436  * Scales an actor with the given factors around the given center
11437  * point. The center point is specified in pixels relative to the
11438  * anchor point (usually the top left corner of the actor).
11439  *
11440  * The #ClutterActor:scale-x and #ClutterActor:scale-y properties
11441  * are animatable.
11442  *
11443  * Since: 1.0
11444  *
11445  * Deprecated: 1.12: Use clutter_actor_set_pivot_point() to control
11446  *   the scale center
11447  */
11448 void
clutter_actor_set_scale_full(ClutterActor * self,gdouble scale_x,gdouble scale_y,gfloat center_x,gfloat center_y)11449 clutter_actor_set_scale_full (ClutterActor *self,
11450                               gdouble       scale_x,
11451                               gdouble       scale_y,
11452                               gfloat        center_x,
11453                               gfloat        center_y)
11454 {
11455   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11456 
11457   g_object_freeze_notify (G_OBJECT (self));
11458 
11459   clutter_actor_set_scale_factor (self, CLUTTER_X_AXIS, scale_x);
11460   clutter_actor_set_scale_factor (self, CLUTTER_Y_AXIS, scale_y);
11461   clutter_actor_set_scale_center (self, CLUTTER_X_AXIS, center_x);
11462   clutter_actor_set_scale_center (self, CLUTTER_Y_AXIS, center_y);
11463 
11464   g_object_thaw_notify (G_OBJECT (self));
11465 }
11466 
11467 /**
11468  * clutter_actor_set_scale_with_gravity:
11469  * @self: A #ClutterActor
11470  * @scale_x: double factor to scale actor by horizontally.
11471  * @scale_y: double factor to scale actor by vertically.
11472  * @gravity: the location of the scale center expressed as a compass
11473  *   direction.
11474  *
11475  * Scales an actor with the given factors around the given
11476  * center point. The center point is specified as one of the compass
11477  * directions in #ClutterGravity. For example, setting it to north
11478  * will cause the top of the actor to remain unchanged and the rest of
11479  * the actor to expand left, right and downwards.
11480  *
11481  * The #ClutterActor:scale-x and #ClutterActor:scale-y properties are
11482  * animatable.
11483  *
11484  * Since: 1.0
11485  *
11486  * Deprecated: 1.12: Use clutter_actor_set_pivot_point() to set the
11487  *   scale center using normalized coordinates instead.
11488  */
11489 void
clutter_actor_set_scale_with_gravity(ClutterActor * self,gdouble scale_x,gdouble scale_y,ClutterGravity gravity)11490 clutter_actor_set_scale_with_gravity (ClutterActor   *self,
11491                                       gdouble         scale_x,
11492                                       gdouble         scale_y,
11493                                       ClutterGravity  gravity)
11494 {
11495   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11496 
11497   g_object_freeze_notify (G_OBJECT (self));
11498 
11499   clutter_actor_set_scale_factor (self, CLUTTER_X_AXIS, scale_x);
11500   clutter_actor_set_scale_factor (self, CLUTTER_Y_AXIS, scale_y);
11501   clutter_actor_set_scale_gravity (self, gravity);
11502 
11503   g_object_thaw_notify (G_OBJECT (self));
11504 }
11505 
11506 /**
11507  * clutter_actor_get_scale:
11508  * @self: A #ClutterActor
11509  * @scale_x: (out) (allow-none): Location to store horizonal
11510  *   scale factor, or %NULL.
11511  * @scale_y: (out) (allow-none): Location to store vertical
11512  *   scale factor, or %NULL.
11513  *
11514  * Retrieves an actors scale factors.
11515  *
11516  * Since: 0.2
11517  */
11518 void
clutter_actor_get_scale(ClutterActor * self,gdouble * scale_x,gdouble * scale_y)11519 clutter_actor_get_scale (ClutterActor *self,
11520 			 gdouble      *scale_x,
11521 			 gdouble      *scale_y)
11522 {
11523   const ClutterTransformInfo *info;
11524 
11525   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11526 
11527   info = _clutter_actor_get_transform_info_or_defaults (self);
11528 
11529   if (scale_x)
11530     *scale_x = info->scale_x;
11531 
11532   if (scale_y)
11533     *scale_y = info->scale_y;
11534 }
11535 
11536 /**
11537  * clutter_actor_get_scale_z:
11538  * @self: A #ClutterActor
11539  *
11540  * Retrieves the scaling factor along the Z axis, as set using
11541  * clutter_actor_set_scale_z().
11542  *
11543  * Return value: the scaling factor along the Z axis
11544  *
11545  * Since: 1.12
11546  */
11547 gdouble
clutter_actor_get_scale_z(ClutterActor * self)11548 clutter_actor_get_scale_z (ClutterActor *self)
11549 {
11550   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 1.0);
11551 
11552   return _clutter_actor_get_transform_info_or_defaults (self)->scale_z;
11553 }
11554 
11555 /**
11556  * clutter_actor_get_scale_center:
11557  * @self: A #ClutterActor
11558  * @center_x: (out) (allow-none): Location to store the X position
11559  *   of the scale center, or %NULL.
11560  * @center_y: (out) (allow-none): Location to store the Y position
11561  *   of the scale center, or %NULL.
11562  *
11563  * Retrieves the scale center coordinate in pixels relative to the top
11564  * left corner of the actor. If the scale center was specified using a
11565  * #ClutterGravity this will calculate the pixel offset using the
11566  * current size of the actor.
11567  *
11568  * Since: 1.0
11569  *
11570  * Deprecated: 1.12: Use clutter_actor_get_pivot_point() instead.
11571  */
11572 void
clutter_actor_get_scale_center(ClutterActor * self,gfloat * center_x,gfloat * center_y)11573 clutter_actor_get_scale_center (ClutterActor *self,
11574                                 gfloat       *center_x,
11575                                 gfloat       *center_y)
11576 {
11577   const ClutterTransformInfo *info;
11578 
11579   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11580 
11581   info = _clutter_actor_get_transform_info_or_defaults (self);
11582 
11583   clutter_anchor_coord_get_units (self, &info->scale_center,
11584                                   center_x,
11585                                   center_y,
11586                                   NULL);
11587 }
11588 
11589 /**
11590  * clutter_actor_get_scale_gravity:
11591  * @self: A #ClutterActor
11592  *
11593  * Retrieves the scale center as a compass direction. If the scale
11594  * center was specified in pixels or units this will return
11595  * %CLUTTER_GRAVITY_NONE.
11596  *
11597  * Return value: the scale gravity
11598  *
11599  * Since: 1.0
11600  *
11601  * Deprecated: 1.12: Use clutter_actor_get_pivot_point() instead.
11602  */
11603 ClutterGravity
clutter_actor_get_scale_gravity(ClutterActor * self)11604 clutter_actor_get_scale_gravity (ClutterActor *self)
11605 {
11606   const ClutterTransformInfo *info;
11607 
11608   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE);
11609 
11610   info = _clutter_actor_get_transform_info_or_defaults (self);
11611 
11612   return clutter_anchor_coord_get_gravity (&info->scale_center);
11613 }
11614 
11615 static inline void
clutter_actor_set_opacity_internal(ClutterActor * self,guint8 opacity)11616 clutter_actor_set_opacity_internal (ClutterActor *self,
11617                                     guint8        opacity)
11618 {
11619   ClutterActorPrivate *priv = self->priv;
11620 
11621   if (priv->opacity != opacity)
11622     {
11623       priv->opacity = opacity;
11624 
11625       /* Queue a redraw from the flatten effect so that it can use
11626          its cached image if available instead of having to redraw the
11627          actual actor. If it doesn't end up using the FBO then the
11628          effect is still able to continue the paint anyway. If there
11629          is no flatten effect yet then this is equivalent to queueing
11630          a full redraw */
11631       _clutter_actor_queue_redraw_full (self,
11632                                         0, /* flags */
11633                                         NULL, /* clip */
11634                                         priv->flatten_effect);
11635 
11636       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_OPACITY]);
11637     }
11638 }
11639 
11640 /**
11641  * clutter_actor_set_opacity:
11642  * @self: A #ClutterActor
11643  * @opacity: New opacity value for the actor.
11644  *
11645  * Sets the actor's opacity, with zero being completely transparent and
11646  * 255 (0xff) being fully opaque.
11647  *
11648  * The #ClutterActor:opacity property is animatable.
11649  */
11650 void
clutter_actor_set_opacity(ClutterActor * self,guint8 opacity)11651 clutter_actor_set_opacity (ClutterActor *self,
11652 			   guint8        opacity)
11653 {
11654   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11655 
11656   _clutter_actor_create_transition (self, obj_props[PROP_OPACITY],
11657                                     self->priv->opacity,
11658                                     opacity);
11659 }
11660 
11661 /*
11662  * clutter_actor_get_paint_opacity_internal:
11663  * @self: a #ClutterActor
11664  *
11665  * Retrieves the absolute opacity of the actor, as it appears on the stage
11666  *
11667  * This function does not do type checks
11668  *
11669  * Return value: the absolute opacity of the actor
11670  */
11671 static guint8
clutter_actor_get_paint_opacity_internal(ClutterActor * self)11672 clutter_actor_get_paint_opacity_internal (ClutterActor *self)
11673 {
11674   ClutterActorPrivate *priv = self->priv;
11675   ClutterActor *parent;
11676 
11677   /* override the top-level opacity to always be 255; even in
11678    * case of ClutterStage:use-alpha being TRUE we want the rest
11679    * of the scene to be painted
11680    */
11681   if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
11682     return 255;
11683 
11684   if (priv->opacity_override >= 0)
11685     return priv->opacity_override;
11686 
11687   parent = priv->parent;
11688 
11689   /* Factor in the actual actors opacity with parents */
11690   if (parent != NULL)
11691     {
11692       guint8 opacity = clutter_actor_get_paint_opacity_internal (parent);
11693 
11694       if (opacity != 0xff)
11695         return (opacity * priv->opacity) / 0xff;
11696     }
11697 
11698   return priv->opacity;
11699 
11700 }
11701 
11702 /**
11703  * clutter_actor_get_paint_opacity:
11704  * @self: A #ClutterActor
11705  *
11706  * Retrieves the absolute opacity of the actor, as it appears on the stage.
11707  *
11708  * This function traverses the hierarchy chain and composites the opacity of
11709  * the actor with that of its parents.
11710  *
11711  * This function is intended for subclasses to use in the paint virtual
11712  * function, to paint themselves with the correct opacity.
11713  *
11714  * Return value: The actor opacity value.
11715  *
11716  * Since: 0.8
11717  */
11718 guint8
clutter_actor_get_paint_opacity(ClutterActor * self)11719 clutter_actor_get_paint_opacity (ClutterActor *self)
11720 {
11721   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11722 
11723   return clutter_actor_get_paint_opacity_internal (self);
11724 }
11725 
11726 /**
11727  * clutter_actor_get_opacity:
11728  * @self: a #ClutterActor
11729  *
11730  * Retrieves the opacity value of an actor, as set by
11731  * clutter_actor_set_opacity().
11732  *
11733  * For retrieving the absolute opacity of the actor inside a paint
11734  * virtual function, see clutter_actor_get_paint_opacity().
11735  *
11736  * Return value: the opacity of the actor
11737  */
11738 guint8
clutter_actor_get_opacity(ClutterActor * self)11739 clutter_actor_get_opacity (ClutterActor *self)
11740 {
11741   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11742 
11743   return self->priv->opacity;
11744 }
11745 
11746 /**
11747  * clutter_actor_set_offscreen_redirect:
11748  * @self: A #ClutterActor
11749  * @redirect: New offscreen redirect flags for the actor.
11750  *
11751  * Defines the circumstances where the actor should be redirected into
11752  * an offscreen image. The offscreen image is used to flatten the
11753  * actor into a single image while painting for two main reasons.
11754  * Firstly, when the actor is painted a second time without any of its
11755  * contents changing it can simply repaint the cached image without
11756  * descending further down the actor hierarchy. Secondly, it will make
11757  * the opacity look correct even if there are overlapping primitives
11758  * in the actor.
11759  *
11760  * Caching the actor could in some cases be a performance win and in
11761  * some cases be a performance lose so it is important to determine
11762  * which value is right for an actor before modifying this value. For
11763  * example, there is never any reason to flatten an actor that is just
11764  * a single texture (such as a #ClutterTexture) because it is
11765  * effectively already cached in an image so the offscreen would be
11766  * redundant. Also if the actor contains primitives that are far apart
11767  * with a large transparent area in the middle (such as a large
11768  * CluterGroup with a small actor in the top left and a small actor in
11769  * the bottom right) then the cached image will contain the entire
11770  * image of the large area and the paint will waste time blending all
11771  * of the transparent pixels in the middle.
11772  *
11773  * The default method of implementing opacity on a container simply
11774  * forwards on the opacity to all of the children. If the children are
11775  * overlapping then it will appear as if they are two separate glassy
11776  * objects and there will be a break in the color where they
11777  * overlap. By redirecting to an offscreen buffer it will be as if the
11778  * two opaque objects are combined into one and then made transparent
11779  * which is usually what is expected.
11780  *
11781  * The image below demonstrates the difference between redirecting and
11782  * not. The image shows two Clutter groups, each containing a red and
11783  * a green rectangle which overlap. The opacity on the group is set to
11784  * 128 (which is 50%). When the offscreen redirect is not used, the
11785  * red rectangle can be seen through the blue rectangle as if the two
11786  * rectangles were separately transparent. When the redirect is used
11787  * the group as a whole is transparent instead so the red rectangle is
11788  * not visible where they overlap.
11789  *
11790  * <figure id="offscreen-redirect">
11791  *   <title>Sample of using an offscreen redirect for transparency</title>
11792  *   <graphic fileref="offscreen-redirect.png" format="PNG"/>
11793  * </figure>
11794  *
11795  * The default value for this property is 0, so we effectively will
11796  * never redirect an actor offscreen by default. This means that there
11797  * are times that transparent actors may look glassy as described
11798  * above. The reason this is the default is because there is a
11799  * performance trade off between quality and performance here. In many
11800  * cases the default form of glassy opacity looks good enough, but if
11801  * it's not you will need to set the
11802  * %CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY flag to enable
11803  * redirection for opacity.
11804  *
11805  * Custom actors that don't contain any overlapping primitives are
11806  * recommended to override the has_overlaps() virtual to return %FALSE
11807  * for maximum efficiency.
11808  *
11809  * Since: 1.8
11810  */
11811 void
clutter_actor_set_offscreen_redirect(ClutterActor * self,ClutterOffscreenRedirect redirect)11812 clutter_actor_set_offscreen_redirect (ClutterActor *self,
11813                                       ClutterOffscreenRedirect redirect)
11814 {
11815   ClutterActorPrivate *priv;
11816 
11817   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11818 
11819   priv = self->priv;
11820 
11821   if (priv->offscreen_redirect != redirect)
11822     {
11823       priv->offscreen_redirect = redirect;
11824 
11825       /* Queue a redraw from the effect so that it can use its cached
11826          image if available instead of having to redraw the actual
11827          actor. If it doesn't end up using the FBO then the effect is
11828          still able to continue the paint anyway. If there is no
11829          effect then this is equivalent to queuing a full redraw */
11830       _clutter_actor_queue_redraw_full (self,
11831                                         0, /* flags */
11832                                         NULL, /* clip */
11833                                         priv->flatten_effect);
11834 
11835       g_object_notify_by_pspec (G_OBJECT (self),
11836                                 obj_props[PROP_OFFSCREEN_REDIRECT]);
11837     }
11838 }
11839 
11840 /**
11841  * clutter_actor_get_offscreen_redirect:
11842  * @self: a #ClutterActor
11843  *
11844  * Retrieves whether to redirect the actor to an offscreen buffer, as
11845  * set by clutter_actor_set_offscreen_redirect().
11846  *
11847  * Return value: the value of the offscreen-redirect property of the actor
11848  *
11849  * Since: 1.8
11850  */
11851 ClutterOffscreenRedirect
clutter_actor_get_offscreen_redirect(ClutterActor * self)11852 clutter_actor_get_offscreen_redirect (ClutterActor *self)
11853 {
11854   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11855 
11856   return self->priv->offscreen_redirect;
11857 }
11858 
11859 /**
11860  * clutter_actor_set_name:
11861  * @self: A #ClutterActor
11862  * @name: Textual tag to apply to actor
11863  *
11864  * Sets the given name to @self. The name can be used to identify
11865  * a #ClutterActor.
11866  */
11867 void
clutter_actor_set_name(ClutterActor * self,const gchar * name)11868 clutter_actor_set_name (ClutterActor *self,
11869 			const gchar  *name)
11870 {
11871   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11872 
11873   g_free (self->priv->name);
11874   self->priv->name = g_strdup (name);
11875 
11876   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NAME]);
11877 }
11878 
11879 /**
11880  * clutter_actor_get_name:
11881  * @self: A #ClutterActor
11882  *
11883  * Retrieves the name of @self.
11884  *
11885  * Return value: the name of the actor, or %NULL. The returned string is
11886  *   owned by the actor and should not be modified or freed.
11887  */
11888 const gchar *
clutter_actor_get_name(ClutterActor * self)11889 clutter_actor_get_name (ClutterActor *self)
11890 {
11891   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
11892 
11893   return self->priv->name;
11894 }
11895 
11896 /**
11897  * clutter_actor_get_gid:
11898  * @self: A #ClutterActor
11899  *
11900  * Retrieves the unique id for @self.
11901  *
11902  * Return value: Globally unique value for this object instance.
11903  *
11904  * Since: 0.6
11905  *
11906  * Deprecated: 1.8: The id is not used any longer, and this function
11907  *   always returns 0.
11908  */
11909 guint32
clutter_actor_get_gid(ClutterActor * self)11910 clutter_actor_get_gid (ClutterActor *self)
11911 {
11912   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
11913 
11914   return 0;
11915 }
11916 
11917 static inline void
clutter_actor_set_depth_internal(ClutterActor * self,float depth)11918 clutter_actor_set_depth_internal (ClutterActor *self,
11919                                   float         depth)
11920 {
11921   ClutterTransformInfo *info;
11922 
11923   info = _clutter_actor_get_transform_info (self);
11924 
11925   if (info->z_position != depth)
11926     {
11927       /* Sets Z value - XXX 2.0: should we invert? */
11928       info->z_position = depth;
11929 
11930       self->priv->transform_valid = FALSE;
11931 
11932       /* FIXME - remove this crap; sadly, there are still containers
11933        * in Clutter that depend on this utter brain damage
11934        */
11935       clutter_container_sort_depth_order (CLUTTER_CONTAINER (self));
11936 
11937       clutter_actor_queue_redraw (self);
11938 
11939       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DEPTH]);
11940     }
11941 }
11942 
11943 static inline void
clutter_actor_set_z_position_internal(ClutterActor * self,float z_position)11944 clutter_actor_set_z_position_internal (ClutterActor *self,
11945                                        float         z_position)
11946 {
11947   ClutterTransformInfo *info;
11948 
11949   info = _clutter_actor_get_transform_info (self);
11950 
11951   if (memcmp (&info->z_position, &z_position, sizeof (float)) != 0)
11952     {
11953       info->z_position = z_position;
11954 
11955       self->priv->transform_valid = FALSE;
11956 
11957       clutter_actor_queue_redraw (self);
11958 
11959       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_Z_POSITION]);
11960     }
11961 }
11962 
11963 /**
11964  * clutter_actor_set_z_position:
11965  * @self: a #ClutterActor
11966  * @z_position: the position on the Z axis
11967  *
11968  * Sets the actor's position on the Z axis.
11969  *
11970  * See #ClutterActor:z-position.
11971  *
11972  * Since: 1.12
11973  */
11974 void
clutter_actor_set_z_position(ClutterActor * self,gfloat z_position)11975 clutter_actor_set_z_position (ClutterActor *self,
11976                               gfloat        z_position)
11977 {
11978   const ClutterTransformInfo *info;
11979 
11980   g_return_if_fail (CLUTTER_IS_ACTOR (self));
11981 
11982   info = _clutter_actor_get_transform_info_or_defaults (self);
11983 
11984   _clutter_actor_create_transition (self, obj_props[PROP_Z_POSITION],
11985                                     info->z_position,
11986                                     z_position);
11987 }
11988 
11989 /**
11990  * clutter_actor_get_z_position:
11991  * @self: a #ClutterActor
11992  *
11993  * Retrieves the actor's position on the Z axis.
11994  *
11995  * Return value: the position on the Z axis.
11996  *
11997  * Since: 1.12
11998  */
11999 gfloat
clutter_actor_get_z_position(ClutterActor * self)12000 clutter_actor_get_z_position (ClutterActor *self)
12001 {
12002   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
12003 
12004   return _clutter_actor_get_transform_info_or_defaults (self)->z_position;
12005 }
12006 
12007 /**
12008  * clutter_actor_set_pivot_point:
12009  * @self: a #ClutterActor
12010  * @pivot_x: the normalized X coordinate of the pivot point
12011  * @pivot_y: the normalized Y coordinate of the pivot point
12012  *
12013  * Sets the position of the #ClutterActor:pivot-point around which the
12014  * scaling and rotation transformations occur.
12015  *
12016  * The pivot point's coordinates are in normalized space, with the (0, 0)
12017  * point being the top left corner of the actor, and the (1, 1) point being
12018  * the bottom right corner.
12019  *
12020  * Since: 1.12
12021  */
12022 void
clutter_actor_set_pivot_point(ClutterActor * self,gfloat pivot_x,gfloat pivot_y)12023 clutter_actor_set_pivot_point (ClutterActor *self,
12024                                gfloat        pivot_x,
12025                                gfloat        pivot_y)
12026 {
12027   ClutterPoint pivot = CLUTTER_POINT_INIT (pivot_x, pivot_y);
12028   const ClutterTransformInfo *info;
12029 
12030   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12031 
12032   info = _clutter_actor_get_transform_info_or_defaults (self);
12033   _clutter_actor_create_transition (self, obj_props[PROP_PIVOT_POINT],
12034                                     &info->pivot,
12035                                     &pivot);
12036 }
12037 
12038 /**
12039  * clutter_actor_get_pivot_point:
12040  * @self: a #ClutterActor
12041  * @pivot_x: (out) (allow-none): return location for the normalized X
12042  *   coordinate of the pivot point, or %NULL
12043  * @pivot_y: (out) (allow-none): return location for the normalized Y
12044  *   coordinate of the pivot point, or %NULL
12045  *
12046  * Retrieves the coordinates of the #ClutterActor:pivot-point.
12047  *
12048  * Since: 1.12
12049  */
12050 void
clutter_actor_get_pivot_point(ClutterActor * self,gfloat * pivot_x,gfloat * pivot_y)12051 clutter_actor_get_pivot_point (ClutterActor *self,
12052                                gfloat       *pivot_x,
12053                                gfloat       *pivot_y)
12054 {
12055   const ClutterTransformInfo *info;
12056 
12057   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12058 
12059   info = _clutter_actor_get_transform_info_or_defaults (self);
12060 
12061   if (pivot_x != NULL)
12062     *pivot_x = info->pivot.x;
12063 
12064   if (pivot_y != NULL)
12065     *pivot_y = info->pivot.y;
12066 }
12067 
12068 /**
12069  * clutter_actor_set_pivot_point_z:
12070  * @self: a #ClutterActor
12071  * @pivot_z: the Z coordinate of the actor's pivot point
12072  *
12073  * Sets the component on the Z axis of the #ClutterActor:pivot-point around
12074  * which the scaling and rotation transformations occur.
12075  *
12076  * The @pivot_z value is expressed as a distance along the Z axis.
12077  *
12078  * Since: 1.12
12079  */
12080 void
clutter_actor_set_pivot_point_z(ClutterActor * self,gfloat pivot_z)12081 clutter_actor_set_pivot_point_z (ClutterActor *self,
12082                                  gfloat        pivot_z)
12083 {
12084   const ClutterTransformInfo *info;
12085 
12086   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12087 
12088   info = _clutter_actor_get_transform_info_or_defaults (self);
12089   _clutter_actor_create_transition (self, obj_props[PROP_PIVOT_POINT_Z],
12090                                     info->pivot_z,
12091                                     pivot_z);
12092 }
12093 
12094 /**
12095  * clutter_actor_get_pivot_point_z:
12096  * @self: a #ClutterActor
12097  *
12098  * Retrieves the Z component of the #ClutterActor:pivot-point.
12099  *
12100  * Since: 1.12
12101  */
12102 gfloat
clutter_actor_get_pivot_point_z(ClutterActor * self)12103 clutter_actor_get_pivot_point_z (ClutterActor *self)
12104 {
12105   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
12106 
12107   return _clutter_actor_get_transform_info_or_defaults (self)->pivot_z;
12108 }
12109 
12110 /**
12111  * clutter_actor_set_depth:
12112  * @self: a #ClutterActor
12113  * @depth: Z co-ord
12114  *
12115  * Sets the Z coordinate of @self to @depth.
12116  *
12117  * The unit used by @depth is dependant on the perspective setup. See
12118  * also clutter_stage_set_perspective().
12119  *
12120  * Deprecated: 1.12: Use clutter_actor_set_z_position() instead.
12121  */
12122 void
clutter_actor_set_depth(ClutterActor * self,gfloat depth)12123 clutter_actor_set_depth (ClutterActor *self,
12124                          gfloat        depth)
12125 {
12126   const ClutterTransformInfo *info;
12127 
12128   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12129 
12130   info = _clutter_actor_get_transform_info_or_defaults (self);
12131   _clutter_actor_create_transition (self, obj_props[PROP_DEPTH],
12132                                     info->z_position,
12133                                     depth);
12134 }
12135 
12136 /**
12137  * clutter_actor_get_depth:
12138  * @self: a #ClutterActor
12139  *
12140  * Retrieves the depth of @self.
12141  *
12142  * Return value: the depth of the actor
12143  *
12144  * Deprecated: 1.12: Use clutter_actor_get_z_position() instead.
12145  */
12146 gfloat
clutter_actor_get_depth(ClutterActor * self)12147 clutter_actor_get_depth (ClutterActor *self)
12148 {
12149   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.0);
12150 
12151   return _clutter_actor_get_transform_info_or_defaults (self)->z_position;
12152 }
12153 
12154 /**
12155  * clutter_actor_set_rotation:
12156  * @self: a #ClutterActor
12157  * @axis: the axis of rotation
12158  * @angle: the angle of rotation
12159  * @x: X coordinate of the rotation center
12160  * @y: Y coordinate of the rotation center
12161  * @z: Z coordinate of the rotation center
12162  *
12163  * Sets the rotation angle of @self around the given axis.
12164  *
12165  * The rotation center coordinates used depend on the value of @axis:
12166  *
12167  *  - %CLUTTER_X_AXIS requires @y and @z
12168  *  - %CLUTTER_Y_AXIS requires @x and @z
12169  *  - %CLUTTER_Z_AXIS requires @x and @y
12170  *
12171  * The rotation coordinates are relative to the anchor point of the
12172  * actor, set using clutter_actor_set_anchor_point(). If no anchor
12173  * point is set, the upper left corner is assumed as the origin.
12174  *
12175  * Since: 0.8
12176  *
12177  * Deprecated: 1.12: Use clutter_actor_set_rotation_angle() and
12178  *   clutter_actor_set_pivot_point() instead.
12179  */
12180 void
clutter_actor_set_rotation(ClutterActor * self,ClutterRotateAxis axis,gdouble angle,gfloat x,gfloat y,gfloat z)12181 clutter_actor_set_rotation (ClutterActor      *self,
12182                             ClutterRotateAxis  axis,
12183                             gdouble            angle,
12184                             gfloat             x,
12185                             gfloat             y,
12186                             gfloat             z)
12187 {
12188   ClutterVertex v;
12189 
12190   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12191 
12192   v.x = x;
12193   v.y = y;
12194   v.z = z;
12195 
12196   g_object_freeze_notify (G_OBJECT (self));
12197 
12198   clutter_actor_set_rotation_angle (self, axis, angle);
12199   clutter_actor_set_rotation_center_internal (self, axis, &v);
12200 
12201   g_object_thaw_notify (G_OBJECT (self));
12202 }
12203 
12204 /**
12205  * clutter_actor_set_z_rotation_from_gravity:
12206  * @self: a #ClutterActor
12207  * @angle: the angle of rotation
12208  * @gravity: the center point of the rotation
12209  *
12210  * Sets the rotation angle of @self around the Z axis using the center
12211  * point specified as a compass point. For example to rotate such that
12212  * the center of the actor remains static you can use
12213  * %CLUTTER_GRAVITY_CENTER. If the actor changes size the center point
12214  * will move accordingly.
12215  *
12216  * Since: 1.0
12217  *
12218  * Deprecated: 1.12: Use clutter_actor_set_rotation_angle() and
12219  *   clutter_actor_set_pivot_point() instead.
12220  */
12221 void
clutter_actor_set_z_rotation_from_gravity(ClutterActor * self,gdouble angle,ClutterGravity gravity)12222 clutter_actor_set_z_rotation_from_gravity (ClutterActor   *self,
12223                                            gdouble         angle,
12224                                            ClutterGravity  gravity)
12225 {
12226   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12227 
12228   if (gravity == CLUTTER_GRAVITY_NONE)
12229     clutter_actor_set_rotation (self, CLUTTER_Z_AXIS, angle, 0, 0, 0);
12230   else
12231     {
12232       GObject *obj = G_OBJECT (self);
12233       ClutterTransformInfo *info;
12234       GParamSpec *pspec;
12235 
12236       pspec = obj_props[PROP_ROTATION_ANGLE_Z];
12237       info = _clutter_actor_get_transform_info (self);
12238 
12239       g_object_freeze_notify (obj);
12240 
12241       clutter_actor_set_rotation_angle_internal (self, angle, pspec);
12242 
12243       clutter_anchor_coord_set_gravity (&info->rz_center, gravity);
12244       g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Z_GRAVITY]);
12245       g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Z]);
12246 
12247       g_object_thaw_notify (obj);
12248     }
12249 }
12250 
12251 /**
12252  * clutter_actor_get_rotation:
12253  * @self: a #ClutterActor
12254  * @axis: the axis of rotation
12255  * @x: (out): return value for the X coordinate of the center of rotation
12256  * @y: (out): return value for the Y coordinate of the center of rotation
12257  * @z: (out): return value for the Z coordinate of the center of rotation
12258  *
12259  * Retrieves the angle and center of rotation on the given axis,
12260  * set using clutter_actor_set_rotation().
12261  *
12262  * Return value: the angle of rotation
12263  *
12264  * Since: 0.8
12265  *
12266  * Deprecated: 1.12: Use clutter_actor_get_rotation_angle() and
12267  *   clutter_actor_get_pivot_point() instead.
12268  */
12269 gdouble
clutter_actor_get_rotation(ClutterActor * self,ClutterRotateAxis axis,gfloat * x,gfloat * y,gfloat * z)12270 clutter_actor_get_rotation (ClutterActor      *self,
12271                             ClutterRotateAxis  axis,
12272                             gfloat            *x,
12273                             gfloat            *y,
12274                             gfloat            *z)
12275 {
12276   const ClutterTransformInfo *info;
12277   const AnchorCoord *anchor_coord;
12278   gdouble retval = 0;
12279 
12280   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
12281 
12282   info = _clutter_actor_get_transform_info_or_defaults (self);
12283 
12284   switch (axis)
12285     {
12286     case CLUTTER_X_AXIS:
12287       anchor_coord = &info->rx_center;
12288       retval = info->rx_angle;
12289       break;
12290 
12291     case CLUTTER_Y_AXIS:
12292       anchor_coord = &info->ry_center;
12293       retval = info->ry_angle;
12294       break;
12295 
12296     case CLUTTER_Z_AXIS:
12297       anchor_coord = &info->rz_center;
12298       retval = info->rz_angle;
12299       break;
12300 
12301     default:
12302       anchor_coord = NULL;
12303       retval = 0.0;
12304       break;
12305     }
12306 
12307   clutter_anchor_coord_get_units (self, anchor_coord, x, y, z);
12308 
12309   return retval;
12310 }
12311 
12312 /**
12313  * clutter_actor_get_z_rotation_gravity:
12314  * @self: A #ClutterActor
12315  *
12316  * Retrieves the center for the rotation around the Z axis as a
12317  * compass direction. If the center was specified in pixels or units
12318  * this will return %CLUTTER_GRAVITY_NONE.
12319  *
12320  * Return value: the Z rotation center
12321  *
12322  * Since: 1.0
12323  *
12324  * Deprecated: 1.12: Use the #ClutterActor:pivot-point instead of
12325  *   a #ClutterGravity
12326  */
12327 ClutterGravity
clutter_actor_get_z_rotation_gravity(ClutterActor * self)12328 clutter_actor_get_z_rotation_gravity (ClutterActor *self)
12329 {
12330   const ClutterTransformInfo *info;
12331 
12332   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE);
12333 
12334   info = _clutter_actor_get_transform_info_or_defaults (self);
12335 
12336   return clutter_anchor_coord_get_gravity (&info->rz_center);
12337 }
12338 
12339 /**
12340  * clutter_actor_set_clip:
12341  * @self: A #ClutterActor
12342  * @xoff: X offset of the clip rectangle
12343  * @yoff: Y offset of the clip rectangle
12344  * @width: Width of the clip rectangle
12345  * @height: Height of the clip rectangle
12346  *
12347  * Sets clip area for @self. The clip area is always computed from the
12348  * upper left corner of the actor, even if the anchor point is set
12349  * otherwise.
12350  *
12351  * Since: 0.6
12352  */
12353 void
clutter_actor_set_clip(ClutterActor * self,gfloat xoff,gfloat yoff,gfloat width,gfloat height)12354 clutter_actor_set_clip (ClutterActor *self,
12355                         gfloat        xoff,
12356                         gfloat        yoff,
12357                         gfloat        width,
12358                         gfloat        height)
12359 {
12360   ClutterActorPrivate *priv;
12361   GObject *obj;
12362 
12363   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12364 
12365   priv = self->priv;
12366 
12367   if (priv->has_clip &&
12368       priv->clip.origin.x == xoff &&
12369       priv->clip.origin.y == yoff &&
12370       priv->clip.size.width == width &&
12371       priv->clip.size.height == height)
12372     return;
12373 
12374   obj = G_OBJECT (self);
12375 
12376   priv->clip.origin.x = xoff;
12377   priv->clip.origin.y = yoff;
12378   priv->clip.size.width = width;
12379   priv->clip.size.height = height;
12380 
12381   priv->has_clip = TRUE;
12382 
12383   clutter_actor_queue_redraw (self);
12384 
12385   g_object_notify_by_pspec (obj, obj_props[PROP_CLIP]);
12386   g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]);
12387   g_object_notify_by_pspec (obj, obj_props[PROP_HAS_CLIP]);
12388 }
12389 
12390 /**
12391  * clutter_actor_remove_clip:
12392  * @self: A #ClutterActor
12393  *
12394  * Removes clip area from @self.
12395  */
12396 void
clutter_actor_remove_clip(ClutterActor * self)12397 clutter_actor_remove_clip (ClutterActor *self)
12398 {
12399   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12400 
12401   if (!self->priv->has_clip)
12402     return;
12403 
12404   self->priv->has_clip = FALSE;
12405 
12406   clutter_actor_queue_redraw (self);
12407 
12408   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]);
12409 }
12410 
12411 /**
12412  * clutter_actor_has_clip:
12413  * @self: a #ClutterActor
12414  *
12415  * Determines whether the actor has a clip area set or not.
12416  *
12417  * Return value: %TRUE if the actor has a clip area set.
12418  *
12419  * Since: 0.2
12420  */
12421 gboolean
clutter_actor_has_clip(ClutterActor * self)12422 clutter_actor_has_clip (ClutterActor *self)
12423 {
12424   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
12425 
12426   return self->priv->has_clip;
12427 }
12428 
12429 /**
12430  * clutter_actor_get_clip:
12431  * @self: a #ClutterActor
12432  * @xoff: (out) (allow-none): return location for the X offset of
12433  *   the clip rectangle, or %NULL
12434  * @yoff: (out) (allow-none): return location for the Y offset of
12435  *   the clip rectangle, or %NULL
12436  * @width: (out) (allow-none): return location for the width of
12437  *   the clip rectangle, or %NULL
12438  * @height: (out) (allow-none): return location for the height of
12439  *   the clip rectangle, or %NULL
12440  *
12441  * Gets the clip area for @self, if any is set.
12442  *
12443  * Since: 0.6
12444  */
12445 void
clutter_actor_get_clip(ClutterActor * self,gfloat * xoff,gfloat * yoff,gfloat * width,gfloat * height)12446 clutter_actor_get_clip (ClutterActor *self,
12447                         gfloat       *xoff,
12448                         gfloat       *yoff,
12449                         gfloat       *width,
12450                         gfloat       *height)
12451 {
12452   ClutterActorPrivate *priv;
12453 
12454   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12455 
12456   priv = self->priv;
12457 
12458   if (!priv->has_clip)
12459     return;
12460 
12461   if (xoff != NULL)
12462     *xoff = priv->clip.origin.x;
12463 
12464   if (yoff != NULL)
12465     *yoff = priv->clip.origin.y;
12466 
12467   if (width != NULL)
12468     *width = priv->clip.size.width;
12469 
12470   if (height != NULL)
12471     *height = priv->clip.size.height;
12472 }
12473 
12474 /**
12475  * clutter_actor_get_children:
12476  * @self: a #ClutterActor
12477  *
12478  * Retrieves the list of children of @self.
12479  *
12480  * Return value: (transfer container) (element-type ClutterActor): A newly
12481  *   allocated #GList of #ClutterActor<!-- -->s. Use g_list_free() when
12482  *   done.
12483  *
12484  * Since: 1.10
12485  */
12486 GList *
clutter_actor_get_children(ClutterActor * self)12487 clutter_actor_get_children (ClutterActor *self)
12488 {
12489   ClutterActor *iter;
12490   GList *res;
12491 
12492   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
12493 
12494   /* we walk the list backward so that we can use prepend(),
12495    * which is O(1)
12496    */
12497   for (iter = self->priv->last_child, res = NULL;
12498        iter != NULL;
12499        iter = iter->priv->prev_sibling)
12500     {
12501       res = g_list_prepend (res, iter);
12502     }
12503 
12504   return res;
12505 }
12506 
12507 /*< private >
12508  * insert_child_at_depth:
12509  * @self: a #ClutterActor
12510  * @child: a #ClutterActor
12511  *
12512  * Inserts @child inside the list of children held by @self, using
12513  * the depth as the insertion criteria.
12514  *
12515  * This sadly makes the insertion not O(1), but we can keep the
12516  * list sorted so that the painters algorithm we use for painting
12517  * the children will work correctly.
12518  */
12519 static void
insert_child_at_depth(ClutterActor * self,ClutterActor * child,gpointer dummy G_GNUC_UNUSED)12520 insert_child_at_depth (ClutterActor *self,
12521                        ClutterActor *child,
12522                        gpointer      dummy G_GNUC_UNUSED)
12523 {
12524   ClutterActor *iter;
12525   float child_depth;
12526 
12527   child->priv->parent = self;
12528 
12529   child_depth =
12530     _clutter_actor_get_transform_info_or_defaults (child)->z_position;
12531 
12532   /* special-case the first child */
12533   if (self->priv->n_children == 0)
12534     {
12535       self->priv->first_child = child;
12536       self->priv->last_child = child;
12537 
12538       child->priv->next_sibling = NULL;
12539       child->priv->prev_sibling = NULL;
12540 
12541       return;
12542     }
12543 
12544   /* Find the right place to insert the child so that it will still be
12545      sorted and the child will be after all of the actors at the same
12546      dept */
12547   for (iter = self->priv->first_child;
12548        iter != NULL;
12549        iter = iter->priv->next_sibling)
12550     {
12551       float iter_depth;
12552 
12553       iter_depth =
12554         _clutter_actor_get_transform_info_or_defaults (iter)->z_position;
12555 
12556       if (iter_depth > child_depth)
12557         break;
12558     }
12559 
12560   if (iter != NULL)
12561     {
12562       ClutterActor *tmp = iter->priv->prev_sibling;
12563 
12564       if (tmp != NULL)
12565         tmp->priv->next_sibling = child;
12566 
12567       /* Insert the node before the found one */
12568       child->priv->prev_sibling = iter->priv->prev_sibling;
12569       child->priv->next_sibling = iter;
12570       iter->priv->prev_sibling = child;
12571     }
12572   else
12573     {
12574       ClutterActor *tmp = self->priv->last_child;
12575 
12576       if (tmp != NULL)
12577         tmp->priv->next_sibling = child;
12578 
12579       /* insert the node at the end of the list */
12580       child->priv->prev_sibling = self->priv->last_child;
12581       child->priv->next_sibling = NULL;
12582     }
12583 
12584   if (child->priv->prev_sibling == NULL)
12585     self->priv->first_child = child;
12586 
12587   if (child->priv->next_sibling == NULL)
12588     self->priv->last_child = child;
12589 }
12590 
12591 static void
insert_child_at_index(ClutterActor * self,ClutterActor * child,gpointer data_)12592 insert_child_at_index (ClutterActor *self,
12593                        ClutterActor *child,
12594                        gpointer      data_)
12595 {
12596   gint index_ = GPOINTER_TO_INT (data_);
12597 
12598   child->priv->parent = self;
12599 
12600   if (index_ == 0)
12601     {
12602       ClutterActor *tmp = self->priv->first_child;
12603 
12604       if (tmp != NULL)
12605         tmp->priv->prev_sibling = child;
12606 
12607       child->priv->prev_sibling = NULL;
12608       child->priv->next_sibling = tmp;
12609     }
12610   else if (index_ < 0 || index_ >= self->priv->n_children)
12611     {
12612       ClutterActor *tmp = self->priv->last_child;
12613 
12614       if (tmp != NULL)
12615         tmp->priv->next_sibling = child;
12616 
12617       child->priv->prev_sibling = tmp;
12618       child->priv->next_sibling = NULL;
12619     }
12620   else
12621     {
12622       ClutterActor *iter;
12623       int i;
12624 
12625       for (iter = self->priv->first_child, i = 0;
12626            iter != NULL;
12627            iter = iter->priv->next_sibling, i += 1)
12628         {
12629           if (index_ == i)
12630             {
12631               ClutterActor *tmp = iter->priv->prev_sibling;
12632 
12633               child->priv->prev_sibling = tmp;
12634               child->priv->next_sibling = iter;
12635 
12636               iter->priv->prev_sibling = child;
12637 
12638               if (tmp != NULL)
12639                 tmp->priv->next_sibling = child;
12640 
12641               break;
12642             }
12643         }
12644     }
12645 
12646   if (child->priv->prev_sibling == NULL)
12647     self->priv->first_child = child;
12648 
12649   if (child->priv->next_sibling == NULL)
12650     self->priv->last_child = child;
12651 }
12652 
12653 static void
insert_child_above(ClutterActor * self,ClutterActor * child,gpointer data)12654 insert_child_above (ClutterActor *self,
12655                     ClutterActor *child,
12656                     gpointer      data)
12657 {
12658   ClutterActor *sibling = data;
12659 
12660   child->priv->parent = self;
12661 
12662   if (sibling == NULL)
12663     sibling = self->priv->last_child;
12664 
12665   child->priv->prev_sibling = sibling;
12666 
12667   if (sibling != NULL)
12668     {
12669       ClutterActor *tmp = sibling->priv->next_sibling;
12670 
12671       child->priv->next_sibling = tmp;
12672 
12673       if (tmp != NULL)
12674         tmp->priv->prev_sibling = child;
12675 
12676       sibling->priv->next_sibling = child;
12677     }
12678   else
12679     child->priv->next_sibling = NULL;
12680 
12681   if (child->priv->prev_sibling == NULL)
12682     self->priv->first_child = child;
12683 
12684   if (child->priv->next_sibling == NULL)
12685     self->priv->last_child = child;
12686 }
12687 
12688 static void
insert_child_below(ClutterActor * self,ClutterActor * child,gpointer data)12689 insert_child_below (ClutterActor *self,
12690                     ClutterActor *child,
12691                     gpointer      data)
12692 {
12693   ClutterActor *sibling = data;
12694 
12695   child->priv->parent = self;
12696 
12697   if (sibling == NULL)
12698     sibling = self->priv->first_child;
12699 
12700   child->priv->next_sibling = sibling;
12701 
12702   if (sibling != NULL)
12703     {
12704       ClutterActor *tmp = sibling->priv->prev_sibling;
12705 
12706       child->priv->prev_sibling = tmp;
12707 
12708       if (tmp != NULL)
12709         tmp->priv->next_sibling = child;
12710 
12711       sibling->priv->prev_sibling = child;
12712     }
12713   else
12714     child->priv->prev_sibling = NULL;
12715 
12716   if (child->priv->prev_sibling == NULL)
12717     self->priv->first_child = child;
12718 
12719   if (child->priv->next_sibling == NULL)
12720     self->priv->last_child = child;
12721 }
12722 
12723 typedef void (* ClutterActorAddChildFunc) (ClutterActor *parent,
12724                                            ClutterActor *child,
12725                                            gpointer      data);
12726 
12727 typedef enum {
12728   ADD_CHILD_CREATE_META        = 1 << 0,
12729   ADD_CHILD_EMIT_PARENT_SET    = 1 << 1,
12730   ADD_CHILD_EMIT_ACTOR_ADDED   = 1 << 2,
12731   ADD_CHILD_CHECK_STATE        = 1 << 3,
12732   ADD_CHILD_NOTIFY_FIRST_LAST  = 1 << 4,
12733   ADD_CHILD_SHOW_ON_SET_PARENT = 1 << 5,
12734 
12735   /* default flags for public API */
12736   ADD_CHILD_DEFAULT_FLAGS    = ADD_CHILD_CREATE_META |
12737                                ADD_CHILD_EMIT_PARENT_SET |
12738                                ADD_CHILD_EMIT_ACTOR_ADDED |
12739                                ADD_CHILD_CHECK_STATE |
12740                                ADD_CHILD_NOTIFY_FIRST_LAST |
12741                                ADD_CHILD_SHOW_ON_SET_PARENT,
12742 
12743   /* flags for legacy/deprecated API */
12744   ADD_CHILD_LEGACY_FLAGS     = ADD_CHILD_EMIT_PARENT_SET |
12745                                ADD_CHILD_CHECK_STATE |
12746                                ADD_CHILD_NOTIFY_FIRST_LAST |
12747                                ADD_CHILD_SHOW_ON_SET_PARENT
12748 } ClutterActorAddChildFlags;
12749 
12750 /*< private >
12751  * clutter_actor_add_child_internal:
12752  * @self: a #ClutterActor
12753  * @child: a #ClutterActor
12754  * @flags: control flags for actions
12755  * @add_func: delegate function
12756  * @data: (closure): data to pass to @add_func
12757  *
12758  * Adds @child to the list of children of @self.
12759  *
12760  * The actual insertion inside the list is delegated to @add_func: this
12761  * function will just set up the state, perform basic checks, and emit
12762  * signals.
12763  *
12764  * The @flags argument is used to perform additional operations.
12765  */
12766 static inline void
clutter_actor_add_child_internal(ClutterActor * self,ClutterActor * child,ClutterActorAddChildFlags flags,ClutterActorAddChildFunc add_func,gpointer data)12767 clutter_actor_add_child_internal (ClutterActor              *self,
12768                                   ClutterActor              *child,
12769                                   ClutterActorAddChildFlags  flags,
12770                                   ClutterActorAddChildFunc   add_func,
12771                                   gpointer                   data)
12772 {
12773   ClutterTextDirection text_dir;
12774   gboolean create_meta;
12775   gboolean emit_parent_set, emit_actor_added;
12776   gboolean check_state;
12777   gboolean notify_first_last;
12778   gboolean show_on_set_parent;
12779   ClutterActor *old_first_child, *old_last_child;
12780   GObject *obj;
12781 
12782   if (self == child)
12783     {
12784       g_warning ("Cannot add the actor '%s' to itself.",
12785                   _clutter_actor_get_debug_name (self));
12786       return;
12787     }
12788 
12789   if (child->priv->parent != NULL)
12790     {
12791       g_warning ("The actor '%s' already has a parent, '%s'. You must "
12792                  "use clutter_actor_remove_child() first.",
12793                  _clutter_actor_get_debug_name (child),
12794                  _clutter_actor_get_debug_name (child->priv->parent));
12795       return;
12796     }
12797 
12798   if (CLUTTER_ACTOR_IS_TOPLEVEL (child))
12799     {
12800       g_warning ("The actor '%s' is a top-level actor, and cannot be "
12801                  "a child of another actor.",
12802                  _clutter_actor_get_debug_name (child));
12803       return;
12804     }
12805 
12806   /* the following check disallows calling methods that change the stacking
12807    * order within the destruction sequence, by triggering a critical
12808    * warning first, and leaving the actor in an undefined state, which
12809    * then ends up being caught by an assertion.
12810    *
12811    * the reproducible sequence is:
12812    *
12813    *   - actor gets destroyed;
12814    *   - another actor, linked to the first, will try to change the
12815    *     stacking order of the first actor;
12816    *   - changing the stacking order is a composite operation composed
12817    *     by the following steps:
12818    *     1. ref() the child;
12819    *     2. remove_child_internal(), which removes the reference;
12820    *     3. add_child_internal(), which adds a reference;
12821    *   - the state of the actor is not changed between (2) and (3), as
12822    *     it could be an expensive recomputation;
12823    *   - if (3) bails out, then the actor is in an undefined state, but
12824    *     still alive;
12825    *   - the destruction sequence terminates, but the actor is unparented
12826    *     while its state indicates being parented instead.
12827    *   - assertion failure.
12828    *
12829    * the obvious fix would be to decompose each set_child_*_sibling()
12830    * method into proper remove_child()/add_child(), with state validation;
12831    * this may cause excessive work, though, and trigger a cascade of other
12832    * bugs in code that assumes that a change in the stacking order is an
12833    * atomic operation.
12834    *
12835    * another potential fix is to just remove this check here, and let
12836    * code doing stacking order changes inside the destruction sequence
12837    * of an actor continue doing the stacking changes as before; this
12838    * option still performs more work than necessary.
12839    *
12840    * the third fix is to silently bail out early from every
12841    * set_child_*_sibling() and set_child_at_index() method, and avoid
12842    * doing stack changes altogether; Clutter implements this last option.
12843    *
12844    * see bug: https://bugzilla.gnome.org/show_bug.cgi?id=670647
12845    */
12846   if (CLUTTER_ACTOR_IN_DESTRUCTION (child))
12847     {
12848       g_warning ("The actor '%s' is currently being destroyed, and "
12849                  "cannot be added as a child of another actor.",
12850                  _clutter_actor_get_debug_name (child));
12851       return;
12852     }
12853 
12854   create_meta = (flags & ADD_CHILD_CREATE_META) != 0;
12855   emit_parent_set = (flags & ADD_CHILD_EMIT_PARENT_SET) != 0;
12856   emit_actor_added = (flags & ADD_CHILD_EMIT_ACTOR_ADDED) != 0;
12857   check_state = (flags & ADD_CHILD_CHECK_STATE) != 0;
12858   notify_first_last = (flags & ADD_CHILD_NOTIFY_FIRST_LAST) != 0;
12859   show_on_set_parent = (flags & ADD_CHILD_SHOW_ON_SET_PARENT) != 0;
12860 
12861   old_first_child = self->priv->first_child;
12862   old_last_child = self->priv->last_child;
12863 
12864   obj = G_OBJECT (self);
12865   g_object_freeze_notify (obj);
12866 
12867   if (create_meta)
12868     clutter_container_create_child_meta (CLUTTER_CONTAINER (self), child);
12869 
12870   g_object_ref_sink (child);
12871   child->priv->parent = NULL;
12872   child->priv->next_sibling = NULL;
12873   child->priv->prev_sibling = NULL;
12874 
12875   /* delegate the actual insertion */
12876   add_func (self, child, data);
12877 
12878   g_assert (child->priv->parent == self);
12879 
12880   self->priv->n_children += 1;
12881 
12882   self->priv->age += 1;
12883 
12884   /* if push_internal() has been called then we automatically set
12885    * the flag on the actor
12886    */
12887   if (self->priv->internal_child)
12888     CLUTTER_SET_PRIVATE_FLAGS (child, CLUTTER_INTERNAL_CHILD);
12889 
12890   /* children may cause their parent to expand, if they are set
12891    * to expand; if a child is not expanded then it cannot change
12892    * its parent's state. any further change later on will queue
12893    * an expand state check.
12894    *
12895    * this check, with the initial state of the needs_compute_expand
12896    * flag set to FALSE, should avoid recomputing the expand flags
12897    * state while building the actor tree.
12898    */
12899   if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
12900       (child->priv->needs_compute_expand ||
12901        child->priv->needs_x_expand ||
12902        child->priv->needs_y_expand))
12903     {
12904       clutter_actor_queue_compute_expand (self);
12905     }
12906 
12907   /* clutter_actor_reparent() will emit ::parent-set for us */
12908   if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child))
12909     g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL);
12910 
12911   if (check_state)
12912     {
12913       /* If parent is mapped or realized, we need to also be mapped or
12914        * realized once we're inside the parent.
12915        */
12916       clutter_actor_update_map_state (child, MAP_STATE_CHECK);
12917 
12918       /* propagate the parent's text direction to the child */
12919       text_dir = clutter_actor_get_text_direction (self);
12920       clutter_actor_set_text_direction (child, text_dir);
12921     }
12922 
12923   /* this may end up queueing a redraw, in case the actor is
12924    * not visible but the show-on-set-parent property is still
12925    * set.
12926    *
12927    * XXX:2.0 - remove this check and unconditionally show() the
12928    * actor once we remove the show-on-set-parent property
12929    */
12930   if (show_on_set_parent && child->priv->show_on_set_parent)
12931     clutter_actor_show (child);
12932 
12933   /* on the other hand, this will catch any other case where
12934    * the actor is supposed to be visible when it's added
12935    */
12936   if (CLUTTER_ACTOR_IS_MAPPED (child))
12937     clutter_actor_queue_redraw (child);
12938 
12939   /* maintain the invariant that if an actor needs layout,
12940    * its parents do as well
12941    */
12942   if (child->priv->needs_width_request ||
12943       child->priv->needs_height_request ||
12944       child->priv->needs_allocation)
12945     {
12946       /* we work around the short-circuiting we do
12947        * in clutter_actor_queue_relayout() since we
12948        * want to force a relayout
12949        */
12950       child->priv->needs_width_request = TRUE;
12951       child->priv->needs_height_request = TRUE;
12952       child->priv->needs_allocation = TRUE;
12953 
12954       /* we only queue a relayout here, because any possible
12955        * redraw has already been queued either by show() or
12956        * by our call to queue_redraw() above
12957        */
12958       _clutter_actor_queue_only_relayout (child->priv->parent);
12959     }
12960 
12961   if (emit_actor_added)
12962     g_signal_emit_by_name (self, "actor-added", child);
12963 
12964   if (notify_first_last)
12965     {
12966       if (old_first_child != self->priv->first_child)
12967         g_object_notify_by_pspec (obj, obj_props[PROP_FIRST_CHILD]);
12968 
12969       if (old_last_child != self->priv->last_child)
12970         g_object_notify_by_pspec (obj, obj_props[PROP_LAST_CHILD]);
12971     }
12972 
12973   g_object_thaw_notify (obj);
12974 }
12975 
12976 /**
12977  * clutter_actor_add_child:
12978  * @self: a #ClutterActor
12979  * @child: a #ClutterActor
12980  *
12981  * Adds @child to the children of @self.
12982  *
12983  * This function will acquire a reference on @child that will only
12984  * be released when calling clutter_actor_remove_child().
12985  *
12986  * This function will take into consideration the #ClutterActor:depth
12987  * of @child, and will keep the list of children sorted.
12988  *
12989  * This function will emit the #ClutterContainer::actor-added signal
12990  * on @self.
12991  *
12992  * Since: 1.10
12993  */
12994 void
clutter_actor_add_child(ClutterActor * self,ClutterActor * child)12995 clutter_actor_add_child (ClutterActor *self,
12996                          ClutterActor *child)
12997 {
12998   g_return_if_fail (CLUTTER_IS_ACTOR (self));
12999   g_return_if_fail (CLUTTER_IS_ACTOR (child));
13000   g_return_if_fail (self != child);
13001   g_return_if_fail (child->priv->parent == NULL);
13002 
13003   clutter_actor_add_child_internal (self, child,
13004                                     ADD_CHILD_DEFAULT_FLAGS,
13005                                     insert_child_at_depth,
13006                                     NULL);
13007 }
13008 
13009 /**
13010  * clutter_actor_insert_child_at_index:
13011  * @self: a #ClutterActor
13012  * @child: a #ClutterActor
13013  * @index_: the index
13014  *
13015  * Inserts @child into the list of children of @self, using the
13016  * given @index_. If @index_ is greater than the number of children
13017  * in @self, or is less than 0, then the new child is added at the end.
13018  *
13019  * This function will acquire a reference on @child that will only
13020  * be released when calling clutter_actor_remove_child().
13021  *
13022  * This function will not take into consideration the #ClutterActor:depth
13023  * of @child.
13024  *
13025  * This function will emit the #ClutterContainer::actor-added signal
13026  * on @self.
13027  *
13028  * Since: 1.10
13029  */
13030 void
clutter_actor_insert_child_at_index(ClutterActor * self,ClutterActor * child,gint index_)13031 clutter_actor_insert_child_at_index (ClutterActor *self,
13032                                      ClutterActor *child,
13033                                      gint          index_)
13034 {
13035   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13036   g_return_if_fail (CLUTTER_IS_ACTOR (child));
13037   g_return_if_fail (self != child);
13038   g_return_if_fail (child->priv->parent == NULL);
13039 
13040   clutter_actor_add_child_internal (self, child,
13041                                     ADD_CHILD_DEFAULT_FLAGS,
13042                                     insert_child_at_index,
13043                                     GINT_TO_POINTER (index_));
13044 }
13045 
13046 /**
13047  * clutter_actor_insert_child_above:
13048  * @self: a #ClutterActor
13049  * @child: a #ClutterActor
13050  * @sibling: (allow-none): a child of @self, or %NULL
13051  *
13052  * Inserts @child into the list of children of @self, above another
13053  * child of @self or, if @sibling is %NULL, above all the children
13054  * of @self.
13055  *
13056  * This function will acquire a reference on @child that will only
13057  * be released when calling clutter_actor_remove_child().
13058  *
13059  * This function will not take into consideration the #ClutterActor:depth
13060  * of @child.
13061  *
13062  * This function will emit the #ClutterContainer::actor-added signal
13063  * on @self.
13064  *
13065  * Since: 1.10
13066  */
13067 void
clutter_actor_insert_child_above(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)13068 clutter_actor_insert_child_above (ClutterActor *self,
13069                                   ClutterActor *child,
13070                                   ClutterActor *sibling)
13071 {
13072   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13073   g_return_if_fail (CLUTTER_IS_ACTOR (child));
13074   g_return_if_fail (self != child);
13075   g_return_if_fail (child != sibling);
13076   g_return_if_fail (child->priv->parent == NULL);
13077   g_return_if_fail (sibling == NULL ||
13078                     (CLUTTER_IS_ACTOR (sibling) &&
13079                      sibling->priv->parent == self));
13080 
13081   clutter_actor_add_child_internal (self, child,
13082                                     ADD_CHILD_DEFAULT_FLAGS,
13083                                     insert_child_above,
13084                                     sibling);
13085 }
13086 
13087 /**
13088  * clutter_actor_insert_child_below:
13089  * @self: a #ClutterActor
13090  * @child: a #ClutterActor
13091  * @sibling: (allow-none): a child of @self, or %NULL
13092  *
13093  * Inserts @child into the list of children of @self, below another
13094  * child of @self or, if @sibling is %NULL, below all the children
13095  * of @self.
13096  *
13097  * This function will acquire a reference on @child that will only
13098  * be released when calling clutter_actor_remove_child().
13099  *
13100  * This function will not take into consideration the #ClutterActor:depth
13101  * of @child.
13102  *
13103  * This function will emit the #ClutterContainer::actor-added signal
13104  * on @self.
13105  *
13106  * Since: 1.10
13107  */
13108 void
clutter_actor_insert_child_below(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)13109 clutter_actor_insert_child_below (ClutterActor *self,
13110                                   ClutterActor *child,
13111                                   ClutterActor *sibling)
13112 {
13113   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13114   g_return_if_fail (CLUTTER_IS_ACTOR (child));
13115   g_return_if_fail (self != child);
13116   g_return_if_fail (child != sibling);
13117   g_return_if_fail (child->priv->parent == NULL);
13118   g_return_if_fail (sibling == NULL ||
13119                     (CLUTTER_IS_ACTOR (sibling) &&
13120                      sibling->priv->parent == self));
13121 
13122   clutter_actor_add_child_internal (self, child,
13123                                     ADD_CHILD_DEFAULT_FLAGS,
13124                                     insert_child_below,
13125                                     sibling);
13126 }
13127 
13128 /**
13129  * clutter_actor_set_parent:
13130  * @self: A #ClutterActor
13131  * @parent: A new #ClutterActor parent
13132  *
13133  * Sets the parent of @self to @parent.
13134  *
13135  * This function will result in @parent acquiring a reference on @self,
13136  * eventually by sinking its floating reference first. The reference
13137  * will be released by clutter_actor_unparent().
13138  *
13139  * This function should only be called by legacy #ClutterActor<!-- -->s
13140  * implementing the #ClutterContainer interface.
13141  *
13142  * Deprecated: 1.10: Use clutter_actor_add_child() instead.
13143  */
13144 void
clutter_actor_set_parent(ClutterActor * self,ClutterActor * parent)13145 clutter_actor_set_parent (ClutterActor *self,
13146 		          ClutterActor *parent)
13147 {
13148   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13149   g_return_if_fail (CLUTTER_IS_ACTOR (parent));
13150   g_return_if_fail (self != parent);
13151   g_return_if_fail (self->priv->parent == NULL);
13152 
13153   /* as this function will be called inside ClutterContainer::add
13154    * implementations or when building up a composite actor, we have
13155    * to preserve the old behaviour, and not create child meta or
13156    * emit the ::actor-added signal, to avoid recursion or double
13157    * emissions
13158    */
13159   clutter_actor_add_child_internal (parent, self,
13160                                     ADD_CHILD_LEGACY_FLAGS,
13161                                     insert_child_at_depth,
13162                                     NULL);
13163 }
13164 
13165 /**
13166  * clutter_actor_get_parent:
13167  * @self: A #ClutterActor
13168  *
13169  * Retrieves the parent of @self.
13170  *
13171  * Return Value: (transfer none): The #ClutterActor parent, or %NULL
13172  *  if no parent is set
13173  */
13174 ClutterActor *
clutter_actor_get_parent(ClutterActor * self)13175 clutter_actor_get_parent (ClutterActor *self)
13176 {
13177   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
13178 
13179   return self->priv->parent;
13180 }
13181 
13182 /**
13183  * clutter_actor_get_paint_visibility:
13184  * @self: A #ClutterActor
13185  *
13186  * Retrieves the 'paint' visibility of an actor recursively checking for non
13187  * visible parents.
13188  *
13189  * This is by definition the same as %CLUTTER_ACTOR_IS_MAPPED.
13190  *
13191  * Return Value: %TRUE if the actor is visibile and will be painted.
13192  *
13193  * Since: 0.8
13194  */
13195 gboolean
clutter_actor_get_paint_visibility(ClutterActor * actor)13196 clutter_actor_get_paint_visibility (ClutterActor *actor)
13197 {
13198   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
13199 
13200   return CLUTTER_ACTOR_IS_MAPPED (actor);
13201 }
13202 
13203 /**
13204  * clutter_actor_remove_child:
13205  * @self: a #ClutterActor
13206  * @child: a #ClutterActor
13207  *
13208  * Removes @child from the children of @self.
13209  *
13210  * This function will release the reference added by
13211  * clutter_actor_add_child(), so if you want to keep using @child
13212  * you will have to acquire a referenced on it before calling this
13213  * function.
13214  *
13215  * This function will emit the #ClutterContainer::actor-removed
13216  * signal on @self.
13217  *
13218  * Since: 1.10
13219  */
13220 void
clutter_actor_remove_child(ClutterActor * self,ClutterActor * child)13221 clutter_actor_remove_child (ClutterActor *self,
13222                             ClutterActor *child)
13223 {
13224   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13225   g_return_if_fail (CLUTTER_IS_ACTOR (child));
13226   g_return_if_fail (self != child);
13227   g_return_if_fail (child->priv->parent != NULL);
13228   g_return_if_fail (child->priv->parent == self);
13229 
13230   clutter_actor_remove_child_internal (self, child,
13231                                        REMOVE_CHILD_DEFAULT_FLAGS);
13232 }
13233 
13234 /**
13235  * clutter_actor_remove_all_children:
13236  * @self: a #ClutterActor
13237  *
13238  * Removes all children of @self.
13239  *
13240  * This function releases the reference added by inserting a child actor
13241  * in the list of children of @self.
13242  *
13243  * If the reference count of a child drops to zero, the child will be
13244  * destroyed. If you want to ensure the destruction of all the children
13245  * of @self, use clutter_actor_destroy_all_children().
13246  *
13247  * Since: 1.10
13248  */
13249 void
clutter_actor_remove_all_children(ClutterActor * self)13250 clutter_actor_remove_all_children (ClutterActor *self)
13251 {
13252   ClutterActorIter iter;
13253 
13254   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13255 
13256   if (self->priv->n_children == 0)
13257     return;
13258 
13259   g_object_freeze_notify (G_OBJECT (self));
13260 
13261   clutter_actor_iter_init (&iter, self);
13262   while (clutter_actor_iter_next (&iter, NULL))
13263     clutter_actor_iter_remove (&iter);
13264 
13265   g_object_thaw_notify (G_OBJECT (self));
13266 
13267   /* sanity check */
13268   g_assert (self->priv->first_child == NULL);
13269   g_assert (self->priv->last_child == NULL);
13270   g_assert (self->priv->n_children == 0);
13271 }
13272 
13273 /**
13274  * clutter_actor_destroy_all_children:
13275  * @self: a #ClutterActor
13276  *
13277  * Destroys all children of @self.
13278  *
13279  * This function releases the reference added by inserting a child
13280  * actor in the list of children of @self, and ensures that the
13281  * #ClutterActor::destroy signal is emitted on each child of the
13282  * actor.
13283  *
13284  * By default, #ClutterActor will emit the #ClutterActor::destroy signal
13285  * when its reference count drops to 0; the default handler of the
13286  * #ClutterActor::destroy signal will destroy all the children of an
13287  * actor. This function ensures that all children are destroyed, instead
13288  * of just removed from @self, unlike clutter_actor_remove_all_children()
13289  * which will merely release the reference and remove each child.
13290  *
13291  * Unless you acquired an additional reference on each child of @self
13292  * prior to calling clutter_actor_remove_all_children() and want to reuse
13293  * the actors, you should use clutter_actor_destroy_all_children() in
13294  * order to make sure that children are destroyed and signal handlers
13295  * are disconnected even in cases where circular references prevent this
13296  * from automatically happening through reference counting alone.
13297  *
13298  * Since: 1.10
13299  */
13300 void
clutter_actor_destroy_all_children(ClutterActor * self)13301 clutter_actor_destroy_all_children (ClutterActor *self)
13302 {
13303   ClutterActorIter iter;
13304 
13305   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13306 
13307   if (self->priv->n_children == 0)
13308     return;
13309 
13310   g_object_freeze_notify (G_OBJECT (self));
13311 
13312   clutter_actor_iter_init (&iter, self);
13313   while (clutter_actor_iter_next (&iter, NULL))
13314     clutter_actor_iter_destroy (&iter);
13315 
13316   g_object_thaw_notify (G_OBJECT (self));
13317 
13318   /* sanity check */
13319   g_assert (self->priv->first_child == NULL);
13320   g_assert (self->priv->last_child == NULL);
13321   g_assert (self->priv->n_children == 0);
13322 }
13323 
13324 typedef struct _InsertBetweenData {
13325   ClutterActor *prev_sibling;
13326   ClutterActor *next_sibling;
13327 } InsertBetweenData;
13328 
13329 static void
insert_child_between(ClutterActor * self,ClutterActor * child,gpointer data_)13330 insert_child_between (ClutterActor *self,
13331                       ClutterActor *child,
13332                       gpointer      data_)
13333 {
13334   InsertBetweenData *data = data_;
13335   ClutterActor *prev_sibling = data->prev_sibling;
13336   ClutterActor *next_sibling = data->next_sibling;
13337 
13338   child->priv->parent = self;
13339   child->priv->prev_sibling = prev_sibling;
13340   child->priv->next_sibling = next_sibling;
13341 
13342   if (prev_sibling != NULL)
13343     prev_sibling->priv->next_sibling = child;
13344 
13345   if (next_sibling != NULL)
13346     next_sibling->priv->prev_sibling = child;
13347 
13348   if (child->priv->prev_sibling == NULL)
13349     self->priv->first_child = child;
13350 
13351   if (child->priv->next_sibling == NULL)
13352     self->priv->last_child = child;
13353 }
13354 
13355 /**
13356  * clutter_actor_replace_child:
13357  * @self: a #ClutterActor
13358  * @old_child: the child of @self to replace
13359  * @new_child: the #ClutterActor to replace @old_child
13360  *
13361  * Replaces @old_child with @new_child in the list of children of @self.
13362  *
13363  * Since: 1.10
13364  */
13365 void
clutter_actor_replace_child(ClutterActor * self,ClutterActor * old_child,ClutterActor * new_child)13366 clutter_actor_replace_child (ClutterActor *self,
13367                              ClutterActor *old_child,
13368                              ClutterActor *new_child)
13369 {
13370   ClutterActor *prev_sibling, *next_sibling;
13371   InsertBetweenData clos;
13372 
13373   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13374   g_return_if_fail (CLUTTER_IS_ACTOR (old_child));
13375   g_return_if_fail (old_child->priv->parent == self);
13376   g_return_if_fail (CLUTTER_IS_ACTOR (new_child));
13377   g_return_if_fail (old_child != new_child);
13378   g_return_if_fail (new_child != self);
13379   g_return_if_fail (new_child->priv->parent == NULL);
13380 
13381   prev_sibling = old_child->priv->prev_sibling;
13382   next_sibling = old_child->priv->next_sibling;
13383   clutter_actor_remove_child_internal (self, old_child,
13384                                        REMOVE_CHILD_DEFAULT_FLAGS);
13385 
13386   clos.prev_sibling = prev_sibling;
13387   clos.next_sibling = next_sibling;
13388   clutter_actor_add_child_internal (self, new_child,
13389                                     ADD_CHILD_DEFAULT_FLAGS,
13390                                     insert_child_between,
13391                                     &clos);
13392 }
13393 
13394 /**
13395  * clutter_actor_unparent:
13396  * @self: a #ClutterActor
13397  *
13398  * Removes the parent of @self.
13399  *
13400  * This will cause the parent of @self to release the reference
13401  * acquired when calling clutter_actor_set_parent(), so if you
13402  * want to keep @self you will have to acquire a reference of
13403  * your own, through g_object_ref().
13404  *
13405  * This function should only be called by legacy #ClutterActor<!-- -->s
13406  * implementing the #ClutterContainer interface.
13407  *
13408  * Since: 0.2
13409  *
13410  * Deprecated: 1.10: Use clutter_actor_remove_child() instead.
13411  */
13412 void
clutter_actor_unparent(ClutterActor * self)13413 clutter_actor_unparent (ClutterActor *self)
13414 {
13415   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13416 
13417   if (self->priv->parent == NULL)
13418     return;
13419 
13420   clutter_actor_remove_child_internal (self->priv->parent, self,
13421                                        REMOVE_CHILD_LEGACY_FLAGS);
13422 }
13423 
13424 /**
13425  * clutter_actor_reparent:
13426  * @self: a #ClutterActor
13427  * @new_parent: the new #ClutterActor parent
13428  *
13429  * Resets the parent actor of @self.
13430  *
13431  * This function is logically equivalent to calling clutter_actor_unparent()
13432  * and clutter_actor_set_parent(), but more efficiently implemented, as it
13433  * ensures the child is not finalized when unparented, and emits the
13434  * #ClutterActor::parent-set signal only once.
13435  *
13436  * In reality, calling this function is less useful than it sounds, as some
13437  * application code may rely on changes in the intermediate state between
13438  * removal and addition of the actor from its old parent to the @new_parent.
13439  * Thus, it is strongly encouraged to avoid using this function in application
13440  * code.
13441  *
13442  * Since: 0.2
13443  *
13444  * Deprecated: 1.10: Use clutter_actor_remove_child() and
13445  *   clutter_actor_add_child() instead; remember to take a reference on
13446  *   the actor being removed before calling clutter_actor_remove_child()
13447  *   to avoid the reference count dropping to zero and the actor being
13448  *   destroyed.
13449  */
13450 void
clutter_actor_reparent(ClutterActor * self,ClutterActor * new_parent)13451 clutter_actor_reparent (ClutterActor *self,
13452                         ClutterActor *new_parent)
13453 {
13454   ClutterActorPrivate *priv;
13455 
13456   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13457   g_return_if_fail (CLUTTER_IS_ACTOR (new_parent));
13458   g_return_if_fail (self != new_parent);
13459 
13460   if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
13461     {
13462       g_warning ("Cannot set a parent on a toplevel actor");
13463       return;
13464     }
13465 
13466   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
13467     {
13468       g_warning ("Cannot set a parent currently being destroyed");
13469       return;
13470     }
13471 
13472   priv = self->priv;
13473 
13474   if (priv->parent != new_parent)
13475     {
13476       ClutterActor *old_parent;
13477 
13478       CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_REPARENT);
13479 
13480       old_parent = priv->parent;
13481 
13482       g_object_ref (self);
13483 
13484       if (old_parent != NULL)
13485         {
13486          /* go through the Container implementation if this is a regular
13487           * child and not an internal one
13488           */
13489          if (!CLUTTER_ACTOR_IS_INTERNAL_CHILD (self))
13490            {
13491              ClutterContainer *parent = CLUTTER_CONTAINER (old_parent);
13492 
13493              /* this will have to call unparent() */
13494              clutter_container_remove_actor (parent, self);
13495            }
13496          else
13497            clutter_actor_remove_child_internal (old_parent, self,
13498                                                 REMOVE_CHILD_LEGACY_FLAGS);
13499         }
13500 
13501       /* Note, will call set_parent() */
13502       if (!CLUTTER_ACTOR_IS_INTERNAL_CHILD (self))
13503         clutter_container_add_actor (CLUTTER_CONTAINER (new_parent), self);
13504       else
13505         clutter_actor_add_child_internal (new_parent, self,
13506                                           ADD_CHILD_LEGACY_FLAGS,
13507                                           insert_child_at_depth,
13508                                           NULL);
13509 
13510       /* we emit the ::parent-set signal once */
13511       g_signal_emit (self, actor_signals[PARENT_SET], 0, old_parent);
13512 
13513       CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_REPARENT);
13514 
13515       /* the IN_REPARENT flag suspends state updates */
13516       clutter_actor_update_map_state (self, MAP_STATE_CHECK);
13517 
13518       g_object_unref (self);
13519    }
13520 }
13521 
13522 /**
13523  * clutter_actor_contains:
13524  * @self: A #ClutterActor
13525  * @descendant: A #ClutterActor, possibly contained in @self
13526  *
13527  * Determines if @descendant is contained inside @self (either as an
13528  * immediate child, or as a deeper descendant). If @self and
13529  * @descendant point to the same actor then it will also return %TRUE.
13530  *
13531  * Return value: whether @descendent is contained within @self
13532  *
13533  * Since: 1.4
13534  */
13535 gboolean
clutter_actor_contains(ClutterActor * self,ClutterActor * descendant)13536 clutter_actor_contains (ClutterActor *self,
13537 			ClutterActor *descendant)
13538 {
13539   ClutterActor *actor;
13540 
13541   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
13542   g_return_val_if_fail (CLUTTER_IS_ACTOR (descendant), FALSE);
13543 
13544   for (actor = descendant; actor; actor = actor->priv->parent)
13545     if (actor == self)
13546       return TRUE;
13547 
13548   return FALSE;
13549 }
13550 
13551 /**
13552  * clutter_actor_set_child_above_sibling:
13553  * @self: a #ClutterActor
13554  * @child: a #ClutterActor child of @self
13555  * @sibling: (allow-none): a #ClutterActor child of @self, or %NULL
13556  *
13557  * Sets @child to be above @sibling in the list of children of @self.
13558  *
13559  * If @sibling is %NULL, @child will be the new last child of @self.
13560  *
13561  * This function is logically equivalent to removing @child and using
13562  * clutter_actor_insert_child_above(), but it will not emit signals
13563  * or change state on @child.
13564  *
13565  * Since: 1.10
13566  */
13567 void
clutter_actor_set_child_above_sibling(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)13568 clutter_actor_set_child_above_sibling (ClutterActor *self,
13569                                        ClutterActor *child,
13570                                        ClutterActor *sibling)
13571 {
13572   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13573   g_return_if_fail (CLUTTER_IS_ACTOR (child));
13574   g_return_if_fail (child->priv->parent == self);
13575   g_return_if_fail (child != sibling);
13576   g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
13577 
13578   if (sibling != NULL)
13579     g_return_if_fail (sibling->priv->parent == self);
13580 
13581   if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
13582       CLUTTER_ACTOR_IN_DESTRUCTION (child) ||
13583       (sibling != NULL && CLUTTER_ACTOR_IN_DESTRUCTION (sibling)))
13584     return;
13585 
13586   /* we don't want to change the state of child, or emit signals, or
13587    * regenerate ChildMeta instances here, but we still want to follow
13588    * the correct sequence of steps encoded in remove_child() and
13589    * add_child(), so that correctness is ensured, and we only go
13590    * through one known code path.
13591    */
13592   g_object_ref (child);
13593   clutter_actor_remove_child_internal (self, child, 0);
13594   clutter_actor_add_child_internal (self, child,
13595                                     ADD_CHILD_NOTIFY_FIRST_LAST,
13596                                     insert_child_above,
13597                                     sibling);
13598   g_object_unref(child);
13599 
13600   clutter_actor_queue_relayout (self);
13601 }
13602 
13603 /**
13604  * clutter_actor_set_child_below_sibling:
13605  * @self: a #ClutterActor
13606  * @child: a #ClutterActor child of @self
13607  * @sibling: (allow-none): a #ClutterActor child of @self, or %NULL
13608  *
13609  * Sets @child to be below @sibling in the list of children of @self.
13610  *
13611  * If @sibling is %NULL, @child will be the new first child of @self.
13612  *
13613  * This function is logically equivalent to removing @self and using
13614  * clutter_actor_insert_child_below(), but it will not emit signals
13615  * or change state on @child.
13616  *
13617  * Since: 1.10
13618  */
13619 void
clutter_actor_set_child_below_sibling(ClutterActor * self,ClutterActor * child,ClutterActor * sibling)13620 clutter_actor_set_child_below_sibling (ClutterActor *self,
13621                                        ClutterActor *child,
13622                                        ClutterActor *sibling)
13623 {
13624   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13625   g_return_if_fail (CLUTTER_IS_ACTOR (child));
13626   g_return_if_fail (child->priv->parent == self);
13627   g_return_if_fail (child != sibling);
13628   g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
13629 
13630   if (sibling != NULL)
13631     g_return_if_fail (sibling->priv->parent == self);
13632 
13633   if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
13634       CLUTTER_ACTOR_IN_DESTRUCTION (child) ||
13635       (sibling != NULL && CLUTTER_ACTOR_IN_DESTRUCTION (sibling)))
13636     return;
13637 
13638   /* see the comment in set_child_above_sibling() */
13639   g_object_ref (child);
13640   clutter_actor_remove_child_internal (self, child, 0);
13641   clutter_actor_add_child_internal (self, child,
13642                                     ADD_CHILD_NOTIFY_FIRST_LAST,
13643                                     insert_child_below,
13644                                     sibling);
13645   g_object_unref(child);
13646 
13647   clutter_actor_queue_relayout (self);
13648 }
13649 
13650 /**
13651  * clutter_actor_set_child_at_index:
13652  * @self: a #ClutterActor
13653  * @child: a #ClutterActor child of @self
13654  * @index_: the new index for @child
13655  *
13656  * Changes the index of @child in the list of children of @self.
13657  *
13658  * This function is logically equivalent to removing @child and
13659  * calling clutter_actor_insert_child_at_index(), but it will not
13660  * emit signals or change state on @child.
13661  *
13662  * Since: 1.10
13663  */
13664 void
clutter_actor_set_child_at_index(ClutterActor * self,ClutterActor * child,gint index_)13665 clutter_actor_set_child_at_index (ClutterActor *self,
13666                                   ClutterActor *child,
13667                                   gint          index_)
13668 {
13669   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13670   g_return_if_fail (CLUTTER_IS_ACTOR (child));
13671   g_return_if_fail (child->priv->parent == self);
13672   g_return_if_fail (index_ <= self->priv->n_children);
13673 
13674   if (CLUTTER_ACTOR_IN_DESTRUCTION (self) ||
13675       CLUTTER_ACTOR_IN_DESTRUCTION (child))
13676     return;
13677 
13678   g_object_ref (child);
13679   clutter_actor_remove_child_internal (self, child, 0);
13680   clutter_actor_add_child_internal (self, child,
13681                                     ADD_CHILD_NOTIFY_FIRST_LAST,
13682                                     insert_child_at_index,
13683                                     GINT_TO_POINTER (index_));
13684   g_object_unref (child);
13685 
13686   clutter_actor_queue_relayout (self);
13687 }
13688 
13689 /**
13690  * clutter_actor_raise:
13691  * @self: A #ClutterActor
13692  * @below: (allow-none): A #ClutterActor to raise above.
13693  *
13694  * Puts @self above @below.
13695  *
13696  * Both actors must have the same parent, and the parent must implement
13697  * the #ClutterContainer interface
13698  *
13699  * This function calls clutter_container_raise_child() internally.
13700  *
13701  * Deprecated: 1.10: Use clutter_actor_set_child_above_sibling() instead.
13702  */
13703 void
clutter_actor_raise(ClutterActor * self,ClutterActor * below)13704 clutter_actor_raise (ClutterActor *self,
13705                      ClutterActor *below)
13706 {
13707   ClutterActor *parent;
13708 
13709   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13710 
13711   parent = clutter_actor_get_parent (self);
13712   if (parent == NULL)
13713     {
13714       g_warning ("%s: Actor '%s' is not inside a container",
13715                  G_STRFUNC,
13716                  _clutter_actor_get_debug_name (self));
13717       return;
13718     }
13719 
13720   if (below != NULL)
13721     {
13722       if (parent != clutter_actor_get_parent (below))
13723         {
13724           g_warning ("%s Actor '%s' is not in the same container as "
13725                      "actor '%s'",
13726                      G_STRFUNC,
13727                      _clutter_actor_get_debug_name (self),
13728                      _clutter_actor_get_debug_name (below));
13729           return;
13730         }
13731     }
13732 
13733   clutter_container_raise_child (CLUTTER_CONTAINER (parent), self, below);
13734 }
13735 
13736 /**
13737  * clutter_actor_lower:
13738  * @self: A #ClutterActor
13739  * @above: (allow-none): A #ClutterActor to lower below
13740  *
13741  * Puts @self below @above.
13742  *
13743  * Both actors must have the same parent, and the parent must implement
13744  * the #ClutterContainer interface.
13745  *
13746  * This function calls clutter_container_lower_child() internally.
13747  *
13748  * Deprecated: 1.10: Use clutter_actor_set_child_below_sibling() instead.
13749  */
13750 void
clutter_actor_lower(ClutterActor * self,ClutterActor * above)13751 clutter_actor_lower (ClutterActor *self,
13752                      ClutterActor *above)
13753 {
13754   ClutterActor *parent;
13755 
13756   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13757 
13758   parent = clutter_actor_get_parent (self);
13759   if (parent == NULL)
13760     {
13761       g_warning ("%s: Actor of type %s is not inside a container",
13762                  G_STRFUNC,
13763                  _clutter_actor_get_debug_name (self));
13764       return;
13765     }
13766 
13767   if (above)
13768     {
13769       if (parent != clutter_actor_get_parent (above))
13770         {
13771           g_warning ("%s: Actor '%s' is not in the same container as "
13772                      "actor '%s'",
13773                      G_STRFUNC,
13774                      _clutter_actor_get_debug_name (self),
13775                      _clutter_actor_get_debug_name (above));
13776           return;
13777         }
13778     }
13779 
13780   clutter_container_lower_child (CLUTTER_CONTAINER (parent), self, above);
13781 }
13782 
13783 /**
13784  * clutter_actor_raise_top:
13785  * @self: A #ClutterActor
13786  *
13787  * Raises @self to the top.
13788  *
13789  * This function calls clutter_actor_raise() internally.
13790  *
13791  * Deprecated: 1.10: Use clutter_actor_set_child_above_sibling() with
13792  *   a %NULL sibling, instead.
13793  */
13794 void
clutter_actor_raise_top(ClutterActor * self)13795 clutter_actor_raise_top (ClutterActor *self)
13796 {
13797   clutter_actor_raise (self, NULL);
13798 }
13799 
13800 /**
13801  * clutter_actor_lower_bottom:
13802  * @self: A #ClutterActor
13803  *
13804  * Lowers @self to the bottom.
13805  *
13806  * This function calls clutter_actor_lower() internally.
13807  *
13808  * Deprecated: 1.10: Use clutter_actor_set_child_below_sibling() with
13809  *   a %NULL sibling, instead.
13810  */
13811 void
clutter_actor_lower_bottom(ClutterActor * self)13812 clutter_actor_lower_bottom (ClutterActor *self)
13813 {
13814   clutter_actor_lower (self, NULL);
13815 }
13816 
13817 /*
13818  * Event handling
13819  */
13820 
13821 /**
13822  * clutter_actor_event:
13823  * @actor: a #ClutterActor
13824  * @event: a #ClutterEvent
13825  * @capture: %TRUE if event in in capture phase, %FALSE otherwise.
13826  *
13827  * This function is used to emit an event on the main stage.
13828  * You should rarely need to use this function, except for
13829  * synthetising events.
13830  *
13831  * Return value: the return value from the signal emission: %TRUE
13832  *   if the actor handled the event, or %FALSE if the event was
13833  *   not handled
13834  *
13835  * Since: 0.6
13836  */
13837 gboolean
clutter_actor_event(ClutterActor * actor,const ClutterEvent * event,gboolean capture)13838 clutter_actor_event (ClutterActor       *actor,
13839                      const ClutterEvent *event,
13840 		     gboolean            capture)
13841 {
13842   gboolean retval = FALSE;
13843   gint signal_num = -1;
13844 
13845   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
13846   g_return_val_if_fail (event != NULL, FALSE);
13847 
13848   g_object_ref (actor);
13849 
13850   if (capture)
13851     {
13852       g_signal_emit (actor, actor_signals[CAPTURED_EVENT], 0,
13853 		     event,
13854                      &retval);
13855       goto out;
13856     }
13857 
13858   g_signal_emit (actor, actor_signals[EVENT], 0, event, &retval);
13859 
13860   if (!retval)
13861     {
13862       switch (event->type)
13863 	{
13864 	case CLUTTER_NOTHING:
13865 	  break;
13866 	case CLUTTER_BUTTON_PRESS:
13867 	  signal_num = BUTTON_PRESS_EVENT;
13868 	  break;
13869 	case CLUTTER_BUTTON_RELEASE:
13870 	  signal_num = BUTTON_RELEASE_EVENT;
13871 	  break;
13872 	case CLUTTER_SCROLL:
13873 	  signal_num = SCROLL_EVENT;
13874 	  break;
13875 	case CLUTTER_KEY_PRESS:
13876 	  signal_num = KEY_PRESS_EVENT;
13877 	  break;
13878 	case CLUTTER_KEY_RELEASE:
13879 	  signal_num = KEY_RELEASE_EVENT;
13880 	  break;
13881 	case CLUTTER_MOTION:
13882 	  signal_num = MOTION_EVENT;
13883 	  break;
13884 	case CLUTTER_ENTER:
13885 	  signal_num = ENTER_EVENT;
13886 	  break;
13887 	case CLUTTER_LEAVE:
13888 	  signal_num = LEAVE_EVENT;
13889 	  break;
13890         case CLUTTER_TOUCH_BEGIN:
13891         case CLUTTER_TOUCH_END:
13892         case CLUTTER_TOUCH_UPDATE:
13893         case CLUTTER_TOUCH_CANCEL:
13894           signal_num = TOUCH_EVENT;
13895           break;
13896 	case CLUTTER_DELETE:
13897 	case CLUTTER_DESTROY_NOTIFY:
13898 	case CLUTTER_CLIENT_MESSAGE:
13899 	default:
13900 	  signal_num = -1;
13901 	  break;
13902 	}
13903 
13904       if (signal_num != -1)
13905 	g_signal_emit (actor, actor_signals[signal_num], 0,
13906 		       event, &retval);
13907     }
13908 
13909 out:
13910   g_object_unref (actor);
13911 
13912   return retval;
13913 }
13914 
13915 /**
13916  * clutter_actor_set_reactive:
13917  * @actor: a #ClutterActor
13918  * @reactive: whether the actor should be reactive to events
13919  *
13920  * Sets @actor as reactive. Reactive actors will receive events.
13921  *
13922  * Since: 0.6
13923  */
13924 void
clutter_actor_set_reactive(ClutterActor * actor,gboolean reactive)13925 clutter_actor_set_reactive (ClutterActor *actor,
13926                             gboolean      reactive)
13927 {
13928   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
13929 
13930   if (reactive == CLUTTER_ACTOR_IS_REACTIVE (actor))
13931     return;
13932 
13933   if (reactive)
13934     CLUTTER_ACTOR_SET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE);
13935   else
13936     CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE);
13937 
13938   g_object_notify_by_pspec (G_OBJECT (actor), obj_props[PROP_REACTIVE]);
13939 }
13940 
13941 /**
13942  * clutter_actor_get_reactive:
13943  * @actor: a #ClutterActor
13944  *
13945  * Checks whether @actor is marked as reactive.
13946  *
13947  * Return value: %TRUE if the actor is reactive
13948  *
13949  * Since: 0.6
13950  */
13951 gboolean
clutter_actor_get_reactive(ClutterActor * actor)13952 clutter_actor_get_reactive (ClutterActor *actor)
13953 {
13954   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
13955 
13956   return CLUTTER_ACTOR_IS_REACTIVE (actor) ? TRUE : FALSE;
13957 }
13958 
13959 /**
13960  * clutter_actor_get_anchor_point:
13961  * @self: a #ClutterActor
13962  * @anchor_x: (out): return location for the X coordinate of the anchor point
13963  * @anchor_y: (out): return location for the Y coordinate of the anchor point
13964  *
13965  * Gets the current anchor point of the @actor in pixels.
13966  *
13967  * Since: 0.6
13968  *
13969  * Deprecated: 1.12: Use #ClutterActor:pivot-point instead
13970  */
13971 void
clutter_actor_get_anchor_point(ClutterActor * self,gfloat * anchor_x,gfloat * anchor_y)13972 clutter_actor_get_anchor_point (ClutterActor *self,
13973 				gfloat       *anchor_x,
13974                                 gfloat       *anchor_y)
13975 {
13976   const ClutterTransformInfo *info;
13977 
13978   g_return_if_fail (CLUTTER_IS_ACTOR (self));
13979 
13980   info = _clutter_actor_get_transform_info_or_defaults (self);
13981   clutter_anchor_coord_get_units (self, &info->anchor,
13982                                   anchor_x,
13983                                   anchor_y,
13984                                   NULL);
13985 }
13986 
13987 /**
13988  * clutter_actor_set_anchor_point:
13989  * @self: a #ClutterActor
13990  * @anchor_x: X coordinate of the anchor point
13991  * @anchor_y: Y coordinate of the anchor point
13992  *
13993  * Sets an anchor point for @self. The anchor point is a point in the
13994  * coordinate space of an actor to which the actor position within its
13995  * parent is relative; the default is (0, 0), i.e. the top-left corner
13996  * of the actor.
13997  *
13998  * Since: 0.6
13999  *
14000  * Deprecated: 1.12: Use #ClutterActor:pivot-point instead.
14001  */
14002 void
clutter_actor_set_anchor_point(ClutterActor * self,gfloat anchor_x,gfloat anchor_y)14003 clutter_actor_set_anchor_point (ClutterActor *self,
14004                                 gfloat        anchor_x,
14005                                 gfloat        anchor_y)
14006 {
14007   ClutterTransformInfo *info;
14008   ClutterActorPrivate *priv;
14009   gboolean changed = FALSE;
14010   gfloat old_anchor_x, old_anchor_y;
14011   GObject *obj;
14012 
14013   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14014 
14015   obj = G_OBJECT (self);
14016   priv = self->priv;
14017   info = _clutter_actor_get_transform_info (self);
14018 
14019   g_object_freeze_notify (obj);
14020 
14021   clutter_anchor_coord_get_units (self, &info->anchor,
14022                                   &old_anchor_x,
14023                                   &old_anchor_y,
14024                                   NULL);
14025 
14026   if (info->anchor.is_fractional)
14027     g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_GRAVITY]);
14028 
14029   if (old_anchor_x != anchor_x)
14030     {
14031       g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_X]);
14032       changed = TRUE;
14033     }
14034 
14035   if (old_anchor_y != anchor_y)
14036     {
14037       g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_Y]);
14038       changed = TRUE;
14039     }
14040 
14041   clutter_anchor_coord_set_units (&info->anchor, anchor_x, anchor_y, 0);
14042 
14043   if (changed)
14044     {
14045       priv->transform_valid = FALSE;
14046       clutter_actor_queue_redraw (self);
14047     }
14048 
14049   g_object_thaw_notify (obj);
14050 }
14051 
14052 /**
14053  * clutter_actor_get_anchor_point_gravity:
14054  * @self: a #ClutterActor
14055  *
14056  * Retrieves the anchor position expressed as a #ClutterGravity. If
14057  * the anchor point was specified using pixels or units this will
14058  * return %CLUTTER_GRAVITY_NONE.
14059  *
14060  * Return value: the #ClutterGravity used by the anchor point
14061  *
14062  * Since: 1.0
14063  *
14064  * Deprecated: 1.12: Use #ClutterActor:pivot-point instead.
14065  */
14066 ClutterGravity
clutter_actor_get_anchor_point_gravity(ClutterActor * self)14067 clutter_actor_get_anchor_point_gravity (ClutterActor *self)
14068 {
14069   const ClutterTransformInfo *info;
14070 
14071   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE);
14072 
14073   info = _clutter_actor_get_transform_info_or_defaults (self);
14074 
14075   return clutter_anchor_coord_get_gravity (&info->anchor);
14076 }
14077 
14078 /**
14079  * clutter_actor_move_anchor_point:
14080  * @self: a #ClutterActor
14081  * @anchor_x: X coordinate of the anchor point
14082  * @anchor_y: Y coordinate of the anchor point
14083  *
14084  * Sets an anchor point for the actor, and adjusts the actor postion so that
14085  * the relative position of the actor toward its parent remains the same.
14086  *
14087  * Since: 0.6
14088  *
14089  * Deprecated: 1.12: Use #ClutterActor:pivot-point and
14090  * clutter_actor_set_translation() instead.
14091  */
14092 void
clutter_actor_move_anchor_point(ClutterActor * self,gfloat anchor_x,gfloat anchor_y)14093 clutter_actor_move_anchor_point (ClutterActor *self,
14094                                  gfloat        anchor_x,
14095                                  gfloat        anchor_y)
14096 {
14097   gfloat old_anchor_x, old_anchor_y;
14098   const ClutterTransformInfo *info;
14099 
14100   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14101 
14102   info = _clutter_actor_get_transform_info (self);
14103   clutter_anchor_coord_get_units (self, &info->anchor,
14104                                   &old_anchor_x,
14105                                   &old_anchor_y,
14106                                   NULL);
14107 
14108   g_object_freeze_notify (G_OBJECT (self));
14109 
14110   clutter_actor_set_anchor_point (self, anchor_x, anchor_y);
14111 
14112   if (self->priv->position_set)
14113     clutter_actor_move_by (self,
14114                            anchor_x - old_anchor_x,
14115                            anchor_y - old_anchor_y);
14116 
14117   g_object_thaw_notify (G_OBJECT (self));
14118 }
14119 
14120 /**
14121  * clutter_actor_move_anchor_point_from_gravity:
14122  * @self: a #ClutterActor
14123  * @gravity: #ClutterGravity.
14124  *
14125  * Sets an anchor point on the actor based on the given gravity, adjusting the
14126  * actor postion so that its relative position within its parent remains
14127  * unchanged.
14128  *
14129  * Since version 1.0 the anchor point will be stored as a gravity so
14130  * that if the actor changes size then the anchor point will move. For
14131  * example, if you set the anchor point to %CLUTTER_GRAVITY_SOUTH_EAST
14132  * and later double the size of the actor, the anchor point will move
14133  * to the bottom right.
14134  *
14135  * Since: 0.6
14136  *
14137  * Deprecated: 1.12: Use #ClutterActor:pivot-point and
14138  * clutter_actor_set_translation() instead.
14139  */
14140 void
clutter_actor_move_anchor_point_from_gravity(ClutterActor * self,ClutterGravity gravity)14141 clutter_actor_move_anchor_point_from_gravity (ClutterActor   *self,
14142 					      ClutterGravity  gravity)
14143 {
14144   gfloat old_anchor_x, old_anchor_y, new_anchor_x, new_anchor_y;
14145   const ClutterTransformInfo *info;
14146   ClutterActorPrivate *priv;
14147 
14148   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14149 
14150   priv = self->priv;
14151   info = _clutter_actor_get_transform_info (self);
14152 
14153   g_object_freeze_notify (G_OBJECT (self));
14154 
14155   clutter_anchor_coord_get_units (self, &info->anchor,
14156                                   &old_anchor_x,
14157                                   &old_anchor_y,
14158                                   NULL);
14159   clutter_actor_set_anchor_point_from_gravity (self, gravity);
14160   clutter_anchor_coord_get_units (self, &info->anchor,
14161                                   &new_anchor_x,
14162                                   &new_anchor_y,
14163                                   NULL);
14164 
14165   if (priv->position_set)
14166     clutter_actor_move_by (self,
14167                            new_anchor_x - old_anchor_x,
14168                            new_anchor_y - old_anchor_y);
14169 
14170   g_object_thaw_notify (G_OBJECT (self));
14171 }
14172 
14173 /**
14174  * clutter_actor_set_anchor_point_from_gravity:
14175  * @self: a #ClutterActor
14176  * @gravity: #ClutterGravity.
14177  *
14178  * Sets an anchor point on the actor, based on the given gravity (this is a
14179  * convenience function wrapping clutter_actor_set_anchor_point()).
14180  *
14181  * Since version 1.0 the anchor point will be stored as a gravity so
14182  * that if the actor changes size then the anchor point will move. For
14183  * example, if you set the anchor point to %CLUTTER_GRAVITY_SOUTH_EAST
14184  * and later double the size of the actor, the anchor point will move
14185  * to the bottom right.
14186  *
14187  * Since: 0.6
14188  *
14189  * Deprecated: 1.12: Use #ClutterActor:pivot-point and
14190  * clutter_actor_set_translation() instead. E.g. For %CLUTTER_GRAVITY_CENTER set
14191  * pivot_point to (0.5,0.5) and the translation to (width/2,height/2).
14192  */
14193 void
clutter_actor_set_anchor_point_from_gravity(ClutterActor * self,ClutterGravity gravity)14194 clutter_actor_set_anchor_point_from_gravity (ClutterActor   *self,
14195 					     ClutterGravity  gravity)
14196 {
14197   g_return_if_fail (CLUTTER_IS_ACTOR (self));
14198 
14199   if (gravity == CLUTTER_GRAVITY_NONE)
14200     clutter_actor_set_anchor_point (self, 0, 0);
14201   else
14202     {
14203       GObject *obj = G_OBJECT (self);
14204       ClutterTransformInfo *info;
14205 
14206       g_object_freeze_notify (obj);
14207 
14208       info = _clutter_actor_get_transform_info (self);
14209       clutter_anchor_coord_set_gravity (&info->anchor, gravity);
14210 
14211       g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_GRAVITY]);
14212       g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_X]);
14213       g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_Y]);
14214 
14215       self->priv->transform_valid = FALSE;
14216 
14217       clutter_actor_queue_redraw (self);
14218 
14219       g_object_thaw_notify (obj);
14220     }
14221 }
14222 
14223 static void
clutter_actor_store_content_box(ClutterActor * self,const ClutterActorBox * box)14224 clutter_actor_store_content_box (ClutterActor *self,
14225                                  const ClutterActorBox *box)
14226 {
14227   if (box != NULL)
14228     {
14229       self->priv->content_box = *box;
14230       self->priv->content_box_valid = TRUE;
14231     }
14232   else
14233     self->priv->content_box_valid = FALSE;
14234 
14235   clutter_actor_queue_redraw (self);
14236 
14237   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
14238 }
14239 
14240 static void
clutter_container_iface_init(ClutterContainerIface * iface)14241 clutter_container_iface_init (ClutterContainerIface *iface)
14242 {
14243   /* we don't override anything, as ClutterContainer already has a default
14244    * implementation that we can use, and which calls into our own API.
14245    */
14246 }
14247 
14248 typedef enum
14249 {
14250   PARSE_X,
14251   PARSE_Y,
14252   PARSE_WIDTH,
14253   PARSE_HEIGHT,
14254   PARSE_ANCHOR_X,
14255   PARSE_ANCHOR_Y
14256 } ParseDimension;
14257 
14258 static gfloat
parse_units(ClutterActor * self,ParseDimension dimension,JsonNode * node)14259 parse_units (ClutterActor   *self,
14260              ParseDimension  dimension,
14261              JsonNode       *node)
14262 {
14263   GValue value = G_VALUE_INIT;
14264   gfloat retval = 0;
14265 
14266   if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE)
14267     return 0;
14268 
14269   json_node_get_value (node, &value);
14270 
14271   if (G_VALUE_HOLDS (&value, G_TYPE_INT64))
14272     {
14273       retval = (gfloat) g_value_get_int64 (&value);
14274     }
14275   else if (G_VALUE_HOLDS (&value, G_TYPE_DOUBLE))
14276     {
14277       retval = g_value_get_double (&value);
14278     }
14279   else if (G_VALUE_HOLDS (&value, G_TYPE_STRING))
14280     {
14281       ClutterUnits units;
14282       gboolean res;
14283 
14284       res = clutter_units_from_string (&units, g_value_get_string (&value));
14285       if (res)
14286         retval = clutter_units_to_pixels (&units);
14287       else
14288         {
14289           g_warning ("Invalid value '%s': integers, strings or floating point "
14290                      "values can be used for the x, y, width and height "
14291                      "properties. Valid modifiers for strings are 'px', 'mm', "
14292                      "'pt' and 'em'.",
14293                      g_value_get_string (&value));
14294           retval = 0;
14295         }
14296     }
14297   else
14298     {
14299       g_warning ("Invalid value of type '%s': integers, strings of floating "
14300                  "point values can be used for the x, y, width, height "
14301                  "anchor-x and anchor-y properties.",
14302                  g_type_name (G_VALUE_TYPE (&value)));
14303     }
14304 
14305   g_value_unset (&value);
14306 
14307   return retval;
14308 }
14309 
14310 typedef struct {
14311   ClutterRotateAxis axis;
14312 
14313   gdouble angle;
14314 
14315   gfloat center_x;
14316   gfloat center_y;
14317   gfloat center_z;
14318 } RotationInfo;
14319 
14320 static inline gboolean
parse_rotation_array(ClutterActor * actor,JsonArray * array,RotationInfo * info)14321 parse_rotation_array (ClutterActor *actor,
14322                       JsonArray    *array,
14323                       RotationInfo *info)
14324 {
14325   JsonNode *element;
14326 
14327   if (json_array_get_length (array) != 2)
14328     return FALSE;
14329 
14330   /* angle */
14331   element = json_array_get_element (array, 0);
14332   if (JSON_NODE_TYPE (element) == JSON_NODE_VALUE)
14333     info->angle = json_node_get_double (element);
14334   else
14335     return FALSE;
14336 
14337   /* center */
14338   element = json_array_get_element (array, 1);
14339   if (JSON_NODE_TYPE (element) == JSON_NODE_ARRAY)
14340     {
14341       JsonArray *center = json_node_get_array (element);
14342 
14343       if (json_array_get_length (center) != 2)
14344         return FALSE;
14345 
14346       switch (info->axis)
14347         {
14348         case CLUTTER_X_AXIS:
14349           info->center_y = parse_units (actor, PARSE_Y,
14350                                         json_array_get_element (center, 0));
14351           info->center_z = parse_units (actor, PARSE_Y,
14352                                         json_array_get_element (center, 1));
14353           return TRUE;
14354 
14355         case CLUTTER_Y_AXIS:
14356           info->center_x = parse_units (actor, PARSE_X,
14357                                         json_array_get_element (center, 0));
14358           info->center_z = parse_units (actor, PARSE_X,
14359                                         json_array_get_element (center, 1));
14360           return TRUE;
14361 
14362         case CLUTTER_Z_AXIS:
14363           info->center_x = parse_units (actor, PARSE_X,
14364                                         json_array_get_element (center, 0));
14365           info->center_y = parse_units (actor, PARSE_Y,
14366                                         json_array_get_element (center, 1));
14367           return TRUE;
14368         }
14369     }
14370 
14371   return FALSE;
14372 }
14373 
14374 static gboolean
parse_rotation(ClutterActor * actor,JsonNode * node,RotationInfo * info)14375 parse_rotation (ClutterActor *actor,
14376                 JsonNode     *node,
14377                 RotationInfo *info)
14378 {
14379   JsonArray *array;
14380   guint len, i;
14381   gboolean retval = FALSE;
14382 
14383   if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
14384     {
14385       g_warning ("Invalid node of type '%s' found, expecting an array",
14386                  json_node_type_name (node));
14387       return FALSE;
14388     }
14389 
14390   array = json_node_get_array (node);
14391   len = json_array_get_length (array);
14392 
14393   for (i = 0; i < len; i++)
14394     {
14395       JsonNode *element = json_array_get_element (array, i);
14396       JsonObject *object;
14397       JsonNode *member;
14398 
14399       if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT)
14400         {
14401           g_warning ("Invalid node of type '%s' found, expecting an object",
14402                      json_node_type_name (element));
14403           return FALSE;
14404         }
14405 
14406       object = json_node_get_object (element);
14407 
14408       if (json_object_has_member (object, "x-axis"))
14409         {
14410           member = json_object_get_member (object, "x-axis");
14411 
14412           info->axis = CLUTTER_X_AXIS;
14413 
14414           if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
14415             {
14416               info->angle = json_node_get_double (member);
14417               retval = TRUE;
14418             }
14419           else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
14420             retval = parse_rotation_array (actor,
14421                                            json_node_get_array (member),
14422                                            info);
14423           else
14424             retval = FALSE;
14425         }
14426       else if (json_object_has_member (object, "y-axis"))
14427         {
14428           member = json_object_get_member (object, "y-axis");
14429 
14430           info->axis = CLUTTER_Y_AXIS;
14431 
14432           if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
14433             {
14434               info->angle = json_node_get_double (member);
14435               retval = TRUE;
14436             }
14437           else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
14438             retval = parse_rotation_array (actor,
14439                                            json_node_get_array (member),
14440                                            info);
14441           else
14442             retval = FALSE;
14443         }
14444       else if (json_object_has_member (object, "z-axis"))
14445         {
14446           member = json_object_get_member (object, "z-axis");
14447 
14448           info->axis = CLUTTER_Z_AXIS;
14449 
14450           if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE)
14451             {
14452               info->angle = json_node_get_double (member);
14453               retval = TRUE;
14454             }
14455           else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY)
14456             retval = parse_rotation_array (actor,
14457                                            json_node_get_array (member),
14458                                            info);
14459           else
14460             retval = FALSE;
14461         }
14462     }
14463 
14464   return retval;
14465 }
14466 
14467 static GSList *
parse_actor_metas(ClutterScript * script,ClutterActor * actor,JsonNode * node)14468 parse_actor_metas (ClutterScript *script,
14469                    ClutterActor  *actor,
14470                    JsonNode      *node)
14471 {
14472   GList *elements, *l;
14473   GSList *retval = NULL;
14474 
14475   if (!JSON_NODE_HOLDS_ARRAY (node))
14476     return NULL;
14477 
14478   elements = json_array_get_elements (json_node_get_array (node));
14479 
14480   for (l = elements; l != NULL; l = l->next)
14481     {
14482       JsonNode *element = l->data;
14483       const gchar *id_ = _clutter_script_get_id_from_node (element);
14484       GObject *meta;
14485 
14486       if (id_ == NULL || *id_ == '\0')
14487         continue;
14488 
14489       meta = clutter_script_get_object (script, id_);
14490       if (meta == NULL)
14491         continue;
14492 
14493       retval = g_slist_prepend (retval, meta);
14494     }
14495 
14496   g_list_free (elements);
14497 
14498   return g_slist_reverse (retval);
14499 }
14500 
14501 static GSList *
parse_behaviours(ClutterScript * script,ClutterActor * actor,JsonNode * node)14502 parse_behaviours (ClutterScript *script,
14503                   ClutterActor  *actor,
14504                   JsonNode      *node)
14505 {
14506   GList *elements, *l;
14507   GSList *retval = NULL;
14508 
14509   if (!JSON_NODE_HOLDS_ARRAY (node))
14510     return NULL;
14511 
14512   elements = json_array_get_elements (json_node_get_array (node));
14513 
14514   for (l = elements; l != NULL; l = l->next)
14515     {
14516       JsonNode *element = l->data;
14517       const gchar *id_ = _clutter_script_get_id_from_node (element);
14518       GObject *behaviour;
14519 
14520       if (id_ == NULL || *id_ == '\0')
14521         continue;
14522 
14523       behaviour = clutter_script_get_object (script, id_);
14524       if (behaviour == NULL)
14525         continue;
14526 
14527       retval = g_slist_prepend (retval, behaviour);
14528     }
14529 
14530   g_list_free (elements);
14531 
14532   return g_slist_reverse (retval);
14533 }
14534 
14535 static ClutterMargin *
parse_margin(ClutterActor * self,JsonNode * node)14536 parse_margin (ClutterActor *self,
14537               JsonNode     *node)
14538 {
14539   ClutterMargin *margin;
14540   JsonArray *array;
14541 
14542   if (!JSON_NODE_HOLDS_ARRAY (node))
14543     {
14544       g_warning ("The margin property must be an array of 1 to 4 elements");
14545       return NULL;
14546     }
14547 
14548   margin = clutter_margin_new ();
14549   array = json_node_get_array (node);
14550   switch (json_array_get_length (array))
14551     {
14552     case 1:
14553       margin->top = margin->right = margin->bottom = margin->left =
14554         parse_units (self, 0, json_array_get_element (array, 0));
14555       break;
14556 
14557     case 2:
14558       margin->top = margin->bottom =
14559         parse_units (self, 0, json_array_get_element (array, 0));
14560       margin->right = margin->left =
14561         parse_units (self, 0, json_array_get_element (array, 1));
14562       break;
14563 
14564     case 3:
14565       margin->top =
14566         parse_units (self, 0, json_array_get_element (array, 0));
14567       margin->right = margin->left =
14568         parse_units (self, 0, json_array_get_element (array, 1));
14569       margin->bottom =
14570         parse_units (self, 0, json_array_get_element (array, 2));
14571       break;
14572 
14573     case 4:
14574       margin->top =
14575         parse_units (self, 0, json_array_get_element (array, 0));
14576       margin->right =
14577         parse_units (self, 0, json_array_get_element (array, 1));
14578       margin->bottom =
14579         parse_units (self, 0, json_array_get_element (array, 2));
14580       margin->left =
14581         parse_units (self, 0, json_array_get_element (array, 3));
14582       break;
14583 
14584     default:
14585       g_warning ("The margin property must be an array of 1 to 4 elements");
14586       clutter_margin_free (margin);
14587       return NULL;
14588     }
14589   return margin;
14590 }
14591 
14592 static gboolean
clutter_actor_parse_custom_node(ClutterScriptable * scriptable,ClutterScript * script,GValue * value,const gchar * name,JsonNode * node)14593 clutter_actor_parse_custom_node (ClutterScriptable *scriptable,
14594                                  ClutterScript     *script,
14595                                  GValue            *value,
14596                                  const gchar       *name,
14597                                  JsonNode          *node)
14598 {
14599   ClutterActor *actor = CLUTTER_ACTOR (scriptable);
14600   gboolean retval = FALSE;
14601 
14602   if ((name[0] == 'x' && name[1] == '\0') ||
14603       (name[0] == 'y' && name[1] == '\0') ||
14604       (strcmp (name, "width") == 0) ||
14605       (strcmp (name, "height") == 0) ||
14606       (strcmp (name, "anchor_x") == 0) ||
14607       (strcmp (name, "anchor_y") == 0))
14608     {
14609       ParseDimension dimension;
14610       gfloat units;
14611 
14612       if (name[0] == 'x')
14613         dimension = PARSE_X;
14614       else if (name[0] == 'y')
14615         dimension = PARSE_Y;
14616       else if (name[0] == 'w')
14617         dimension = PARSE_WIDTH;
14618       else if (name[0] == 'h')
14619         dimension = PARSE_HEIGHT;
14620       else if (name[0] == 'a' && name[7] == 'x')
14621         dimension = PARSE_ANCHOR_X;
14622       else if (name[0] == 'a' && name[7] == 'y')
14623         dimension = PARSE_ANCHOR_Y;
14624       else
14625         return FALSE;
14626 
14627       units = parse_units (actor, dimension, node);
14628 
14629       /* convert back to pixels: all properties are pixel-based */
14630       g_value_init (value, G_TYPE_FLOAT);
14631       g_value_set_float (value, units);
14632 
14633       retval = TRUE;
14634     }
14635   else if (strcmp (name, "rotation") == 0)
14636     {
14637       RotationInfo *info;
14638 
14639       info = g_slice_new0 (RotationInfo);
14640       retval = parse_rotation (actor, node, info);
14641 
14642       if (retval)
14643         {
14644           g_value_init (value, G_TYPE_POINTER);
14645           g_value_set_pointer (value, info);
14646         }
14647       else
14648         g_slice_free (RotationInfo, info);
14649     }
14650   else if (strcmp (name, "behaviours") == 0)
14651     {
14652       GSList *l;
14653 
14654 #ifdef CLUTTER_ENABLE_DEBUG
14655       if (G_UNLIKELY (_clutter_diagnostic_enabled ()))
14656         _clutter_diagnostic_message ("The 'behaviours' key is deprecated "
14657                                      "and it should not be used in newly "
14658                                      "written ClutterScript definitions.");
14659 #endif
14660 
14661       l = parse_behaviours (script, actor, node);
14662 
14663       g_value_init (value, G_TYPE_POINTER);
14664       g_value_set_pointer (value, l);
14665 
14666       retval = TRUE;
14667     }
14668   else if (strcmp (name, "actions") == 0 ||
14669            strcmp (name, "constraints") == 0 ||
14670            strcmp (name, "effects") == 0)
14671     {
14672       GSList *l;
14673 
14674       l = parse_actor_metas (script, actor, node);
14675 
14676       g_value_init (value, G_TYPE_POINTER);
14677       g_value_set_pointer (value, l);
14678 
14679       retval = TRUE;
14680     }
14681   else if (strcmp (name, "margin") == 0)
14682     {
14683       ClutterMargin *margin = parse_margin (actor, node);
14684 
14685       if (margin)
14686         {
14687           g_value_init (value, CLUTTER_TYPE_MARGIN);
14688           g_value_set_boxed (value, margin);
14689           retval = TRUE;
14690         }
14691     }
14692 
14693   return retval;
14694 }
14695 
14696 static void
clutter_actor_set_custom_property(ClutterScriptable * scriptable,ClutterScript * script,const gchar * name,const GValue * value)14697 clutter_actor_set_custom_property (ClutterScriptable *scriptable,
14698                                    ClutterScript     *script,
14699                                    const gchar       *name,
14700                                    const GValue      *value)
14701 {
14702   ClutterActor *actor = CLUTTER_ACTOR (scriptable);
14703 
14704 #ifdef CLUTTER_ENABLE_DEBUG
14705   if (G_UNLIKELY (CLUTTER_HAS_DEBUG (SCRIPT)))
14706     {
14707       gchar *tmp = g_strdup_value_contents (value);
14708 
14709       CLUTTER_NOTE (SCRIPT,
14710                     "in ClutterActor::set_custom_property('%s') = %s",
14711                     name,
14712                     tmp);
14713 
14714       g_free (tmp);
14715     }
14716 #endif /* CLUTTER_ENABLE_DEBUG */
14717 
14718   if (strcmp (name, "rotation") == 0)
14719     {
14720       RotationInfo *info;
14721 
14722       if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
14723         return;
14724 
14725       info = g_value_get_pointer (value);
14726 
14727       clutter_actor_set_rotation (actor,
14728                                   info->axis, info->angle,
14729                                   info->center_x,
14730                                   info->center_y,
14731                                   info->center_z);
14732 
14733       g_slice_free (RotationInfo, info);
14734 
14735       return;
14736     }
14737 
14738   if (strcmp (name, "behaviours") == 0)
14739     {
14740       GSList *behaviours, *l;
14741 
14742       if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
14743         return;
14744 
14745       behaviours = g_value_get_pointer (value);
14746       for (l = behaviours; l != NULL; l = l->next)
14747         {
14748           ClutterBehaviour *behaviour = l->data;
14749 
14750           clutter_behaviour_apply (behaviour, actor);
14751         }
14752 
14753       g_slist_free (behaviours);
14754 
14755       return;
14756     }
14757 
14758   if (strcmp (name, "actions") == 0 ||
14759       strcmp (name, "constraints") == 0 ||
14760       strcmp (name, "effects") == 0)
14761     {
14762       GSList *metas, *l;
14763 
14764       if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
14765         return;
14766 
14767       metas = g_value_get_pointer (value);
14768       for (l = metas; l != NULL; l = l->next)
14769         {
14770           if (name[0] == 'a')
14771             clutter_actor_add_action (actor, l->data);
14772 
14773           if (name[0] == 'c')
14774             clutter_actor_add_constraint (actor, l->data);
14775 
14776           if (name[0] == 'e')
14777             clutter_actor_add_effect (actor, l->data);
14778         }
14779 
14780       g_slist_free (metas);
14781 
14782       return;
14783     }
14784   if (strcmp (name, "margin") == 0)
14785     {
14786       clutter_actor_set_margin (actor, g_value_get_boxed (value));
14787       return;
14788     }
14789 
14790   g_object_set_property (G_OBJECT (scriptable), name, value);
14791 }
14792 
14793 static void
clutter_scriptable_iface_init(ClutterScriptableIface * iface)14794 clutter_scriptable_iface_init (ClutterScriptableIface *iface)
14795 {
14796   iface->parse_custom_node = clutter_actor_parse_custom_node;
14797   iface->set_custom_property = clutter_actor_set_custom_property;
14798 }
14799 
14800 static ClutterActorMeta *
get_meta_from_animation_property(ClutterActor * actor,const gchar * name,gchar ** name_p)14801 get_meta_from_animation_property (ClutterActor  *actor,
14802                                   const gchar   *name,
14803                                   gchar        **name_p)
14804 {
14805   ClutterActorPrivate *priv = actor->priv;
14806   ClutterActorMeta *meta = NULL;
14807   gchar **tokens;
14808 
14809   /* if this is not a special property, fall through */
14810   if (name[0] != '@')
14811     return NULL;
14812 
14813   /* detect the properties named using the following spec:
14814    *
14815    *   @<section>.<meta-name>.<property-name>
14816    *
14817    * where <section> can be one of the following:
14818    *
14819    *   - actions
14820    *   - constraints
14821    *   - effects
14822    *
14823    * and <meta-name> is the name set on a specific ActorMeta
14824    */
14825 
14826   tokens = g_strsplit (name + 1, ".", -1);
14827   if (tokens == NULL || g_strv_length (tokens) != 3)
14828     {
14829       CLUTTER_NOTE (ANIMATION, "Invalid property name '%s'",
14830                     name + 1);
14831       g_strfreev (tokens);
14832       return NULL;
14833     }
14834 
14835   if (strcmp (tokens[0], "actions") == 0)
14836     meta = _clutter_meta_group_get_meta (priv->actions, tokens[1]);
14837 
14838   if (strcmp (tokens[0], "constraints") == 0)
14839     meta = _clutter_meta_group_get_meta (priv->constraints, tokens[1]);
14840 
14841   if (strcmp (tokens[0], "effects") == 0)
14842     meta = _clutter_meta_group_get_meta (priv->effects, tokens[1]);
14843 
14844   if (name_p != NULL)
14845     *name_p = g_strdup (tokens[2]);
14846 
14847   CLUTTER_NOTE (ANIMATION,
14848                 "Looking for property '%s' of object '%s' in section '%s'",
14849                 tokens[2],
14850                 tokens[1],
14851                 tokens[0]);
14852 
14853   g_strfreev (tokens);
14854 
14855   return meta;
14856 }
14857 
14858 static GParamSpec *
clutter_actor_find_property(ClutterAnimatable * animatable,const gchar * property_name)14859 clutter_actor_find_property (ClutterAnimatable *animatable,
14860                              const gchar       *property_name)
14861 {
14862   ClutterActorMeta *meta = NULL;
14863   GObjectClass *klass = NULL;
14864   GParamSpec *pspec = NULL;
14865   gchar *p_name = NULL;
14866 
14867   meta = get_meta_from_animation_property (CLUTTER_ACTOR (animatable),
14868                                            property_name,
14869                                            &p_name);
14870 
14871   if (meta != NULL)
14872     {
14873       klass = G_OBJECT_GET_CLASS (meta);
14874 
14875       pspec = g_object_class_find_property (klass, p_name);
14876     }
14877   else
14878     {
14879       klass = G_OBJECT_GET_CLASS (animatable);
14880 
14881       pspec = g_object_class_find_property (klass, property_name);
14882     }
14883 
14884   g_free (p_name);
14885 
14886   return pspec;
14887 }
14888 
14889 static void
clutter_actor_get_initial_state(ClutterAnimatable * animatable,const gchar * property_name,GValue * initial)14890 clutter_actor_get_initial_state (ClutterAnimatable *animatable,
14891                                  const gchar       *property_name,
14892                                  GValue            *initial)
14893 {
14894   ClutterActorMeta *meta = NULL;
14895   gchar *p_name = NULL;
14896 
14897   meta = get_meta_from_animation_property (CLUTTER_ACTOR (animatable),
14898                                            property_name,
14899                                            &p_name);
14900 
14901   if (meta != NULL)
14902     g_object_get_property (G_OBJECT (meta), p_name, initial);
14903   else
14904     g_object_get_property (G_OBJECT (animatable), property_name, initial);
14905 
14906   g_free (p_name);
14907 }
14908 
14909 /*
14910  * clutter_actor_set_animatable_property:
14911  * @actor: a #ClutterActor
14912  * @prop_id: the paramspec id
14913  * @value: the value to set
14914  * @pspec: the paramspec
14915  *
14916  * Sets values of animatable properties.
14917  *
14918  * This is a variant of clutter_actor_set_property() that gets called
14919  * by the #ClutterAnimatable implementation of #ClutterActor for the
14920  * properties with the %CLUTTER_PARAM_ANIMATABLE flag set on their
14921  * #GParamSpec.
14922  *
14923  * Unlike the implementation of #GObjectClass.set_property(), this
14924  * function will not update the interval if a transition involving an
14925  * animatable property is in progress - this avoids cycles with the
14926  * transition API calling the public API.
14927  */
14928 static void
clutter_actor_set_animatable_property(ClutterActor * actor,guint prop_id,const GValue * value,GParamSpec * pspec)14929 clutter_actor_set_animatable_property (ClutterActor *actor,
14930                                        guint         prop_id,
14931                                        const GValue *value,
14932                                        GParamSpec   *pspec)
14933 {
14934   GObject *obj = G_OBJECT (actor);
14935 
14936   g_object_freeze_notify (obj);
14937 
14938   switch (prop_id)
14939     {
14940     case PROP_X:
14941       clutter_actor_set_x_internal (actor, g_value_get_float (value));
14942       break;
14943 
14944     case PROP_Y:
14945       clutter_actor_set_y_internal (actor, g_value_get_float (value));
14946       break;
14947 
14948     case PROP_POSITION:
14949       clutter_actor_set_position_internal (actor, g_value_get_boxed (value));
14950       break;
14951 
14952     case PROP_WIDTH:
14953       clutter_actor_set_width_internal (actor, g_value_get_float (value));
14954       break;
14955 
14956     case PROP_HEIGHT:
14957       clutter_actor_set_height_internal (actor, g_value_get_float (value));
14958       break;
14959 
14960     case PROP_SIZE:
14961       clutter_actor_set_size_internal (actor, g_value_get_boxed (value));
14962       break;
14963 
14964     case PROP_ALLOCATION:
14965       clutter_actor_allocate_internal (actor,
14966                                        g_value_get_boxed (value),
14967                                        actor->priv->allocation_flags);
14968       clutter_actor_queue_redraw (actor);
14969       break;
14970 
14971     case PROP_DEPTH:
14972       clutter_actor_set_depth_internal (actor, g_value_get_float (value));
14973       break;
14974 
14975     case PROP_Z_POSITION:
14976       clutter_actor_set_z_position_internal (actor, g_value_get_float (value));
14977       break;
14978 
14979     case PROP_OPACITY:
14980       clutter_actor_set_opacity_internal (actor, g_value_get_uint (value));
14981       break;
14982 
14983     case PROP_BACKGROUND_COLOR:
14984       clutter_actor_set_background_color_internal (actor, clutter_value_get_color (value));
14985       break;
14986 
14987     case PROP_PIVOT_POINT:
14988       clutter_actor_set_pivot_point_internal (actor, g_value_get_boxed (value));
14989       break;
14990 
14991     case PROP_PIVOT_POINT_Z:
14992       clutter_actor_set_pivot_point_z_internal (actor, g_value_get_float (value));
14993       break;
14994 
14995     case PROP_TRANSLATION_X:
14996     case PROP_TRANSLATION_Y:
14997     case PROP_TRANSLATION_Z:
14998       clutter_actor_set_translation_internal (actor,
14999                                               g_value_get_float (value),
15000                                               pspec);
15001       break;
15002 
15003     case PROP_SCALE_X:
15004     case PROP_SCALE_Y:
15005     case PROP_SCALE_Z:
15006       clutter_actor_set_scale_factor_internal (actor,
15007                                                g_value_get_double (value),
15008                                                pspec);
15009       break;
15010 
15011     case PROP_ROTATION_ANGLE_X:
15012     case PROP_ROTATION_ANGLE_Y:
15013     case PROP_ROTATION_ANGLE_Z:
15014       clutter_actor_set_rotation_angle_internal (actor,
15015                                                  g_value_get_double (value),
15016                                                  pspec);
15017       break;
15018 
15019     case PROP_CONTENT_BOX:
15020       clutter_actor_store_content_box (actor, g_value_get_boxed (value));
15021       break;
15022 
15023     case PROP_MARGIN_TOP:
15024     case PROP_MARGIN_BOTTOM:
15025     case PROP_MARGIN_LEFT:
15026     case PROP_MARGIN_RIGHT:
15027       clutter_actor_set_margin_internal (actor, g_value_get_float (value),
15028                                          pspec);
15029       break;
15030 
15031     case PROP_TRANSFORM:
15032       clutter_actor_set_transform_internal (actor, g_value_get_boxed (value));
15033       break;
15034 
15035     case PROP_CHILD_TRANSFORM:
15036       clutter_actor_set_child_transform_internal (actor, g_value_get_boxed (value));
15037       break;
15038 
15039     default:
15040       g_object_set_property (obj, pspec->name, value);
15041       break;
15042     }
15043 
15044   g_object_thaw_notify (obj);
15045 }
15046 
15047 static void
clutter_actor_set_final_state(ClutterAnimatable * animatable,const gchar * property_name,const GValue * final)15048 clutter_actor_set_final_state (ClutterAnimatable *animatable,
15049                                const gchar       *property_name,
15050                                const GValue      *final)
15051 {
15052   ClutterActor *actor = CLUTTER_ACTOR (animatable);
15053   ClutterActorMeta *meta = NULL;
15054   gchar *p_name = NULL;
15055 
15056   meta = get_meta_from_animation_property (actor,
15057                                            property_name,
15058                                            &p_name);
15059   if (meta != NULL)
15060     g_object_set_property (G_OBJECT (meta), p_name, final);
15061   else
15062     {
15063       GObjectClass *obj_class = G_OBJECT_GET_CLASS (animatable);
15064       GParamSpec *pspec;
15065 
15066       pspec = g_object_class_find_property (obj_class, property_name);
15067 
15068       if (pspec != NULL)
15069         {
15070           if ((pspec->flags & CLUTTER_PARAM_ANIMATABLE) != 0)
15071             {
15072               /* XXX - I'm going to the special hell for this */
15073               clutter_actor_set_animatable_property (actor, pspec->param_id, final, pspec);
15074             }
15075           else
15076             g_object_set_property (G_OBJECT (animatable), pspec->name, final);
15077         }
15078     }
15079 
15080   g_free (p_name);
15081 }
15082 
15083 static void
clutter_animatable_iface_init(ClutterAnimatableIface * iface)15084 clutter_animatable_iface_init (ClutterAnimatableIface *iface)
15085 {
15086   iface->find_property = clutter_actor_find_property;
15087   iface->get_initial_state = clutter_actor_get_initial_state;
15088   iface->set_final_state = clutter_actor_set_final_state;
15089 }
15090 
15091 /**
15092  * clutter_actor_transform_stage_point:
15093  * @self: A #ClutterActor
15094  * @x: (in): x screen coordinate of the point to unproject
15095  * @y: (in): y screen coordinate of the point to unproject
15096  * @x_out: (out): return location for the unprojected x coordinance
15097  * @y_out: (out): return location for the unprojected y coordinance
15098  *
15099  * This function translates screen coordinates (@x, @y) to
15100  * coordinates relative to the actor. For example, it can be used to translate
15101  * screen events from global screen coordinates into actor-local coordinates.
15102  *
15103  * The conversion can fail, notably if the transform stack results in the
15104  * actor being projected on the screen as a mere line.
15105  *
15106  * The conversion should not be expected to be pixel-perfect due to the
15107  * nature of the operation. In general the error grows when the skewing
15108  * of the actor rectangle on screen increases.
15109  *
15110  * This function can be computationally intensive.
15111  *
15112  * This function only works when the allocation is up-to-date, i.e. inside of
15113  * the #ClutterActorClass.paint() implementation
15114  *
15115  * Return value: %TRUE if conversion was successful.
15116  *
15117  * Since: 0.6
15118  */
15119 gboolean
clutter_actor_transform_stage_point(ClutterActor * self,gfloat x,gfloat y,gfloat * x_out,gfloat * y_out)15120 clutter_actor_transform_stage_point (ClutterActor *self,
15121 				     gfloat        x,
15122 				     gfloat        y,
15123 				     gfloat       *x_out,
15124 				     gfloat       *y_out)
15125 {
15126   ClutterVertex v[4];
15127   double ST[3][3];
15128   double RQ[3][3];
15129   int du, dv;
15130   double px, py;
15131   double det;
15132   float xf, yf, wf;
15133   ClutterActorPrivate *priv;
15134 
15135   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15136 
15137   priv = self->priv;
15138 
15139   /* This implementation is based on the quad -> quad projection algorithm
15140    * described by Paul Heckbert in:
15141    *
15142    *   http://www.cs.cmu.edu/~ph/texfund/texfund.pdf
15143    *
15144    * and the sample implementation at:
15145    *
15146    *   http://www.cs.cmu.edu/~ph/src/texfund/
15147    *
15148    * Our texture is a rectangle with origin [0, 0], so we are mapping from
15149    * quad to rectangle only, which significantly simplifies things; the
15150    * function calls have been unrolled, and most of the math is done in fixed
15151    * point.
15152    */
15153   clutter_actor_get_abs_allocation_vertices (self, v);
15154 
15155   /* Keeping these as ints simplifies the multiplication (no significant
15156    * loss of precision here).
15157    */
15158   du = ceilf (priv->allocation.x2 - priv->allocation.x1);
15159   dv = ceilf (priv->allocation.y2 - priv->allocation.y1);
15160 
15161   if (du == 0 || dv == 0)
15162     return FALSE;
15163 
15164 #define DET(a,b,c,d)    (((a) * (d)) - ((b) * (c)))
15165 
15166   /* First, find mapping from unit uv square to xy quadrilateral; this
15167    * equivalent to the pmap_square_quad() functions in the sample
15168    * implementation, which we can simplify, since our target is always
15169    * a rectangle.
15170    */
15171   px = v[0].x - v[1].x + v[3].x - v[2].x;
15172   py = v[0].y - v[1].y + v[3].y - v[2].y;
15173 
15174   if ((int) px == 0 && (int) py == 0)
15175     {
15176       /* affine transform */
15177       RQ[0][0] = v[1].x - v[0].x;
15178       RQ[1][0] = v[3].x - v[1].x;
15179       RQ[2][0] = v[0].x;
15180       RQ[0][1] = v[1].y - v[0].y;
15181       RQ[1][1] = v[3].y - v[1].y;
15182       RQ[2][1] = v[0].y;
15183       RQ[0][2] = 0.0;
15184       RQ[1][2] = 0.0;
15185       RQ[2][2] = 1.0;
15186     }
15187   else
15188     {
15189       /* projective transform */
15190       double dx1, dx2, dy1, dy2;
15191 
15192       dx1 = v[1].x - v[3].x;
15193       dx2 = v[2].x - v[3].x;
15194       dy1 = v[1].y - v[3].y;
15195       dy2 = v[2].y - v[3].y;
15196 
15197       det = DET (dx1, dx2, dy1, dy2);
15198       if (fabs (det) <= DBL_EPSILON)
15199         return FALSE;
15200 
15201       RQ[0][2] = DET (px, dx2, py, dy2) / det;
15202       RQ[1][2] = DET (dx1, px, dy1, py) / det;
15203       RQ[1][2] = DET (dx1, px, dy1, py) / det;
15204       RQ[2][2] = 1.0;
15205       RQ[0][0] = v[1].x - v[0].x + (RQ[0][2] * v[1].x);
15206       RQ[1][0] = v[2].x - v[0].x + (RQ[1][2] * v[2].x);
15207       RQ[2][0] = v[0].x;
15208       RQ[0][1] = v[1].y - v[0].y + (RQ[0][2] * v[1].y);
15209       RQ[1][1] = v[2].y - v[0].y + (RQ[1][2] * v[2].y);
15210       RQ[2][1] = v[0].y;
15211     }
15212 
15213   /*
15214    * Now combine with transform from our rectangle (u0,v0,u1,v1) to unit
15215    * square. Since our rectangle is based at 0,0 we only need to scale.
15216    */
15217   RQ[0][0] /= du;
15218   RQ[1][0] /= dv;
15219   RQ[0][1] /= du;
15220   RQ[1][1] /= dv;
15221   RQ[0][2] /= du;
15222   RQ[1][2] /= dv;
15223 
15224   /*
15225    * Now RQ is transform from uv rectangle to xy quadrilateral; we need an
15226    * inverse of that.
15227    */
15228   ST[0][0] = DET (RQ[1][1], RQ[1][2], RQ[2][1], RQ[2][2]);
15229   ST[1][0] = DET (RQ[1][2], RQ[1][0], RQ[2][2], RQ[2][0]);
15230   ST[2][0] = DET (RQ[1][0], RQ[1][1], RQ[2][0], RQ[2][1]);
15231   ST[0][1] = DET (RQ[2][1], RQ[2][2], RQ[0][1], RQ[0][2]);
15232   ST[1][1] = DET (RQ[2][2], RQ[2][0], RQ[0][2], RQ[0][0]);
15233   ST[2][1] = DET (RQ[2][0], RQ[2][1], RQ[0][0], RQ[0][1]);
15234   ST[0][2] = DET (RQ[0][1], RQ[0][2], RQ[1][1], RQ[1][2]);
15235   ST[1][2] = DET (RQ[0][2], RQ[0][0], RQ[1][2], RQ[1][0]);
15236   ST[2][2] = DET (RQ[0][0], RQ[0][1], RQ[1][0], RQ[1][1]);
15237 
15238   /*
15239    * Check the resulting matrix is OK.
15240    */
15241   det = (RQ[0][0] * ST[0][0])
15242       + (RQ[0][1] * ST[0][1])
15243       + (RQ[0][2] * ST[0][2]);
15244   if (fabs (det) <= DBL_EPSILON)
15245     return FALSE;
15246 
15247   /*
15248    * Now transform our point with the ST matrix; the notional w
15249    * coordinate is 1, hence the last part is simply added.
15250    */
15251   xf = x * ST[0][0] + y * ST[1][0] + ST[2][0];
15252   yf = x * ST[0][1] + y * ST[1][1] + ST[2][1];
15253   wf = x * ST[0][2] + y * ST[1][2] + ST[2][2];
15254 
15255   if (x_out)
15256     *x_out = xf / wf;
15257 
15258   if (y_out)
15259     *y_out = yf / wf;
15260 
15261 #undef DET
15262 
15263   return TRUE;
15264 }
15265 
15266 /**
15267  * clutter_actor_is_rotated:
15268  * @self: a #ClutterActor
15269  *
15270  * Checks whether any rotation is applied to the actor.
15271  *
15272  * Return value: %TRUE if the actor is rotated.
15273  *
15274  * Since: 0.6
15275  */
15276 gboolean
clutter_actor_is_rotated(ClutterActor * self)15277 clutter_actor_is_rotated (ClutterActor *self)
15278 {
15279   const ClutterTransformInfo *info;
15280 
15281   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15282 
15283   info = _clutter_actor_get_transform_info_or_defaults (self);
15284 
15285   if (info->rx_angle || info->ry_angle || info->rz_angle)
15286     return TRUE;
15287 
15288   return FALSE;
15289 }
15290 
15291 /**
15292  * clutter_actor_is_scaled:
15293  * @self: a #ClutterActor
15294  *
15295  * Checks whether the actor is scaled in either dimension.
15296  *
15297  * Return value: %TRUE if the actor is scaled.
15298  *
15299  * Since: 0.6
15300  */
15301 gboolean
clutter_actor_is_scaled(ClutterActor * self)15302 clutter_actor_is_scaled (ClutterActor *self)
15303 {
15304   const ClutterTransformInfo *info;
15305 
15306   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
15307 
15308   info = _clutter_actor_get_transform_info_or_defaults (self);
15309 
15310   if (info->scale_x != 1.0 || info->scale_y != 1.0)
15311     return TRUE;
15312 
15313   return FALSE;
15314 }
15315 
15316 ClutterActor *
_clutter_actor_get_stage_internal(ClutterActor * actor)15317 _clutter_actor_get_stage_internal (ClutterActor *actor)
15318 {
15319   while (actor && !CLUTTER_ACTOR_IS_TOPLEVEL (actor))
15320     actor = actor->priv->parent;
15321 
15322   return actor;
15323 }
15324 
15325 /**
15326  * clutter_actor_get_stage:
15327  * @actor: a #ClutterActor
15328  *
15329  * Retrieves the #ClutterStage where @actor is contained.
15330  *
15331  * Return value: (transfer none) (type Clutter.Stage): the stage
15332  *   containing the actor, or %NULL
15333  *
15334  * Since: 0.8
15335  */
15336 ClutterActor *
clutter_actor_get_stage(ClutterActor * actor)15337 clutter_actor_get_stage (ClutterActor *actor)
15338 {
15339   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
15340 
15341   return _clutter_actor_get_stage_internal (actor);
15342 }
15343 
15344 /**
15345  * clutter_actor_allocate_available_size:
15346  * @self: a #ClutterActor
15347  * @x: the actor's X coordinate
15348  * @y: the actor's Y coordinate
15349  * @available_width: the maximum available width, or -1 to use the
15350  *   actor's natural width
15351  * @available_height: the maximum available height, or -1 to use the
15352  *   actor's natural height
15353  * @flags: flags controlling the allocation
15354  *
15355  * Allocates @self taking into account the #ClutterActor's
15356  * preferred size, but limiting it to the maximum available width
15357  * and height provided.
15358  *
15359  * This function will do the right thing when dealing with the
15360  * actor's request mode.
15361  *
15362  * The implementation of this function is equivalent to:
15363  *
15364  * |[<!-- language="C" -->
15365  *   if (request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
15366  *     {
15367  *       clutter_actor_get_preferred_width (self, available_height,
15368  *                                          &min_width,
15369  *                                          &natural_width);
15370  *       width = CLAMP (natural_width, min_width, available_width);
15371  *
15372  *       clutter_actor_get_preferred_height (self, width,
15373  *                                           &min_height,
15374  *                                           &natural_height);
15375  *       height = CLAMP (natural_height, min_height, available_height);
15376  *     }
15377  *   else if (request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
15378  *     {
15379  *       clutter_actor_get_preferred_height (self, available_width,
15380  *                                           &min_height,
15381  *                                           &natural_height);
15382  *       height = CLAMP (natural_height, min_height, available_height);
15383  *
15384  *       clutter_actor_get_preferred_width (self, height,
15385  *                                          &min_width,
15386  *                                          &natural_width);
15387  *       width = CLAMP (natural_width, min_width, available_width);
15388  *     }
15389  *   else if (request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
15390  *     {
15391  *       clutter_content_get_preferred_size (content, &natural_width, &natural_height);
15392  *
15393  *       width = CLAMP (natural_width, 0, available_width);
15394  *       height = CLAMP (natural_height, 0, available_height);
15395  *     }
15396  *
15397  *   box.x1 = x; box.y1 = y;
15398  *   box.x2 = box.x1 + available_width;
15399  *   box.y2 = box.y1 + available_height;
15400  *   clutter_actor_allocate (self, &box, flags);
15401  * ]|
15402  *
15403  * This function can be used by fluid layout managers to allocate
15404  * an actor's preferred size without making it bigger than the area
15405  * available for the container.
15406  *
15407  * Since: 1.0
15408  */
15409 void
clutter_actor_allocate_available_size(ClutterActor * self,gfloat x,gfloat y,gfloat available_width,gfloat available_height,ClutterAllocationFlags flags)15410 clutter_actor_allocate_available_size (ClutterActor           *self,
15411                                        gfloat                  x,
15412                                        gfloat                  y,
15413                                        gfloat                  available_width,
15414                                        gfloat                  available_height,
15415                                        ClutterAllocationFlags  flags)
15416 {
15417   ClutterActorPrivate *priv;
15418   gfloat width, height;
15419   gfloat min_width, min_height;
15420   gfloat natural_width, natural_height;
15421   ClutterActorBox box;
15422 
15423   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15424 
15425   priv = self->priv;
15426 
15427   width = height = 0.0;
15428 
15429   switch (priv->request_mode)
15430     {
15431     case CLUTTER_REQUEST_HEIGHT_FOR_WIDTH:
15432       clutter_actor_get_preferred_width (self, available_height,
15433                                          &min_width,
15434                                          &natural_width);
15435       width  = CLAMP (natural_width, min_width, available_width);
15436 
15437       clutter_actor_get_preferred_height (self, width,
15438                                           &min_height,
15439                                           &natural_height);
15440       height = CLAMP (natural_height, min_height, available_height);
15441       break;
15442 
15443     case CLUTTER_REQUEST_WIDTH_FOR_HEIGHT:
15444       clutter_actor_get_preferred_height (self, available_width,
15445                                           &min_height,
15446                                           &natural_height);
15447       height = CLAMP (natural_height, min_height, available_height);
15448 
15449       clutter_actor_get_preferred_width (self, height,
15450                                          &min_width,
15451                                          &natural_width);
15452       width  = CLAMP (natural_width, min_width, available_width);
15453       break;
15454 
15455     case CLUTTER_REQUEST_CONTENT_SIZE:
15456       if (priv->content != NULL)
15457         {
15458           clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height);
15459 
15460           width = CLAMP (natural_width, 0, available_width);
15461           height = CLAMP (natural_height, 0, available_height);
15462         }
15463       break;
15464     }
15465 
15466 
15467   box.x1 = x;
15468   box.y1 = y;
15469   box.x2 = box.x1 + width;
15470   box.y2 = box.y1 + height;
15471   clutter_actor_allocate (self, &box, flags);
15472 }
15473 
15474 /**
15475  * clutter_actor_allocate_preferred_size:
15476  * @self: a #ClutterActor
15477  * @flags: flags controlling the allocation
15478  *
15479  * Allocates the natural size of @self.
15480  *
15481  * This function is a utility call for #ClutterActor implementations
15482  * that allocates the actor's preferred natural size. It can be used
15483  * by fixed layout managers (like #ClutterGroup or so called
15484  * 'composite actors') inside the ClutterActor::allocate
15485  * implementation to give each child exactly how much space it
15486  * requires, regardless of the size of the parent.
15487  *
15488  * This function is not meant to be used by applications. It is also
15489  * not meant to be used outside the implementation of the
15490  * #ClutterActorClass.allocate virtual function.
15491  *
15492  * Since: 0.8
15493  */
15494 void
clutter_actor_allocate_preferred_size(ClutterActor * self,ClutterAllocationFlags flags)15495 clutter_actor_allocate_preferred_size (ClutterActor           *self,
15496                                        ClutterAllocationFlags  flags)
15497 {
15498   gfloat actor_x, actor_y;
15499   gfloat natural_width, natural_height;
15500   ClutterActorBox actor_box;
15501   ClutterActorPrivate *priv;
15502   const ClutterLayoutInfo *info;
15503 
15504   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15505 
15506   priv = self->priv;
15507 
15508   if (priv->position_set)
15509     {
15510       info = _clutter_actor_get_layout_info_or_defaults (self);
15511       actor_x = info->fixed_pos.x;
15512       actor_y = info->fixed_pos.y;
15513     }
15514   else
15515     {
15516       actor_x = 0;
15517       actor_y = 0;
15518     }
15519 
15520   clutter_actor_get_preferred_size (self,
15521                                     NULL, NULL,
15522                                     &natural_width,
15523                                     &natural_height);
15524 
15525   actor_box.x1 = actor_x;
15526   actor_box.y1 = actor_y;
15527   actor_box.x2 = actor_box.x1 + natural_width;
15528   actor_box.y2 = actor_box.y1 + natural_height;
15529 
15530   clutter_actor_allocate (self, &actor_box, flags);
15531 }
15532 
15533 /**
15534  * clutter_actor_allocate_align_fill:
15535  * @self: a #ClutterActor
15536  * @box: a #ClutterActorBox, containing the available width and height
15537  * @x_align: the horizontal alignment, between 0 and 1
15538  * @y_align: the vertical alignment, between 0 and 1
15539  * @x_fill: whether the actor should fill horizontally
15540  * @y_fill: whether the actor should fill vertically
15541  * @flags: allocation flags to be passed to clutter_actor_allocate()
15542  *
15543  * Allocates @self by taking into consideration the available allocation
15544  * area; an alignment factor on either axis; and whether the actor should
15545  * fill the allocation on either axis.
15546  *
15547  * The @box should contain the available allocation width and height;
15548  * if the x1 and y1 members of #ClutterActorBox are not set to 0, the
15549  * allocation will be offset by their value.
15550  *
15551  * This function takes into consideration the geometry request specified by
15552  * the #ClutterActor:request-mode property, and the text direction.
15553  *
15554  * This function is useful for fluid layout managers using legacy alignment
15555  * flags. Newly written layout managers should use the #ClutterActor:x-align
15556  * and #ClutterActor:y-align properties, instead, and just call
15557  * clutter_actor_allocate() inside their #ClutterActorClass.allocate()
15558  * implementation.
15559  *
15560  * Since: 1.4
15561  */
15562 void
clutter_actor_allocate_align_fill(ClutterActor * self,const ClutterActorBox * box,gdouble x_align,gdouble y_align,gboolean x_fill,gboolean y_fill,ClutterAllocationFlags flags)15563 clutter_actor_allocate_align_fill (ClutterActor           *self,
15564                                    const ClutterActorBox  *box,
15565                                    gdouble                 x_align,
15566                                    gdouble                 y_align,
15567                                    gboolean                x_fill,
15568                                    gboolean                y_fill,
15569                                    ClutterAllocationFlags  flags)
15570 {
15571   ClutterActorPrivate *priv;
15572   ClutterActorBox allocation = CLUTTER_ACTOR_BOX_INIT_ZERO;
15573   gfloat x_offset, y_offset;
15574   gfloat available_width, available_height;
15575   gfloat child_width = 0.f, child_height = 0.f;
15576 
15577   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15578   g_return_if_fail (box != NULL);
15579   g_return_if_fail (x_align >= 0.0 && x_align <= 1.0);
15580   g_return_if_fail (y_align >= 0.0 && y_align <= 1.0);
15581 
15582   priv = self->priv;
15583 
15584   clutter_actor_box_get_origin (box, &x_offset, &y_offset);
15585   clutter_actor_box_get_size (box, &available_width, &available_height);
15586 
15587   if (available_width <= 0)
15588     available_width = 0.f;
15589 
15590   if (available_height <= 0)
15591     available_height = 0.f;
15592 
15593   allocation.x1 = x_offset;
15594   allocation.y1 = y_offset;
15595 
15596   if (available_width == 0.f && available_height == 0.f)
15597     goto out;
15598 
15599   if (x_fill)
15600     child_width = available_width;
15601 
15602   if (y_fill)
15603     child_height = available_height;
15604 
15605   /* if we are filling horizontally and vertically then we're done */
15606   if (x_fill && y_fill)
15607     goto out;
15608 
15609   if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
15610     {
15611       gfloat min_width, natural_width;
15612       gfloat min_height, natural_height;
15613 
15614       if (!x_fill)
15615         {
15616           clutter_actor_get_preferred_width (self, available_height,
15617                                              &min_width,
15618                                              &natural_width);
15619 
15620           child_width = CLAMP (natural_width, min_width, available_width);
15621         }
15622 
15623       if (!y_fill)
15624         {
15625           clutter_actor_get_preferred_height (self, child_width,
15626                                               &min_height,
15627                                               &natural_height);
15628 
15629           child_height = CLAMP (natural_height, min_height, available_height);
15630         }
15631     }
15632   else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
15633     {
15634       gfloat min_width, natural_width;
15635       gfloat min_height, natural_height;
15636 
15637       if (!y_fill)
15638         {
15639           clutter_actor_get_preferred_height (self, available_width,
15640                                               &min_height,
15641                                               &natural_height);
15642 
15643           child_height = CLAMP (natural_height, min_height, available_height);
15644         }
15645 
15646       if (!x_fill)
15647         {
15648           clutter_actor_get_preferred_width (self, child_height,
15649                                              &min_width,
15650                                              &natural_width);
15651 
15652           child_width = CLAMP (natural_width, min_width, available_width);
15653         }
15654     }
15655   else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL)
15656     {
15657       gfloat natural_width, natural_height;
15658 
15659       clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height);
15660 
15661       if (!x_fill)
15662         child_width = CLAMP (natural_width, 0, available_width);
15663 
15664       if (!y_fill)
15665         child_height = CLAMP (natural_height, 0, available_height);
15666     }
15667 
15668   /* invert the horizontal alignment for RTL languages */
15669   if (priv->text_direction == CLUTTER_TEXT_DIRECTION_RTL)
15670     x_align = 1.0 - x_align;
15671 
15672   if (!x_fill)
15673     allocation.x1 += ((available_width - child_width) * x_align);
15674 
15675   if (!y_fill)
15676     allocation.y1 += ((available_height - child_height) * y_align);
15677 
15678 out:
15679 
15680   allocation.x1 = floorf (allocation.x1);
15681   allocation.y1 = floorf (allocation.y1);
15682   allocation.x2 = ceilf (allocation.x1 + MAX (child_width, 0));
15683   allocation.y2 = ceilf (allocation.y1 + MAX (child_height, 0));
15684 
15685   clutter_actor_allocate (self, &allocation, flags);
15686 }
15687 
15688 /**
15689  * clutter_actor_grab_key_focus:
15690  * @self: a #ClutterActor
15691  *
15692  * Sets the key focus of the #ClutterStage including @self
15693  * to this #ClutterActor.
15694  *
15695  * Since: 1.0
15696  */
15697 void
clutter_actor_grab_key_focus(ClutterActor * self)15698 clutter_actor_grab_key_focus (ClutterActor *self)
15699 {
15700   ClutterActor *stage;
15701 
15702   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15703 
15704   stage = _clutter_actor_get_stage_internal (self);
15705   if (stage != NULL)
15706     clutter_stage_set_key_focus (CLUTTER_STAGE (stage), self);
15707 }
15708 
15709 static void
update_pango_context(ClutterBackend * backend,PangoContext * context)15710 update_pango_context (ClutterBackend *backend,
15711                       PangoContext   *context)
15712 {
15713   ClutterSettings *settings;
15714   PangoFontDescription *font_desc;
15715   const cairo_font_options_t *font_options;
15716   gchar *font_name;
15717   PangoDirection pango_dir;
15718   gdouble resolution;
15719 
15720   settings = clutter_settings_get_default ();
15721 
15722   /* update the text direction */
15723   if (clutter_get_default_text_direction () == CLUTTER_TEXT_DIRECTION_RTL)
15724     pango_dir = PANGO_DIRECTION_RTL;
15725   else
15726     pango_dir = PANGO_DIRECTION_LTR;
15727 
15728   pango_context_set_base_dir (context, pango_dir);
15729 
15730   g_object_get (settings, "font-name", &font_name, NULL);
15731 
15732   /* get the configuration for the PangoContext from the backend */
15733   font_options = clutter_backend_get_font_options (backend);
15734   resolution = clutter_backend_get_resolution (backend);
15735 
15736   font_desc = pango_font_description_from_string (font_name);
15737 
15738   if (resolution < 0)
15739     resolution = 96.0; /* fall back */
15740 
15741   pango_context_set_font_description (context, font_desc);
15742   pango_cairo_context_set_font_options (context, font_options);
15743   pango_cairo_context_set_resolution (context, resolution);
15744 
15745   pango_font_description_free (font_desc);
15746   g_free (font_name);
15747 }
15748 
15749 /**
15750  * clutter_actor_get_pango_context:
15751  * @self: a #ClutterActor
15752  *
15753  * Retrieves the #PangoContext for @self. The actor's #PangoContext
15754  * is already configured using the appropriate font map, resolution
15755  * and font options.
15756  *
15757  * Unlike clutter_actor_create_pango_context(), this context is owend
15758  * by the #ClutterActor and it will be updated each time the options
15759  * stored by the #ClutterBackend change.
15760  *
15761  * You can use the returned #PangoContext to create a #PangoLayout
15762  * and render text using cogl_pango_render_layout() to reuse the
15763  * glyphs cache also used by Clutter.
15764  *
15765  * Return value: (transfer none): the #PangoContext for a #ClutterActor.
15766  *   The returned #PangoContext is owned by the actor and should not be
15767  *   unreferenced by the application code
15768  *
15769  * Since: 1.0
15770  */
15771 PangoContext *
clutter_actor_get_pango_context(ClutterActor * self)15772 clutter_actor_get_pango_context (ClutterActor *self)
15773 {
15774   ClutterActorPrivate *priv;
15775   ClutterBackend *backend = clutter_get_default_backend ();
15776 
15777   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15778 
15779   priv = self->priv;
15780 
15781   if (G_UNLIKELY (priv->pango_context == NULL))
15782     {
15783       priv->pango_context = clutter_actor_create_pango_context (self);
15784 
15785       g_signal_connect_object (backend, "resolution-changed",
15786                                G_CALLBACK (update_pango_context), priv->pango_context, 0);
15787       g_signal_connect_object (backend, "font-changed",
15788                                G_CALLBACK (update_pango_context), priv->pango_context, 0);
15789     }
15790   else
15791     update_pango_context (backend, priv->pango_context);
15792 
15793   return priv->pango_context;
15794 }
15795 
15796 /**
15797  * clutter_actor_create_pango_context:
15798  * @self: a #ClutterActor
15799  *
15800  * Creates a #PangoContext for the given actor. The #PangoContext
15801  * is already configured using the appropriate font map, resolution
15802  * and font options.
15803  *
15804  * See also clutter_actor_get_pango_context().
15805  *
15806  * Return value: (transfer full): the newly created #PangoContext.
15807  *   Use g_object_unref() on the returned value to deallocate its
15808  *   resources
15809  *
15810  * Since: 1.0
15811  */
15812 PangoContext *
clutter_actor_create_pango_context(ClutterActor * self)15813 clutter_actor_create_pango_context (ClutterActor *self)
15814 {
15815   CoglPangoFontMap *font_map;
15816   PangoContext *context;
15817 
15818   font_map = COGL_PANGO_FONT_MAP (clutter_get_font_map ());
15819 
15820   context = cogl_pango_font_map_create_context (font_map);
15821   update_pango_context (clutter_get_default_backend (), context);
15822   pango_context_set_language (context, pango_language_get_default ());
15823 
15824   return context;
15825 }
15826 
15827 /**
15828  * clutter_actor_create_pango_layout:
15829  * @self: a #ClutterActor
15830  * @text: (allow-none): the text to set on the #PangoLayout, or %NULL
15831  *
15832  * Creates a new #PangoLayout from the same #PangoContext used
15833  * by the #ClutterActor. The #PangoLayout is already configured
15834  * with the font map, resolution and font options, and the
15835  * given @text.
15836  *
15837  * If you want to keep around a #PangoLayout created by this
15838  * function you will have to connect to the #ClutterBackend::font-changed
15839  * and #ClutterBackend::resolution-changed signals, and call
15840  * pango_layout_context_changed() in response to them.
15841  *
15842  * Return value: (transfer full): the newly created #PangoLayout.
15843  *   Use g_object_unref() when done
15844  *
15845  * Since: 1.0
15846  */
15847 PangoLayout *
clutter_actor_create_pango_layout(ClutterActor * self,const gchar * text)15848 clutter_actor_create_pango_layout (ClutterActor *self,
15849                                    const gchar  *text)
15850 {
15851   PangoContext *context;
15852   PangoLayout *layout;
15853 
15854   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
15855 
15856   context = clutter_actor_get_pango_context (self);
15857   layout = pango_layout_new (context);
15858 
15859   if (text)
15860     pango_layout_set_text (layout, text, -1);
15861 
15862   return layout;
15863 }
15864 
15865 /**
15866  * clutter_actor_set_opacity_override:
15867  * @self: a #ClutterActor
15868  * @opacity: the override opacity value, or -1 to reset
15869  *
15870  * Allows overriding the calculated paint opacity (as returned by
15871  * clutter_actor_get_paint_opacity()). This is used internally by
15872  * ClutterClone and ClutterOffscreenEffect, and should be used by
15873  * actors that need to mimick those.
15874  *
15875  * In almost all cases this should not used by applications.
15876  *
15877  * Stability: unstable
15878  */
15879 void
clutter_actor_set_opacity_override(ClutterActor * self,gint opacity)15880 clutter_actor_set_opacity_override (ClutterActor *self,
15881                                      gint          opacity)
15882 {
15883   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15884 
15885   /* ensure bounds */
15886   if (opacity >= 0)
15887     opacity = CLAMP (opacity, 0, 255);
15888   else
15889     opacity = -1;
15890 
15891   self->priv->opacity_override = opacity;
15892 }
15893 
15894 /**
15895  * clutter_actor_get_opacity_override:
15896  * @self: a #ClutterActor
15897  *
15898  * See clutter_actor_set_opacity_override()
15899  *
15900  * Returns: the override value for the actor's opacity, or -1 if no override
15901  *   is set.
15902  *
15903  * Since: 1.22
15904  *
15905  * Stability: unstable
15906  */
15907 gint
clutter_actor_get_opacity_override(ClutterActor * self)15908 clutter_actor_get_opacity_override (ClutterActor *self)
15909 {
15910   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), -1);
15911 
15912   return self->priv->opacity_override;
15913 }
15914 
15915 /* Allows you to disable applying the actors model view transform during
15916  * a paint. Used by ClutterClone. */
15917 void
_clutter_actor_set_enable_model_view_transform(ClutterActor * self,gboolean enable)15918 _clutter_actor_set_enable_model_view_transform (ClutterActor *self,
15919                                                 gboolean      enable)
15920 {
15921   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15922 
15923   self->priv->enable_model_view_transform = enable;
15924 }
15925 
15926 void
_clutter_actor_set_enable_paint_unmapped(ClutterActor * self,gboolean enable)15927 _clutter_actor_set_enable_paint_unmapped (ClutterActor *self,
15928                                           gboolean      enable)
15929 {
15930   ClutterActorPrivate *priv;
15931 
15932   g_return_if_fail (CLUTTER_IS_ACTOR (self));
15933 
15934   priv = self->priv;
15935 
15936   priv->enable_paint_unmapped = enable;
15937 
15938   if (priv->enable_paint_unmapped)
15939     {
15940       /* Make sure that the parents of the widget are realized first;
15941        * otherwise checks in clutter_actor_update_map_state() will
15942        * fail.
15943        */
15944       clutter_actor_realize (self);
15945 
15946       /* If the actor isn't ultimately connected to a toplevel, it can't be
15947        * realized or painted.
15948        */
15949       if (CLUTTER_ACTOR_IS_REALIZED (self))
15950           clutter_actor_update_map_state (self, MAP_STATE_MAKE_MAPPED);
15951     }
15952   else
15953     {
15954       clutter_actor_update_map_state (self, MAP_STATE_CHECK);
15955     }
15956 }
15957 
15958 static void
clutter_anchor_coord_get_units(ClutterActor * self,const AnchorCoord * coord,gfloat * x,gfloat * y,gfloat * z)15959 clutter_anchor_coord_get_units (ClutterActor      *self,
15960                                 const AnchorCoord *coord,
15961                                 gfloat            *x,
15962                                 gfloat            *y,
15963                                 gfloat            *z)
15964 {
15965   if (coord->is_fractional)
15966     {
15967       gfloat actor_width, actor_height;
15968 
15969       clutter_actor_get_size (self, &actor_width, &actor_height);
15970 
15971       if (x)
15972         *x = actor_width * coord->v.fraction.x;
15973 
15974       if (y)
15975         *y = actor_height * coord->v.fraction.y;
15976 
15977       if (z)
15978         *z = 0;
15979     }
15980   else
15981     {
15982       if (x)
15983         *x = coord->v.units.x;
15984 
15985       if (y)
15986         *y = coord->v.units.y;
15987 
15988       if (z)
15989         *z = coord->v.units.z;
15990     }
15991 }
15992 
15993 static void
clutter_anchor_coord_set_units(AnchorCoord * coord,gfloat x,gfloat y,gfloat z)15994 clutter_anchor_coord_set_units (AnchorCoord *coord,
15995                                 gfloat       x,
15996                                 gfloat       y,
15997                                 gfloat       z)
15998 {
15999   coord->is_fractional = FALSE;
16000   coord->v.units.x = x;
16001   coord->v.units.y = y;
16002   coord->v.units.z = z;
16003 }
16004 
16005 static ClutterGravity
clutter_anchor_coord_get_gravity(const AnchorCoord * coord)16006 clutter_anchor_coord_get_gravity (const AnchorCoord *coord)
16007 {
16008   if (coord->is_fractional)
16009     {
16010       if (coord->v.fraction.x == 0.0)
16011         {
16012           if (coord->v.fraction.y == 0.0)
16013             return CLUTTER_GRAVITY_NORTH_WEST;
16014           else if (coord->v.fraction.y == 0.5)
16015             return CLUTTER_GRAVITY_WEST;
16016           else if (coord->v.fraction.y == 1.0)
16017             return CLUTTER_GRAVITY_SOUTH_WEST;
16018           else
16019             return CLUTTER_GRAVITY_NONE;
16020         }
16021       else if (coord->v.fraction.x == 0.5)
16022         {
16023           if (coord->v.fraction.y == 0.0)
16024             return CLUTTER_GRAVITY_NORTH;
16025           else if (coord->v.fraction.y == 0.5)
16026             return CLUTTER_GRAVITY_CENTER;
16027           else if (coord->v.fraction.y == 1.0)
16028             return CLUTTER_GRAVITY_SOUTH;
16029           else
16030             return CLUTTER_GRAVITY_NONE;
16031         }
16032       else if (coord->v.fraction.x == 1.0)
16033         {
16034           if (coord->v.fraction.y == 0.0)
16035             return CLUTTER_GRAVITY_NORTH_EAST;
16036           else if (coord->v.fraction.y == 0.5)
16037             return CLUTTER_GRAVITY_EAST;
16038           else if (coord->v.fraction.y == 1.0)
16039             return CLUTTER_GRAVITY_SOUTH_EAST;
16040           else
16041             return CLUTTER_GRAVITY_NONE;
16042         }
16043       else
16044         return CLUTTER_GRAVITY_NONE;
16045     }
16046   else
16047     return CLUTTER_GRAVITY_NONE;
16048 }
16049 
16050 static void
clutter_anchor_coord_set_gravity(AnchorCoord * coord,ClutterGravity gravity)16051 clutter_anchor_coord_set_gravity (AnchorCoord    *coord,
16052                                   ClutterGravity  gravity)
16053 {
16054   switch (gravity)
16055     {
16056     case CLUTTER_GRAVITY_NORTH:
16057       coord->v.fraction.x = 0.5;
16058       coord->v.fraction.y = 0.0;
16059       break;
16060 
16061     case CLUTTER_GRAVITY_NORTH_EAST:
16062       coord->v.fraction.x = 1.0;
16063       coord->v.fraction.y = 0.0;
16064       break;
16065 
16066     case CLUTTER_GRAVITY_EAST:
16067       coord->v.fraction.x = 1.0;
16068       coord->v.fraction.y = 0.5;
16069       break;
16070 
16071     case CLUTTER_GRAVITY_SOUTH_EAST:
16072       coord->v.fraction.x = 1.0;
16073       coord->v.fraction.y = 1.0;
16074       break;
16075 
16076     case CLUTTER_GRAVITY_SOUTH:
16077       coord->v.fraction.x = 0.5;
16078       coord->v.fraction.y = 1.0;
16079       break;
16080 
16081     case CLUTTER_GRAVITY_SOUTH_WEST:
16082       coord->v.fraction.x = 0.0;
16083       coord->v.fraction.y = 1.0;
16084       break;
16085 
16086     case CLUTTER_GRAVITY_WEST:
16087       coord->v.fraction.x = 0.0;
16088       coord->v.fraction.y = 0.5;
16089       break;
16090 
16091     case CLUTTER_GRAVITY_NORTH_WEST:
16092       coord->v.fraction.x = 0.0;
16093       coord->v.fraction.y = 0.0;
16094       break;
16095 
16096     case CLUTTER_GRAVITY_CENTER:
16097       coord->v.fraction.x = 0.5;
16098       coord->v.fraction.y = 0.5;
16099       break;
16100 
16101     default:
16102       coord->v.fraction.x = 0.0;
16103       coord->v.fraction.y = 0.0;
16104       break;
16105     }
16106 
16107   coord->is_fractional = TRUE;
16108 }
16109 
16110 static gboolean
clutter_anchor_coord_is_zero(const AnchorCoord * coord)16111 clutter_anchor_coord_is_zero (const AnchorCoord *coord)
16112 {
16113   if (coord->is_fractional)
16114     return coord->v.fraction.x == 0.0 && coord->v.fraction.y == 0.0;
16115   else
16116     return (coord->v.units.x == 0.0
16117             && coord->v.units.y == 0.0
16118             && coord->v.units.z == 0.0);
16119 }
16120 
16121 /**
16122  * clutter_actor_get_flags:
16123  * @self: a #ClutterActor
16124  *
16125  * Retrieves the flags set on @self
16126  *
16127  * Return value: a bitwise or of #ClutterActorFlags or 0
16128  *
16129  * Since: 1.0
16130  */
16131 ClutterActorFlags
clutter_actor_get_flags(ClutterActor * self)16132 clutter_actor_get_flags (ClutterActor *self)
16133 {
16134   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
16135 
16136   return self->flags;
16137 }
16138 
16139 /**
16140  * clutter_actor_set_flags:
16141  * @self: a #ClutterActor
16142  * @flags: the flags to set
16143  *
16144  * Sets @flags on @self
16145  *
16146  * This function will emit notifications for the changed properties
16147  *
16148  * Since: 1.0
16149  */
16150 void
clutter_actor_set_flags(ClutterActor * self,ClutterActorFlags flags)16151 clutter_actor_set_flags (ClutterActor      *self,
16152                          ClutterActorFlags  flags)
16153 {
16154   ClutterActorFlags old_flags;
16155   GObject *obj;
16156   gboolean was_reactive_set, reactive_set;
16157   gboolean was_realized_set, realized_set;
16158   gboolean was_mapped_set, mapped_set;
16159   gboolean was_visible_set, visible_set;
16160 
16161   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16162 
16163   if (self->flags == flags)
16164     return;
16165 
16166   obj = G_OBJECT (self);
16167   g_object_ref (obj);
16168   g_object_freeze_notify (obj);
16169 
16170   old_flags = self->flags;
16171 
16172   was_reactive_set = ((old_flags & CLUTTER_ACTOR_REACTIVE) != 0);
16173   was_realized_set = ((old_flags & CLUTTER_ACTOR_REALIZED) != 0);
16174   was_mapped_set   = ((old_flags & CLUTTER_ACTOR_MAPPED)   != 0);
16175   was_visible_set  = ((old_flags & CLUTTER_ACTOR_VISIBLE)  != 0);
16176 
16177   self->flags |= flags;
16178 
16179   reactive_set = ((self->flags & CLUTTER_ACTOR_REACTIVE) != 0);
16180   realized_set = ((self->flags & CLUTTER_ACTOR_REALIZED) != 0);
16181   mapped_set   = ((self->flags & CLUTTER_ACTOR_MAPPED)   != 0);
16182   visible_set  = ((self->flags & CLUTTER_ACTOR_VISIBLE)  != 0);
16183 
16184   if (reactive_set != was_reactive_set)
16185     g_object_notify_by_pspec (obj, obj_props[PROP_REACTIVE]);
16186 
16187   if (realized_set != was_realized_set)
16188     g_object_notify_by_pspec (obj, obj_props[PROP_REALIZED]);
16189 
16190   if (mapped_set != was_mapped_set)
16191     g_object_notify_by_pspec (obj, obj_props[PROP_MAPPED]);
16192 
16193   if (visible_set != was_visible_set)
16194     g_object_notify_by_pspec (obj, obj_props[PROP_VISIBLE]);
16195 
16196   g_object_thaw_notify (obj);
16197   g_object_unref (obj);
16198 }
16199 
16200 /**
16201  * clutter_actor_unset_flags:
16202  * @self: a #ClutterActor
16203  * @flags: the flags to unset
16204  *
16205  * Unsets @flags on @self
16206  *
16207  * This function will emit notifications for the changed properties
16208  *
16209  * Since: 1.0
16210  */
16211 void
clutter_actor_unset_flags(ClutterActor * self,ClutterActorFlags flags)16212 clutter_actor_unset_flags (ClutterActor      *self,
16213                            ClutterActorFlags  flags)
16214 {
16215   ClutterActorFlags old_flags;
16216   GObject *obj;
16217   gboolean was_reactive_set, reactive_set;
16218   gboolean was_realized_set, realized_set;
16219   gboolean was_mapped_set, mapped_set;
16220   gboolean was_visible_set, visible_set;
16221 
16222   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16223 
16224   obj = G_OBJECT (self);
16225   g_object_freeze_notify (obj);
16226 
16227   old_flags = self->flags;
16228 
16229   was_reactive_set = ((old_flags & CLUTTER_ACTOR_REACTIVE) != 0);
16230   was_realized_set = ((old_flags & CLUTTER_ACTOR_REALIZED) != 0);
16231   was_mapped_set   = ((old_flags & CLUTTER_ACTOR_MAPPED)   != 0);
16232   was_visible_set  = ((old_flags & CLUTTER_ACTOR_VISIBLE)  != 0);
16233 
16234   self->flags &= ~flags;
16235 
16236   if (self->flags == old_flags)
16237     return;
16238 
16239   reactive_set = ((self->flags & CLUTTER_ACTOR_REACTIVE) != 0);
16240   realized_set = ((self->flags & CLUTTER_ACTOR_REALIZED) != 0);
16241   mapped_set   = ((self->flags & CLUTTER_ACTOR_MAPPED)   != 0);
16242   visible_set  = ((self->flags & CLUTTER_ACTOR_VISIBLE)  != 0);
16243 
16244   if (reactive_set != was_reactive_set)
16245     g_object_notify_by_pspec (obj, obj_props[PROP_REACTIVE]);
16246 
16247   if (realized_set != was_realized_set)
16248     g_object_notify_by_pspec (obj, obj_props[PROP_REALIZED]);
16249 
16250   if (mapped_set != was_mapped_set)
16251     g_object_notify_by_pspec (obj, obj_props[PROP_MAPPED]);
16252 
16253   if (visible_set != was_visible_set)
16254     g_object_notify_by_pspec (obj, obj_props[PROP_VISIBLE]);
16255 
16256   g_object_thaw_notify (obj);
16257 }
16258 
16259 /**
16260  * clutter_actor_get_transformation_matrix:
16261  * @self: a #ClutterActor
16262  * @matrix: (out caller-allocates): the return location for a #ClutterMatrix
16263  *
16264  * Retrieves the transformations applied to @self relative to its
16265  * parent.
16266  *
16267  * Since: 1.0
16268  *
16269  * Deprecated: 1.12: Use clutter_actor_get_transform() instead
16270  */
16271 void
clutter_actor_get_transformation_matrix(ClutterActor * self,ClutterMatrix * matrix)16272 clutter_actor_get_transformation_matrix (ClutterActor  *self,
16273                                          ClutterMatrix *matrix)
16274 {
16275   clutter_actor_get_transform (self, matrix);
16276 }
16277 
16278 static void
clutter_actor_set_transform_internal(ClutterActor * self,const ClutterMatrix * transform)16279 clutter_actor_set_transform_internal (ClutterActor        *self,
16280                                       const ClutterMatrix *transform)
16281 {
16282   ClutterTransformInfo *info;
16283   gboolean was_set;
16284   GObject *obj;
16285 
16286   obj = G_OBJECT (self);
16287 
16288   info = _clutter_actor_get_transform_info (self);
16289 
16290   was_set = info->transform_set;
16291 
16292   info->transform = *transform;
16293   info->transform_set = !cogl_matrix_is_identity (&info->transform);
16294 
16295   self->priv->transform_valid = FALSE;
16296 
16297   clutter_actor_queue_redraw (self);
16298 
16299   g_object_notify_by_pspec (obj, obj_props[PROP_TRANSFORM]);
16300 
16301   if (was_set != info->transform_set)
16302     g_object_notify_by_pspec (obj, obj_props[PROP_TRANSFORM_SET]);
16303 }
16304 
16305 /**
16306  * clutter_actor_set_transform:
16307  * @self: a #ClutterActor
16308  * @transform: (allow-none): a #ClutterMatrix, or %NULL to
16309  *   unset a custom transformation
16310  *
16311  * Overrides the transformations of a #ClutterActor with a custom
16312  * matrix, which will be applied relative to the origin of the
16313  * actor's allocation and to the actor's pivot point.
16314  *
16315  * The #ClutterActor:transform property is animatable.
16316  *
16317  * Since: 1.12
16318  */
16319 void
clutter_actor_set_transform(ClutterActor * self,const ClutterMatrix * transform)16320 clutter_actor_set_transform (ClutterActor        *self,
16321                              const ClutterMatrix *transform)
16322 {
16323   const ClutterTransformInfo *info;
16324   ClutterMatrix new_transform;
16325 
16326   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16327 
16328   info = _clutter_actor_get_transform_info_or_defaults (self);
16329 
16330   if (transform != NULL)
16331     clutter_matrix_init_from_matrix (&new_transform, transform);
16332   else
16333     clutter_matrix_init_identity (&new_transform);
16334 
16335   _clutter_actor_create_transition (self, obj_props[PROP_TRANSFORM],
16336                                     &info->transform,
16337                                     &new_transform);
16338 }
16339 
16340 /**
16341  * clutter_actor_get_transform:
16342  * @self: a #ClutterActor
16343  * @transform: (out caller-allocates): a #ClutterMatrix
16344  *
16345  * Retrieves the current transformation matrix of a #ClutterActor.
16346  *
16347  * Since: 1.12
16348  */
16349 void
clutter_actor_get_transform(ClutterActor * self,ClutterMatrix * transform)16350 clutter_actor_get_transform (ClutterActor  *self,
16351                              ClutterMatrix *transform)
16352 {
16353   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16354   g_return_if_fail (transform != NULL);
16355 
16356   cogl_matrix_init_identity (transform);
16357   _clutter_actor_apply_modelview_transform (self, transform);
16358 }
16359 
16360 void
_clutter_actor_set_in_clone_paint(ClutterActor * self,gboolean is_in_clone_paint)16361 _clutter_actor_set_in_clone_paint (ClutterActor *self,
16362                                    gboolean      is_in_clone_paint)
16363 {
16364   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16365   self->priv->in_clone_paint = is_in_clone_paint;
16366 }
16367 
16368 /**
16369  * clutter_actor_is_in_clone_paint:
16370  * @self: a #ClutterActor
16371  *
16372  * Checks whether @self is being currently painted by a #ClutterClone
16373  *
16374  * This function is useful only inside the ::paint virtual function
16375  * implementations or within handlers for the #ClutterActor::paint
16376  * signal
16377  *
16378  * This function should not be used by applications
16379  *
16380  * Return value: %TRUE if the #ClutterActor is currently being painted
16381  *   by a #ClutterClone, and %FALSE otherwise
16382  *
16383  * Since: 1.0
16384  */
16385 gboolean
clutter_actor_is_in_clone_paint(ClutterActor * self)16386 clutter_actor_is_in_clone_paint (ClutterActor *self)
16387 {
16388   ClutterActor *parent;
16389 
16390   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
16391 
16392   if (self->priv->in_clone_paint)
16393     return TRUE;
16394 
16395   if (self->priv->in_cloned_branch == 0)
16396     return FALSE;
16397 
16398   parent = self->priv->parent;
16399   while (parent != NULL)
16400     {
16401       if (parent->priv->in_cloned_branch == 0)
16402         break;
16403 
16404       if (parent->priv->in_clone_paint)
16405         return TRUE;
16406 
16407       parent = parent->priv->parent;
16408     }
16409 
16410   return FALSE;
16411 }
16412 
16413 static gboolean
set_direction_recursive(ClutterActor * actor,gpointer user_data)16414 set_direction_recursive (ClutterActor *actor,
16415                          gpointer      user_data)
16416 {
16417   ClutterTextDirection text_dir = GPOINTER_TO_INT (user_data);
16418 
16419   clutter_actor_set_text_direction (actor, text_dir);
16420 
16421   return TRUE;
16422 }
16423 
16424 /**
16425  * clutter_actor_set_text_direction:
16426  * @self: a #ClutterActor
16427  * @text_dir: the text direction for @self
16428  *
16429  * Sets the #ClutterTextDirection for an actor
16430  *
16431  * The passed text direction must not be %CLUTTER_TEXT_DIRECTION_DEFAULT
16432  *
16433  * If @self implements #ClutterContainer then this function will recurse
16434  * inside all the children of @self (including the internal ones).
16435  *
16436  * Composite actors not implementing #ClutterContainer, or actors requiring
16437  * special handling when the text direction changes, should connect to
16438  * the #GObject::notify signal for the #ClutterActor:text-direction property
16439  *
16440  * Since: 1.2
16441  */
16442 void
clutter_actor_set_text_direction(ClutterActor * self,ClutterTextDirection text_dir)16443 clutter_actor_set_text_direction (ClutterActor         *self,
16444                                   ClutterTextDirection  text_dir)
16445 {
16446   ClutterActorPrivate *priv;
16447 
16448   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16449   g_return_if_fail (text_dir != CLUTTER_TEXT_DIRECTION_DEFAULT);
16450 
16451   priv = self->priv;
16452 
16453   if (priv->text_direction != text_dir)
16454     {
16455       priv->text_direction = text_dir;
16456 
16457       /* we need to emit the notify::text-direction first, so that
16458        * the sub-classes can catch that and do specific handling of
16459        * the text direction; see clutter_text_direction_changed_cb()
16460        * inside clutter-text.c
16461        */
16462       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT_DIRECTION]);
16463 
16464       _clutter_actor_foreach_child (self, set_direction_recursive,
16465                                     GINT_TO_POINTER (text_dir));
16466 
16467       clutter_actor_queue_relayout (self);
16468     }
16469 }
16470 
16471 void
_clutter_actor_set_has_pointer(ClutterActor * self,gboolean has_pointer)16472 _clutter_actor_set_has_pointer (ClutterActor *self,
16473                                 gboolean      has_pointer)
16474 {
16475   ClutterActorPrivate *priv = self->priv;
16476 
16477   if (priv->has_pointer != has_pointer)
16478     {
16479       priv->has_pointer = has_pointer;
16480 
16481       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_POINTER]);
16482     }
16483 }
16484 
16485 /**
16486  * clutter_actor_get_text_direction:
16487  * @self: a #ClutterActor
16488  *
16489  * Retrieves the value set using clutter_actor_set_text_direction()
16490  *
16491  * If no text direction has been previously set, the default text
16492  * direction, as returned by clutter_get_default_text_direction(), will
16493  * be returned instead
16494  *
16495  * Return value: the #ClutterTextDirection for the actor
16496  *
16497  * Since: 1.2
16498  */
16499 ClutterTextDirection
clutter_actor_get_text_direction(ClutterActor * self)16500 clutter_actor_get_text_direction (ClutterActor *self)
16501 {
16502   ClutterActorPrivate *priv;
16503 
16504   g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
16505                         CLUTTER_TEXT_DIRECTION_LTR);
16506 
16507   priv = self->priv;
16508 
16509   /* if no direction has been set yet use the default */
16510   if (priv->text_direction == CLUTTER_TEXT_DIRECTION_DEFAULT)
16511     priv->text_direction = clutter_get_default_text_direction ();
16512 
16513   return priv->text_direction;
16514 }
16515 
16516 /**
16517  * clutter_actor_push_internal:
16518  * @self: a #ClutterActor
16519  *
16520  * Should be used by actors implementing the #ClutterContainer and with
16521  * internal children added through clutter_actor_set_parent(), for instance:
16522  *
16523  * |[<!-- language="C" -->
16524  *   static void
16525  *   my_actor_init (MyActor *self)
16526  *   {
16527  *     self->priv = my_actor_get_instance_private (self);
16528  *
16529  *     clutter_actor_push_internal (CLUTTER_ACTOR (self));
16530  *
16531  *     // calling clutter_actor_set_parent() now will result in
16532  *     // the internal flag being set on a child of MyActor
16533  *
16534  *     // internal child - a background texture
16535  *     self->priv->background_tex = clutter_texture_new ();
16536  *     clutter_actor_set_parent (self->priv->background_tex,
16537  *                               CLUTTER_ACTOR (self));
16538  *
16539  *     // internal child - a label
16540  *     self->priv->label = clutter_text_new ();
16541  *     clutter_actor_set_parent (self->priv->label,
16542  *                               CLUTTER_ACTOR (self));
16543  *
16544  *     clutter_actor_pop_internal (CLUTTER_ACTOR (self));
16545  *
16546  *     // calling clutter_actor_set_parent() now will not result in
16547  *     // the internal flag being set on a child of MyActor
16548  *   }
16549  * ]|
16550  *
16551  * This function will be used by Clutter to toggle an "internal child"
16552  * flag whenever clutter_actor_set_parent() is called; internal children
16553  * are handled differently by Clutter, specifically when destroying their
16554  * parent.
16555  *
16556  * Call clutter_actor_pop_internal() when you finished adding internal
16557  * children.
16558  *
16559  * Nested calls to clutter_actor_push_internal() are allowed, but each
16560  * one must by followed by a clutter_actor_pop_internal() call.
16561  *
16562  * Since: 1.2
16563  *
16564  * Deprecated: 1.10: All children of an actor are accessible through
16565  *   the #ClutterActor API, and #ClutterActor implements the
16566  *   #ClutterContainer interface, so this function is only useful
16567  *   for legacy containers overriding the default implementation.
16568  */
16569 void
clutter_actor_push_internal(ClutterActor * self)16570 clutter_actor_push_internal (ClutterActor *self)
16571 {
16572   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16573 
16574   self->priv->internal_child += 1;
16575 }
16576 
16577 /**
16578  * clutter_actor_pop_internal:
16579  * @self: a #ClutterActor
16580  *
16581  * Disables the effects of clutter_actor_push_internal().
16582  *
16583  * Since: 1.2
16584  *
16585  * Deprecated: 1.10: All children of an actor are accessible through
16586  *   the #ClutterActor API. This function is only useful for legacy
16587  *   containers overriding the default implementation of the
16588  *   #ClutterContainer interface.
16589  */
16590 void
clutter_actor_pop_internal(ClutterActor * self)16591 clutter_actor_pop_internal (ClutterActor *self)
16592 {
16593   ClutterActorPrivate *priv;
16594 
16595   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16596 
16597   priv = self->priv;
16598 
16599   if (priv->internal_child == 0)
16600     {
16601       g_warning ("Mismatched %s: you need to call "
16602                  "clutter_actor_push_composite() at least once before "
16603                  "calling this function", G_STRFUNC);
16604       return;
16605     }
16606 
16607   priv->internal_child -= 1;
16608 }
16609 
16610 /**
16611  * clutter_actor_has_pointer:
16612  * @self: a #ClutterActor
16613  *
16614  * Checks whether an actor contains the pointer of a
16615  * #ClutterInputDevice
16616  *
16617  * Return value: %TRUE if the actor contains the pointer, and
16618  *   %FALSE otherwise
16619  *
16620  * Since: 1.2
16621  */
16622 gboolean
clutter_actor_has_pointer(ClutterActor * self)16623 clutter_actor_has_pointer (ClutterActor *self)
16624 {
16625   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
16626 
16627   return self->priv->has_pointer;
16628 }
16629 
16630 /* XXX: This is a workaround for not being able to break the ABI of
16631  * the QUEUE_REDRAW signal. It is an out-of-band argument.  See
16632  * clutter_actor_queue_clipped_redraw() for details.
16633  */
16634 ClutterPaintVolume *
_clutter_actor_get_queue_redraw_clip(ClutterActor * self)16635 _clutter_actor_get_queue_redraw_clip (ClutterActor *self)
16636 {
16637   return g_object_get_data (G_OBJECT (self),
16638                             "-clutter-actor-queue-redraw-clip");
16639 }
16640 
16641 void
_clutter_actor_set_queue_redraw_clip(ClutterActor * self,ClutterPaintVolume * clip)16642 _clutter_actor_set_queue_redraw_clip (ClutterActor       *self,
16643                                       ClutterPaintVolume *clip)
16644 {
16645   g_object_set_data (G_OBJECT (self),
16646                      "-clutter-actor-queue-redraw-clip",
16647                      clip);
16648 }
16649 
16650 /**
16651  * clutter_actor_has_allocation:
16652  * @self: a #ClutterActor
16653  *
16654  * Checks if the actor has an up-to-date allocation assigned to
16655  * it. This means that the actor should have an allocation: it's
16656  * visible and has a parent. It also means that there is no
16657  * outstanding relayout request in progress for the actor or its
16658  * children (There might be other outstanding layout requests in
16659  * progress that will cause the actor to get a new allocation
16660  * when the stage is laid out, however).
16661  *
16662  * If this function returns %FALSE, then the actor will normally
16663  * be allocated before it is next drawn on the screen.
16664  *
16665  * Return value: %TRUE if the actor has an up-to-date allocation
16666  *
16667  * Since: 1.4
16668  */
16669 gboolean
clutter_actor_has_allocation(ClutterActor * self)16670 clutter_actor_has_allocation (ClutterActor *self)
16671 {
16672   ClutterActorPrivate *priv;
16673 
16674   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
16675 
16676   priv = self->priv;
16677 
16678   return priv->parent != NULL &&
16679          CLUTTER_ACTOR_IS_VISIBLE (self) &&
16680          !priv->needs_allocation;
16681 }
16682 
16683 /**
16684  * clutter_actor_add_action:
16685  * @self: a #ClutterActor
16686  * @action: a #ClutterAction
16687  *
16688  * Adds @action to the list of actions applied to @self
16689  *
16690  * A #ClutterAction can only belong to one actor at a time
16691  *
16692  * The #ClutterActor will hold a reference on @action until either
16693  * clutter_actor_remove_action() or clutter_actor_clear_actions()
16694  * is called
16695  *
16696  * Since: 1.4
16697  */
16698 void
clutter_actor_add_action(ClutterActor * self,ClutterAction * action)16699 clutter_actor_add_action (ClutterActor  *self,
16700                           ClutterAction *action)
16701 {
16702   ClutterActorPrivate *priv;
16703 
16704   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16705   g_return_if_fail (CLUTTER_IS_ACTION (action));
16706 
16707   priv = self->priv;
16708 
16709   if (priv->actions == NULL)
16710     {
16711       priv->actions = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
16712       priv->actions->actor = self;
16713     }
16714 
16715   _clutter_meta_group_add_meta (priv->actions, CLUTTER_ACTOR_META (action));
16716 
16717   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
16718 }
16719 
16720 /**
16721  * clutter_actor_add_action_with_name:
16722  * @self: a #ClutterActor
16723  * @name: the name to set on the action
16724  * @action: a #ClutterAction
16725  *
16726  * A convenience function for setting the name of a #ClutterAction
16727  * while adding it to the list of actions applied to @self
16728  *
16729  * This function is the logical equivalent of:
16730  *
16731  * |[<!-- language="C" -->
16732  *   clutter_actor_meta_set_name (CLUTTER_ACTOR_META (action), name);
16733  *   clutter_actor_add_action (self, action);
16734  * ]|
16735  *
16736  * Since: 1.4
16737  */
16738 void
clutter_actor_add_action_with_name(ClutterActor * self,const gchar * name,ClutterAction * action)16739 clutter_actor_add_action_with_name (ClutterActor  *self,
16740                                     const gchar   *name,
16741                                     ClutterAction *action)
16742 {
16743   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16744   g_return_if_fail (name != NULL);
16745   g_return_if_fail (CLUTTER_IS_ACTION (action));
16746 
16747   clutter_actor_meta_set_name (CLUTTER_ACTOR_META (action), name);
16748   clutter_actor_add_action (self, action);
16749 }
16750 
16751 /**
16752  * clutter_actor_remove_action:
16753  * @self: a #ClutterActor
16754  * @action: a #ClutterAction
16755  *
16756  * Removes @action from the list of actions applied to @self
16757  *
16758  * The reference held by @self on the #ClutterAction will be released
16759  *
16760  * Since: 1.4
16761  */
16762 void
clutter_actor_remove_action(ClutterActor * self,ClutterAction * action)16763 clutter_actor_remove_action (ClutterActor  *self,
16764                              ClutterAction *action)
16765 {
16766   ClutterActorPrivate *priv;
16767 
16768   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16769   g_return_if_fail (CLUTTER_IS_ACTION (action));
16770 
16771   priv = self->priv;
16772 
16773   if (priv->actions == NULL)
16774     return;
16775 
16776   _clutter_meta_group_remove_meta (priv->actions, CLUTTER_ACTOR_META (action));
16777 
16778   if (_clutter_meta_group_peek_metas (priv->actions) == NULL)
16779     g_clear_object (&priv->actions);
16780 
16781   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
16782 }
16783 
16784 /**
16785  * clutter_actor_remove_action_by_name:
16786  * @self: a #ClutterActor
16787  * @name: the name of the action to remove
16788  *
16789  * Removes the #ClutterAction with the given name from the list
16790  * of actions applied to @self
16791  *
16792  * Since: 1.4
16793  */
16794 void
clutter_actor_remove_action_by_name(ClutterActor * self,const gchar * name)16795 clutter_actor_remove_action_by_name (ClutterActor *self,
16796                                      const gchar  *name)
16797 {
16798   ClutterActorPrivate *priv;
16799   ClutterActorMeta *meta;
16800 
16801   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16802   g_return_if_fail (name != NULL);
16803 
16804   priv = self->priv;
16805 
16806   if (priv->actions == NULL)
16807     return;
16808 
16809   meta = _clutter_meta_group_get_meta (priv->actions, name);
16810   if (meta == NULL)
16811     return;
16812 
16813   _clutter_meta_group_remove_meta (priv->actions, meta);
16814 
16815   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]);
16816 }
16817 
16818 /**
16819  * clutter_actor_get_actions:
16820  * @self: a #ClutterActor
16821  *
16822  * Retrieves the list of actions applied to @self
16823  *
16824  * Return value: (transfer container) (element-type Clutter.Action): a copy
16825  *   of the list of #ClutterAction<!-- -->s. The contents of the list are
16826  *   owned by the #ClutterActor. Use g_list_free() to free the resources
16827  *   allocated by the returned #GList
16828  *
16829  * Since: 1.4
16830  */
16831 GList *
clutter_actor_get_actions(ClutterActor * self)16832 clutter_actor_get_actions (ClutterActor *self)
16833 {
16834   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
16835 
16836   if (self->priv->actions == NULL)
16837     return NULL;
16838 
16839   return _clutter_meta_group_get_metas_no_internal (self->priv->actions);
16840 }
16841 
16842 /**
16843  * clutter_actor_get_action:
16844  * @self: a #ClutterActor
16845  * @name: the name of the action to retrieve
16846  *
16847  * Retrieves the #ClutterAction with the given name in the list
16848  * of actions applied to @self
16849  *
16850  * Return value: (transfer none): a #ClutterAction for the given
16851  *   name, or %NULL. The returned #ClutterAction is owned by the
16852  *   actor and it should not be unreferenced directly
16853  *
16854  * Since: 1.4
16855  */
16856 ClutterAction *
clutter_actor_get_action(ClutterActor * self,const gchar * name)16857 clutter_actor_get_action (ClutterActor *self,
16858                           const gchar  *name)
16859 {
16860   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
16861   g_return_val_if_fail (name != NULL, NULL);
16862 
16863   if (self->priv->actions == NULL)
16864     return NULL;
16865 
16866   return CLUTTER_ACTION (_clutter_meta_group_get_meta (self->priv->actions, name));
16867 }
16868 
16869 /**
16870  * clutter_actor_clear_actions:
16871  * @self: a #ClutterActor
16872  *
16873  * Clears the list of actions applied to @self
16874  *
16875  * Since: 1.4
16876  */
16877 void
clutter_actor_clear_actions(ClutterActor * self)16878 clutter_actor_clear_actions (ClutterActor *self)
16879 {
16880   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16881 
16882   if (self->priv->actions == NULL)
16883     return;
16884 
16885   _clutter_meta_group_clear_metas_no_internal (self->priv->actions);
16886 }
16887 
16888 /**
16889  * clutter_actor_add_constraint:
16890  * @self: a #ClutterActor
16891  * @constraint: a #ClutterConstraint
16892  *
16893  * Adds @constraint to the list of #ClutterConstraint<!-- -->s applied
16894  * to @self
16895  *
16896  * The #ClutterActor will hold a reference on the @constraint until
16897  * either clutter_actor_remove_constraint() or
16898  * clutter_actor_clear_constraints() is called.
16899  *
16900  * Since: 1.4
16901  */
16902 void
clutter_actor_add_constraint(ClutterActor * self,ClutterConstraint * constraint)16903 clutter_actor_add_constraint (ClutterActor      *self,
16904                               ClutterConstraint *constraint)
16905 {
16906   ClutterActorPrivate *priv;
16907 
16908   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16909   g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
16910 
16911   priv = self->priv;
16912 
16913   if (priv->constraints == NULL)
16914     {
16915       priv->constraints = g_object_new (CLUTTER_TYPE_META_GROUP, NULL);
16916       priv->constraints->actor = self;
16917     }
16918 
16919   _clutter_meta_group_add_meta (priv->constraints,
16920                                 CLUTTER_ACTOR_META (constraint));
16921   clutter_actor_queue_relayout (self);
16922 
16923   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]);
16924 }
16925 
16926 /**
16927  * clutter_actor_add_constraint_with_name:
16928  * @self: a #ClutterActor
16929  * @name: the name to set on the constraint
16930  * @constraint: a #ClutterConstraint
16931  *
16932  * A convenience function for setting the name of a #ClutterConstraint
16933  * while adding it to the list of constraints applied to @self
16934  *
16935  * This function is the logical equivalent of:
16936  *
16937  * |[<!-- language="C" -->
16938  *   clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), name);
16939  *   clutter_actor_add_constraint (self, constraint);
16940  * ]|
16941  *
16942  * Since: 1.4
16943  */
16944 void
clutter_actor_add_constraint_with_name(ClutterActor * self,const gchar * name,ClutterConstraint * constraint)16945 clutter_actor_add_constraint_with_name (ClutterActor      *self,
16946                                         const gchar       *name,
16947                                         ClutterConstraint *constraint)
16948 {
16949   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16950   g_return_if_fail (name != NULL);
16951   g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
16952 
16953   clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), name);
16954   clutter_actor_add_constraint (self, constraint);
16955 }
16956 
16957 /**
16958  * clutter_actor_remove_constraint:
16959  * @self: a #ClutterActor
16960  * @constraint: a #ClutterConstraint
16961  *
16962  * Removes @constraint from the list of constraints applied to @self
16963  *
16964  * The reference held by @self on the #ClutterConstraint will be released
16965  *
16966  * Since: 1.4
16967  */
16968 void
clutter_actor_remove_constraint(ClutterActor * self,ClutterConstraint * constraint)16969 clutter_actor_remove_constraint (ClutterActor      *self,
16970                                  ClutterConstraint *constraint)
16971 {
16972   ClutterActorPrivate *priv;
16973 
16974   g_return_if_fail (CLUTTER_IS_ACTOR (self));
16975   g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint));
16976 
16977   priv = self->priv;
16978 
16979   if (priv->constraints == NULL)
16980     return;
16981 
16982   _clutter_meta_group_remove_meta (priv->constraints,
16983                                    CLUTTER_ACTOR_META (constraint));
16984 
16985   if (_clutter_meta_group_peek_metas (priv->constraints) == NULL)
16986     g_clear_object (&priv->constraints);
16987 
16988   clutter_actor_queue_relayout (self);
16989 
16990   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]);
16991 }
16992 
16993 /**
16994  * clutter_actor_remove_constraint_by_name:
16995  * @self: a #ClutterActor
16996  * @name: the name of the constraint to remove
16997  *
16998  * Removes the #ClutterConstraint with the given name from the list
16999  * of constraints applied to @self
17000  *
17001  * Since: 1.4
17002  */
17003 void
clutter_actor_remove_constraint_by_name(ClutterActor * self,const gchar * name)17004 clutter_actor_remove_constraint_by_name (ClutterActor *self,
17005                                          const gchar  *name)
17006 {
17007   ClutterActorPrivate *priv;
17008   ClutterActorMeta *meta;
17009 
17010   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17011   g_return_if_fail (name != NULL);
17012 
17013   priv = self->priv;
17014 
17015   if (priv->constraints == NULL)
17016     return;
17017 
17018   meta = _clutter_meta_group_get_meta (priv->constraints, name);
17019   if (meta == NULL)
17020     return;
17021 
17022   _clutter_meta_group_remove_meta (priv->constraints, meta);
17023   clutter_actor_queue_relayout (self);
17024 }
17025 
17026 /**
17027  * clutter_actor_get_constraints:
17028  * @self: a #ClutterActor
17029  *
17030  * Retrieves the list of constraints applied to @self
17031  *
17032  * Return value: (transfer container) (element-type Clutter.Constraint): a copy
17033  *   of the list of #ClutterConstraint<!-- -->s. The contents of the list are
17034  *   owned by the #ClutterActor. Use g_list_free() to free the resources
17035  *   allocated by the returned #GList
17036  *
17037  * Since: 1.4
17038  */
17039 GList *
clutter_actor_get_constraints(ClutterActor * self)17040 clutter_actor_get_constraints (ClutterActor *self)
17041 {
17042   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17043 
17044   if (self->priv->constraints == NULL)
17045     return NULL;
17046 
17047   return _clutter_meta_group_get_metas_no_internal (self->priv->constraints);
17048 }
17049 
17050 /**
17051  * clutter_actor_get_constraint:
17052  * @self: a #ClutterActor
17053  * @name: the name of the constraint to retrieve
17054  *
17055  * Retrieves the #ClutterConstraint with the given name in the list
17056  * of constraints applied to @self
17057  *
17058  * Return value: (transfer none): a #ClutterConstraint for the given
17059  *   name, or %NULL. The returned #ClutterConstraint is owned by the
17060  *   actor and it should not be unreferenced directly
17061  *
17062  * Since: 1.4
17063  */
17064 ClutterConstraint *
clutter_actor_get_constraint(ClutterActor * self,const gchar * name)17065 clutter_actor_get_constraint (ClutterActor *self,
17066                               const gchar  *name)
17067 {
17068   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17069   g_return_val_if_fail (name != NULL, NULL);
17070 
17071   if (self->priv->constraints == NULL)
17072     return NULL;
17073 
17074   return CLUTTER_CONSTRAINT (_clutter_meta_group_get_meta (self->priv->constraints, name));
17075 }
17076 
17077 /**
17078  * clutter_actor_clear_constraints:
17079  * @self: a #ClutterActor
17080  *
17081  * Clears the list of constraints applied to @self
17082  *
17083  * Since: 1.4
17084  */
17085 void
clutter_actor_clear_constraints(ClutterActor * self)17086 clutter_actor_clear_constraints (ClutterActor *self)
17087 {
17088   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17089 
17090   if (self->priv->constraints == NULL)
17091     return;
17092 
17093   _clutter_meta_group_clear_metas_no_internal (self->priv->constraints);
17094 
17095   clutter_actor_queue_relayout (self);
17096 }
17097 
17098 /**
17099  * clutter_actor_set_clip_to_allocation:
17100  * @self: a #ClutterActor
17101  * @clip_set: %TRUE to apply a clip tracking the allocation
17102  *
17103  * Sets whether @self should be clipped to the same size as its
17104  * allocation
17105  *
17106  * Since: 1.4
17107  */
17108 void
clutter_actor_set_clip_to_allocation(ClutterActor * self,gboolean clip_set)17109 clutter_actor_set_clip_to_allocation (ClutterActor *self,
17110                                       gboolean      clip_set)
17111 {
17112   ClutterActorPrivate *priv;
17113 
17114   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17115 
17116   clip_set = !!clip_set;
17117 
17118   priv = self->priv;
17119 
17120   if (priv->clip_to_allocation != clip_set)
17121     {
17122       priv->clip_to_allocation = clip_set;
17123 
17124       clutter_actor_queue_redraw (self);
17125 
17126       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CLIP_TO_ALLOCATION]);
17127       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]);
17128     }
17129 }
17130 
17131 /**
17132  * clutter_actor_get_clip_to_allocation:
17133  * @self: a #ClutterActor
17134  *
17135  * Retrieves the value set using clutter_actor_set_clip_to_allocation()
17136  *
17137  * Return value: %TRUE if the #ClutterActor is clipped to its allocation
17138  *
17139  * Since: 1.4
17140  */
17141 gboolean
clutter_actor_get_clip_to_allocation(ClutterActor * self)17142 clutter_actor_get_clip_to_allocation (ClutterActor *self)
17143 {
17144   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17145 
17146   return self->priv->clip_to_allocation;
17147 }
17148 
17149 /**
17150  * clutter_actor_add_effect:
17151  * @self: a #ClutterActor
17152  * @effect: a #ClutterEffect
17153  *
17154  * Adds @effect to the list of #ClutterEffect<!-- -->s applied to @self
17155  *
17156  * The #ClutterActor will hold a reference on the @effect until either
17157  * clutter_actor_remove_effect() or clutter_actor_clear_effects() is
17158  * called.
17159  *
17160  * Note that as #ClutterEffect is initially unowned,
17161  * clutter_actor_add_effect() will sink any floating reference on @effect.
17162  *
17163  * Since: 1.4
17164  */
17165 void
clutter_actor_add_effect(ClutterActor * self,ClutterEffect * effect)17166 clutter_actor_add_effect (ClutterActor  *self,
17167                           ClutterEffect *effect)
17168 {
17169   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17170   g_return_if_fail (CLUTTER_IS_EFFECT (effect));
17171 
17172   _clutter_actor_add_effect_internal (self, effect);
17173 
17174   clutter_actor_queue_redraw (self);
17175 
17176   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EFFECT]);
17177 }
17178 
17179 /**
17180  * clutter_actor_add_effect_with_name:
17181  * @self: a #ClutterActor
17182  * @name: the name to set on the effect
17183  * @effect: a #ClutterEffect
17184  *
17185  * A convenience function for setting the name of a #ClutterEffect
17186  * while adding it to the list of effects applied to @self.
17187  *
17188  * Note that as #ClutterEffect is initially unowned,
17189  * clutter_actor_add_effect_with_name() will sink any floating
17190  * reference on @effect.
17191  *
17192  * This function is the logical equivalent of:
17193  *
17194  * |[<!-- language="C" -->
17195  *   clutter_actor_meta_set_name (CLUTTER_ACTOR_META (effect), name);
17196  *   clutter_actor_add_effect (self, effect);
17197  * ]|
17198  *
17199  * Since: 1.4
17200  */
17201 void
clutter_actor_add_effect_with_name(ClutterActor * self,const gchar * name,ClutterEffect * effect)17202 clutter_actor_add_effect_with_name (ClutterActor  *self,
17203                                     const gchar   *name,
17204                                     ClutterEffect *effect)
17205 {
17206   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17207   g_return_if_fail (name != NULL);
17208   g_return_if_fail (CLUTTER_IS_EFFECT (effect));
17209 
17210   clutter_actor_meta_set_name (CLUTTER_ACTOR_META (effect), name);
17211   clutter_actor_add_effect (self, effect);
17212 }
17213 
17214 /**
17215  * clutter_actor_remove_effect:
17216  * @self: a #ClutterActor
17217  * @effect: a #ClutterEffect
17218  *
17219  * Removes @effect from the list of effects applied to @self
17220  *
17221  * The reference held by @self on the #ClutterEffect will be released
17222  *
17223  * Since: 1.4
17224  */
17225 void
clutter_actor_remove_effect(ClutterActor * self,ClutterEffect * effect)17226 clutter_actor_remove_effect (ClutterActor  *self,
17227                              ClutterEffect *effect)
17228 {
17229   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17230   g_return_if_fail (CLUTTER_IS_EFFECT (effect));
17231 
17232   _clutter_actor_remove_effect_internal (self, effect);
17233 
17234   clutter_actor_queue_redraw (self);
17235 
17236   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EFFECT]);
17237 }
17238 
17239 /**
17240  * clutter_actor_remove_effect_by_name:
17241  * @self: a #ClutterActor
17242  * @name: the name of the effect to remove
17243  *
17244  * Removes the #ClutterEffect with the given name from the list
17245  * of effects applied to @self
17246  *
17247  * Since: 1.4
17248  */
17249 void
clutter_actor_remove_effect_by_name(ClutterActor * self,const gchar * name)17250 clutter_actor_remove_effect_by_name (ClutterActor *self,
17251                                      const gchar  *name)
17252 {
17253   ClutterActorPrivate *priv;
17254   ClutterActorMeta *meta;
17255 
17256   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17257   g_return_if_fail (name != NULL);
17258 
17259   priv = self->priv;
17260 
17261   if (priv->effects == NULL)
17262     return;
17263 
17264   meta = _clutter_meta_group_get_meta (priv->effects, name);
17265   if (meta == NULL)
17266     return;
17267 
17268   clutter_actor_remove_effect (self, CLUTTER_EFFECT (meta));
17269 }
17270 
17271 /**
17272  * clutter_actor_get_effects:
17273  * @self: a #ClutterActor
17274  *
17275  * Retrieves the #ClutterEffect<!-- -->s applied on @self, if any
17276  *
17277  * Return value: (transfer container) (element-type Clutter.Effect): a list
17278  *   of #ClutterEffect<!-- -->s, or %NULL. The elements of the returned
17279  *   list are owned by Clutter and they should not be freed. You should
17280  *   free the returned list using g_list_free() when done
17281  *
17282  * Since: 1.4
17283  */
17284 GList *
clutter_actor_get_effects(ClutterActor * self)17285 clutter_actor_get_effects (ClutterActor *self)
17286 {
17287   ClutterActorPrivate *priv;
17288 
17289   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17290 
17291   priv = self->priv;
17292 
17293   if (priv->effects == NULL)
17294     return NULL;
17295 
17296   return _clutter_meta_group_get_metas_no_internal (priv->effects);
17297 }
17298 
17299 /**
17300  * clutter_actor_get_effect:
17301  * @self: a #ClutterActor
17302  * @name: the name of the effect to retrieve
17303  *
17304  * Retrieves the #ClutterEffect with the given name in the list
17305  * of effects applied to @self
17306  *
17307  * Return value: (transfer none): a #ClutterEffect for the given
17308  *   name, or %NULL. The returned #ClutterEffect is owned by the
17309  *   actor and it should not be unreferenced directly
17310  *
17311  * Since: 1.4
17312  */
17313 ClutterEffect *
clutter_actor_get_effect(ClutterActor * self,const gchar * name)17314 clutter_actor_get_effect (ClutterActor *self,
17315                           const gchar  *name)
17316 {
17317   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17318   g_return_val_if_fail (name != NULL, NULL);
17319 
17320   if (self->priv->effects == NULL)
17321     return NULL;
17322 
17323   return CLUTTER_EFFECT (_clutter_meta_group_get_meta (self->priv->effects, name));
17324 }
17325 
17326 /**
17327  * clutter_actor_clear_effects:
17328  * @self: a #ClutterActor
17329  *
17330  * Clears the list of effects applied to @self
17331  *
17332  * Since: 1.4
17333  */
17334 void
clutter_actor_clear_effects(ClutterActor * self)17335 clutter_actor_clear_effects (ClutterActor *self)
17336 {
17337   g_return_if_fail (CLUTTER_IS_ACTOR (self));
17338 
17339   if (self->priv->effects == NULL)
17340     return;
17341 
17342   _clutter_meta_group_clear_metas_no_internal (self->priv->effects);
17343 
17344   clutter_actor_queue_redraw (self);
17345 }
17346 
17347 /**
17348  * clutter_actor_has_key_focus:
17349  * @self: a #ClutterActor
17350  *
17351  * Checks whether @self is the #ClutterActor that has key focus
17352  *
17353  * Return value: %TRUE if the actor has key focus, and %FALSE otherwise
17354  *
17355  * Since: 1.4
17356  */
17357 gboolean
clutter_actor_has_key_focus(ClutterActor * self)17358 clutter_actor_has_key_focus (ClutterActor *self)
17359 {
17360   ClutterActor *stage;
17361 
17362   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17363 
17364   stage = _clutter_actor_get_stage_internal (self);
17365   if (stage == NULL)
17366     return FALSE;
17367 
17368   return clutter_stage_get_key_focus (CLUTTER_STAGE (stage)) == self;
17369 }
17370 
17371 static gboolean
_clutter_actor_get_paint_volume_real(ClutterActor * self,ClutterPaintVolume * pv)17372 _clutter_actor_get_paint_volume_real (ClutterActor *self,
17373                                       ClutterPaintVolume *pv)
17374 {
17375   ClutterActorPrivate *priv = self->priv;
17376 
17377   /* Actors are only expected to report a valid paint volume
17378    * while they have a valid allocation. */
17379   if (G_UNLIKELY (priv->needs_allocation))
17380     {
17381       CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
17382                     "Actor needs allocation",
17383                     _clutter_actor_get_debug_name (self));
17384       return FALSE;
17385     }
17386 
17387   /* Check if there are any handlers connected to the paint
17388    * signal. If there are then all bets are off for what the paint
17389    * volume for this actor might possibly be!
17390    *
17391    * XXX: It's expected that this is going to end up being quite a
17392    * costly check to have to do here, but we haven't come up with
17393    * another solution that can reliably catch paint signal handlers at
17394    * the right time to either avoid artefacts due to invalid stage
17395    * clipping or due to incorrect culling.
17396    *
17397    * Previously we checked in clutter_actor_paint(), but at that time
17398    * we may already be using a stage clip that could be derived from
17399    * an invalid paint-volume. We used to try and handle that by
17400    * queuing a follow up, unclipped, redraw but still the previous
17401    * checking wasn't enough to catch invalid volumes involved in
17402    * culling (considering that containers may derive their volume from
17403    * children that haven't yet been painted)
17404    *
17405    * Longer term, improved solutions could be:
17406    * - Disallow painting in the paint signal, only allow using it
17407    *   for tracking when paints happen. We can add another API that
17408    *   allows monkey patching the paint of arbitrary actors but in a
17409    *   more controlled way and that also supports modifying the
17410    *   paint-volume.
17411    * - If we could be notified somehow when signal handlers are
17412    *   connected we wouldn't have to poll for handlers like this.
17413    *
17414    * XXX:2.0 - Remove when we remove the paint signal
17415    */
17416   if (g_signal_has_handler_pending (self,
17417                                     actor_signals[PAINT],
17418                                     0,
17419                                     TRUE))
17420     {
17421       CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
17422                     "Actor has \"paint\" signal handlers",
17423                     _clutter_actor_get_debug_name (self));
17424       return FALSE;
17425     }
17426 
17427   _clutter_paint_volume_init_static (pv, self);
17428 
17429   if (!CLUTTER_ACTOR_GET_CLASS (self)->get_paint_volume (self, pv))
17430     {
17431       clutter_paint_volume_free (pv);
17432       CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
17433                     "Actor failed to report a volume",
17434                     _clutter_actor_get_debug_name (self));
17435       return FALSE;
17436     }
17437 
17438   /* since effects can modify the paint volume, we allow them to actually
17439    * do this by making get_paint_volume() "context sensitive"
17440    */
17441   if (priv->effects != NULL)
17442     {
17443       if (priv->current_effect != NULL)
17444         {
17445           const GList *effects, *l;
17446 
17447           /* if we are being called from within the paint sequence of
17448            * an actor, get the paint volume up to the current effect
17449            */
17450           effects = _clutter_meta_group_peek_metas (priv->effects);
17451           for (l = effects;
17452                l != NULL || (l != NULL && l->data != priv->current_effect);
17453                l = l->next)
17454             {
17455               if (!_clutter_effect_get_paint_volume (l->data, pv))
17456                 {
17457                   clutter_paint_volume_free (pv);
17458                   CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
17459                                 "Effect (%s) failed to report a volume",
17460                                 _clutter_actor_get_debug_name (self),
17461                                 _clutter_actor_meta_get_debug_name (l->data));
17462                   return FALSE;
17463                 }
17464             }
17465         }
17466       else
17467         {
17468           const GList *effects, *l;
17469 
17470           /* otherwise, get the cumulative volume */
17471           effects = _clutter_meta_group_peek_metas (priv->effects);
17472           for (l = effects; l != NULL; l = l->next)
17473             if (!_clutter_effect_get_paint_volume (l->data, pv))
17474               {
17475                 clutter_paint_volume_free (pv);
17476                 CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): "
17477                               "Effect (%s) failed to report a volume",
17478                               _clutter_actor_get_debug_name (self),
17479                               _clutter_actor_meta_get_debug_name (l->data));
17480                 return FALSE;
17481               }
17482         }
17483     }
17484 
17485   return TRUE;
17486 }
17487 
17488 /* The public clutter_actor_get_paint_volume API returns a const
17489  * pointer since we return a pointer directly to the cached
17490  * PaintVolume associated with the actor and don't want the user to
17491  * inadvertently modify it, but for internal uses we sometimes need
17492  * access to the same PaintVolume but need to apply some book-keeping
17493  * modifications to it so we don't want a const pointer.
17494  */
17495 static ClutterPaintVolume *
_clutter_actor_get_paint_volume_mutable(ClutterActor * self)17496 _clutter_actor_get_paint_volume_mutable (ClutterActor *self)
17497 {
17498   ClutterActorPrivate *priv;
17499 
17500   priv = self->priv;
17501 
17502   if (priv->paint_volume_valid)
17503     clutter_paint_volume_free (&priv->paint_volume);
17504 
17505   if (_clutter_actor_get_paint_volume_real (self, &priv->paint_volume))
17506     {
17507       priv->paint_volume_valid = TRUE;
17508       return &priv->paint_volume;
17509     }
17510   else
17511     {
17512       priv->paint_volume_valid = FALSE;
17513       return NULL;
17514     }
17515 }
17516 
17517 /**
17518  * clutter_actor_get_paint_volume:
17519  * @self: a #ClutterActor
17520  *
17521  * Retrieves the paint volume of the passed #ClutterActor, or %NULL
17522  * when a paint volume can't be determined.
17523  *
17524  * The paint volume is defined as the 3D space occupied by an actor
17525  * when being painted.
17526  *
17527  * This function will call the #ClutterActorClass.get_paint_volume()
17528  * virtual function of the #ClutterActor class. Sub-classes of #ClutterActor
17529  * should not usually care about overriding the default implementation,
17530  * unless they are, for instance: painting outside their allocation, or
17531  * actors with a depth factor (not in terms of #ClutterActor:depth but real
17532  * 3D depth).
17533  *
17534  * Note: 2D actors overriding #ClutterActorClass.get_paint_volume()
17535  * should ensure that their volume has a depth of 0. (This will be true
17536  * as long as you don't call clutter_paint_volume_set_depth().)
17537  *
17538  * Return value: (transfer none): a pointer to a #ClutterPaintVolume,
17539  *   or %NULL if no volume could be determined. The returned pointer
17540  *   is not guaranteed to be valid across multiple frames; if you want
17541  *   to keep it, you will need to copy it using clutter_paint_volume_copy().
17542  *
17543  * Since: 1.6
17544  */
17545 const ClutterPaintVolume *
clutter_actor_get_paint_volume(ClutterActor * self)17546 clutter_actor_get_paint_volume (ClutterActor *self)
17547 {
17548   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17549 
17550   return _clutter_actor_get_paint_volume_mutable (self);
17551 }
17552 
17553 /**
17554  * clutter_actor_get_transformed_paint_volume:
17555  * @self: a #ClutterActor
17556  * @relative_to_ancestor: A #ClutterActor that is an ancestor of @self
17557  *    (or %NULL for the stage)
17558  *
17559  * Retrieves the 3D paint volume of an actor like
17560  * clutter_actor_get_paint_volume() does (Please refer to the
17561  * documentation of clutter_actor_get_paint_volume() for more
17562  * details.) and it additionally transforms the paint volume into the
17563  * coordinate space of @relative_to_ancestor. (Or the stage if %NULL
17564  * is passed for @relative_to_ancestor)
17565  *
17566  * This can be used by containers that base their paint volume on
17567  * the volume of their children. Such containers can query the
17568  * transformed paint volume of all of its children and union them
17569  * together using clutter_paint_volume_union().
17570  *
17571  * Return value: (transfer none): a pointer to a #ClutterPaintVolume,
17572  *   or %NULL if no volume could be determined. The returned pointer is
17573  *   not guaranteed to be valid across multiple frames; if you wish to
17574  *   keep it, you will have to copy it using clutter_paint_volume_copy().
17575  *
17576  * Since: 1.6
17577  */
17578 const ClutterPaintVolume *
clutter_actor_get_transformed_paint_volume(ClutterActor * self,ClutterActor * relative_to_ancestor)17579 clutter_actor_get_transformed_paint_volume (ClutterActor *self,
17580                                             ClutterActor *relative_to_ancestor)
17581 {
17582   const ClutterPaintVolume *volume;
17583   ClutterActor *stage;
17584   ClutterPaintVolume *transformed_volume;
17585 
17586   stage = _clutter_actor_get_stage_internal (self);
17587   if (G_UNLIKELY (stage == NULL))
17588     return NULL;
17589 
17590   if (relative_to_ancestor == NULL)
17591     relative_to_ancestor = stage;
17592 
17593   volume = clutter_actor_get_paint_volume (self);
17594   if (volume == NULL)
17595     return NULL;
17596 
17597   transformed_volume =
17598     _clutter_stage_paint_volume_stack_allocate (CLUTTER_STAGE (stage));
17599 
17600   _clutter_paint_volume_copy_static (volume, transformed_volume);
17601 
17602   _clutter_paint_volume_transform_relative (transformed_volume,
17603                                             relative_to_ancestor);
17604 
17605   return transformed_volume;
17606 }
17607 
17608 /**
17609  * clutter_actor_get_paint_box:
17610  * @self: a #ClutterActor
17611  * @box: (out): return location for a #ClutterActorBox
17612  *
17613  * Retrieves the paint volume of the passed #ClutterActor, and
17614  * transforms it into a 2D bounding box in stage coordinates.
17615  *
17616  * This function is useful to determine the on screen area occupied by
17617  * the actor. The box is only an approximation and may often be
17618  * considerably larger due to the optimizations used to calculate the
17619  * box. The box is never smaller though, so it can reliably be used
17620  * for culling.
17621  *
17622  * There are times when a 2D paint box can't be determined, e.g.
17623  * because the actor isn't yet parented under a stage or because
17624  * the actor is unable to determine a paint volume.
17625  *
17626  * Return value: %TRUE if a 2D paint box could be determined, else
17627  * %FALSE.
17628  *
17629  * Since: 1.6
17630  */
17631 gboolean
clutter_actor_get_paint_box(ClutterActor * self,ClutterActorBox * box)17632 clutter_actor_get_paint_box (ClutterActor    *self,
17633                              ClutterActorBox *box)
17634 {
17635   ClutterActor *stage;
17636   ClutterPaintVolume *pv;
17637 
17638   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17639   g_return_val_if_fail (box != NULL, FALSE);
17640 
17641   stage = _clutter_actor_get_stage_internal (self);
17642   if (G_UNLIKELY (!stage))
17643     return FALSE;
17644 
17645   pv = _clutter_actor_get_paint_volume_mutable (self);
17646   if (G_UNLIKELY (!pv))
17647     return FALSE;
17648 
17649   _clutter_paint_volume_get_stage_paint_box (pv, CLUTTER_STAGE (stage), box);
17650 
17651   return TRUE;
17652 }
17653 
17654 /**
17655  * clutter_actor_has_overlaps:
17656  * @self: A #ClutterActor
17657  *
17658  * Asks the actor's implementation whether it may contain overlapping
17659  * primitives.
17660  *
17661  * For example; Clutter may use this to determine whether the painting
17662  * should be redirected to an offscreen buffer to correctly implement
17663  * the opacity property.
17664  *
17665  * Custom actors can override the default response by implementing the
17666  * #ClutterActorClass.has_overlaps() virtual function. See
17667  * clutter_actor_set_offscreen_redirect() for more information.
17668  *
17669  * Return value: %TRUE if the actor may have overlapping primitives, and
17670  *   %FALSE otherwise
17671  *
17672  * Since: 1.8
17673  */
17674 gboolean
clutter_actor_has_overlaps(ClutterActor * self)17675 clutter_actor_has_overlaps (ClutterActor *self)
17676 {
17677   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), TRUE);
17678 
17679   return CLUTTER_ACTOR_GET_CLASS (self)->has_overlaps (self);
17680 }
17681 
17682 /**
17683  * clutter_actor_has_effects:
17684  * @self: A #ClutterActor
17685  *
17686  * Returns whether the actor has any effects applied.
17687  *
17688  * Return value: %TRUE if the actor has any effects,
17689  *   %FALSE otherwise
17690  *
17691  * Since: 1.10
17692  */
17693 gboolean
clutter_actor_has_effects(ClutterActor * self)17694 clutter_actor_has_effects (ClutterActor *self)
17695 {
17696   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17697 
17698   if (self->priv->effects == NULL)
17699     return FALSE;
17700 
17701   return _clutter_meta_group_has_metas_no_internal (self->priv->effects);
17702 }
17703 
17704 /**
17705  * clutter_actor_has_constraints:
17706  * @self: A #ClutterActor
17707  *
17708  * Returns whether the actor has any constraints applied.
17709  *
17710  * Return value: %TRUE if the actor has any constraints,
17711  *   %FALSE otherwise
17712  *
17713  * Since: 1.10
17714  */
17715 gboolean
clutter_actor_has_constraints(ClutterActor * self)17716 clutter_actor_has_constraints (ClutterActor *self)
17717 {
17718   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17719 
17720   if (self->priv->constraints == NULL)
17721     return FALSE;
17722 
17723   return _clutter_meta_group_has_metas_no_internal (self->priv->constraints);
17724 }
17725 
17726 /**
17727  * clutter_actor_has_actions:
17728  * @self: A #ClutterActor
17729  *
17730  * Returns whether the actor has any actions applied.
17731  *
17732  * Return value: %TRUE if the actor has any actions,
17733  *   %FALSE otherwise
17734  *
17735  * Since: 1.10
17736  */
17737 gboolean
clutter_actor_has_actions(ClutterActor * self)17738 clutter_actor_has_actions (ClutterActor *self)
17739 {
17740   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17741 
17742   if (self->priv->actions == NULL)
17743     return FALSE;
17744 
17745   return _clutter_meta_group_has_metas_no_internal (self->priv->actions);
17746 }
17747 
17748 /**
17749  * clutter_actor_get_n_children:
17750  * @self: a #ClutterActor
17751  *
17752  * Retrieves the number of children of @self.
17753  *
17754  * Return value: the number of children of an actor
17755  *
17756  * Since: 1.10
17757  */
17758 gint
clutter_actor_get_n_children(ClutterActor * self)17759 clutter_actor_get_n_children (ClutterActor *self)
17760 {
17761   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
17762 
17763   return self->priv->n_children;
17764 }
17765 
17766 /**
17767  * clutter_actor_get_child_at_index:
17768  * @self: a #ClutterActor
17769  * @index_: the position in the list of children
17770  *
17771  * Retrieves the actor at the given @index_ inside the list of
17772  * children of @self.
17773  *
17774  * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
17775  *
17776  * Since: 1.10
17777  */
17778 ClutterActor *
clutter_actor_get_child_at_index(ClutterActor * self,gint index_)17779 clutter_actor_get_child_at_index (ClutterActor *self,
17780                                   gint          index_)
17781 {
17782   ClutterActor *iter;
17783   int i;
17784 
17785   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
17786   g_return_val_if_fail (index_ <= self->priv->n_children, NULL);
17787 
17788   for (iter = self->priv->first_child, i = 0;
17789        iter != NULL && i < index_;
17790        iter = iter->priv->next_sibling, i += 1)
17791     ;
17792 
17793   return iter;
17794 }
17795 
17796 /*< private >
17797  * _clutter_actor_foreach_child:
17798  * @actor: The actor whos children you want to iterate
17799  * @callback: The function to call for each child
17800  * @user_data: Private data to pass to @callback
17801  *
17802  * Calls a given @callback once for each child of the specified @actor and
17803  * passing the @user_data pointer each time.
17804  *
17805  * Return value: returns %TRUE if all children were iterated, else
17806  *    %FALSE if a callback broke out of iteration early.
17807  */
17808 gboolean
_clutter_actor_foreach_child(ClutterActor * self,ClutterForeachCallback callback,gpointer user_data)17809 _clutter_actor_foreach_child (ClutterActor           *self,
17810                               ClutterForeachCallback  callback,
17811                               gpointer                user_data)
17812 {
17813   ClutterActor *iter;
17814   gboolean cont;
17815 
17816   if (self->priv->first_child == NULL)
17817     return TRUE;
17818 
17819   cont = TRUE;
17820   iter = self->priv->first_child;
17821 
17822   /* we use this form so that it's safe to change the children
17823    * list while iterating it
17824    */
17825   while (cont && iter != NULL)
17826     {
17827       ClutterActor *next = iter->priv->next_sibling;
17828 
17829       cont = callback (iter, user_data);
17830 
17831       iter = next;
17832     }
17833 
17834   return cont;
17835 }
17836 
17837 #if 0
17838 /* For debugging purposes this gives us a simple way to print out
17839  * the scenegraph e.g in gdb using:
17840  * [|
17841  *   _clutter_actor_traverse (stage,
17842  *                            0,
17843  *                            clutter_debug_print_actor_cb,
17844  *                            NULL,
17845  *                            NULL);
17846  * |]
17847  */
17848 static ClutterActorTraverseVisitFlags
17849 clutter_debug_print_actor_cb (ClutterActor *actor,
17850                               int depth,
17851                               void *user_data)
17852 {
17853   g_print ("%*s%s:%p\n",
17854            depth * 2, "",
17855            _clutter_actor_get_debug_name (actor),
17856            actor);
17857 
17858   return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
17859 }
17860 #endif
17861 
17862 static void
_clutter_actor_traverse_breadth(ClutterActor * actor,ClutterTraverseCallback callback,gpointer user_data)17863 _clutter_actor_traverse_breadth (ClutterActor           *actor,
17864                                  ClutterTraverseCallback callback,
17865                                  gpointer                user_data)
17866 {
17867   GQueue *queue = g_queue_new ();
17868   ClutterActor dummy;
17869   int current_depth = 0;
17870 
17871   g_queue_push_tail (queue, actor);
17872   g_queue_push_tail (queue, &dummy); /* use to delimit depth changes */
17873 
17874   while ((actor = g_queue_pop_head (queue)))
17875     {
17876       ClutterActorTraverseVisitFlags flags;
17877 
17878       if (actor == &dummy)
17879         {
17880           current_depth++;
17881           g_queue_push_tail (queue, &dummy);
17882           continue;
17883         }
17884 
17885       flags = callback (actor, current_depth, user_data);
17886       if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
17887         break;
17888       else if (!(flags & CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN))
17889         {
17890           ClutterActor *iter;
17891 
17892           for (iter = actor->priv->first_child;
17893                iter != NULL;
17894                iter = iter->priv->next_sibling)
17895             {
17896               g_queue_push_tail (queue, iter);
17897             }
17898         }
17899     }
17900 
17901   g_queue_free (queue);
17902 }
17903 
17904 static ClutterActorTraverseVisitFlags
_clutter_actor_traverse_depth(ClutterActor * actor,ClutterTraverseCallback before_children_callback,ClutterTraverseCallback after_children_callback,int current_depth,gpointer user_data)17905 _clutter_actor_traverse_depth (ClutterActor           *actor,
17906                                ClutterTraverseCallback before_children_callback,
17907                                ClutterTraverseCallback after_children_callback,
17908                                int                     current_depth,
17909                                gpointer                user_data)
17910 {
17911   ClutterActorTraverseVisitFlags flags;
17912 
17913   flags = before_children_callback (actor, current_depth, user_data);
17914   if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
17915     return CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK;
17916 
17917   if (!(flags & CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN))
17918     {
17919       ClutterActor *iter;
17920 
17921       for (iter = actor->priv->first_child;
17922            iter != NULL;
17923            iter = iter->priv->next_sibling)
17924         {
17925           flags = _clutter_actor_traverse_depth (iter,
17926                                                  before_children_callback,
17927                                                  after_children_callback,
17928                                                  current_depth + 1,
17929                                                  user_data);
17930 
17931           if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
17932             return CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK;
17933         }
17934     }
17935 
17936   if (after_children_callback)
17937     return after_children_callback (actor, current_depth, user_data);
17938   else
17939     return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
17940 }
17941 
17942 /* _clutter_actor_traverse:
17943  * @actor: The actor to start traversing the graph from
17944  * @flags: These flags may affect how the traversal is done
17945  * @before_children_callback: A function to call before visiting the
17946  *   children of the current actor.
17947  * @after_children_callback: A function to call after visiting the
17948  *   children of the current actor. (Ignored if
17949  *   %CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST is passed to @flags.)
17950  * @user_data: The private data to pass to the callbacks
17951  *
17952  * Traverses the scenegraph starting at the specified @actor and
17953  * descending through all its children and its children's children.
17954  * For each actor traversed @before_children_callback and
17955  * @after_children_callback are called with the specified
17956  * @user_data, before and after visiting that actor's children.
17957  *
17958  * The callbacks can return flags that affect the ongoing traversal
17959  * such as by skipping over an actors children or bailing out of
17960  * any further traversing.
17961  */
17962 void
_clutter_actor_traverse(ClutterActor * actor,ClutterActorTraverseFlags flags,ClutterTraverseCallback before_children_callback,ClutterTraverseCallback after_children_callback,gpointer user_data)17963 _clutter_actor_traverse (ClutterActor              *actor,
17964                          ClutterActorTraverseFlags  flags,
17965                          ClutterTraverseCallback    before_children_callback,
17966                          ClutterTraverseCallback    after_children_callback,
17967                          gpointer                   user_data)
17968 {
17969   if (flags & CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST)
17970     _clutter_actor_traverse_breadth (actor,
17971                                      before_children_callback,
17972                                      user_data);
17973   else /* DEPTH_FIRST */
17974     _clutter_actor_traverse_depth (actor,
17975                                    before_children_callback,
17976                                    after_children_callback,
17977                                    0, /* start depth */
17978                                    user_data);
17979 }
17980 
17981 static void
on_layout_manager_changed(ClutterLayoutManager * manager,ClutterActor * self)17982 on_layout_manager_changed (ClutterLayoutManager *manager,
17983                            ClutterActor         *self)
17984 {
17985   clutter_actor_queue_relayout (self);
17986 }
17987 
17988 /**
17989  * clutter_actor_set_layout_manager:
17990  * @self: a #ClutterActor
17991  * @manager: (allow-none): a #ClutterLayoutManager, or %NULL to unset it
17992  *
17993  * Sets the #ClutterLayoutManager delegate object that will be used to
17994  * lay out the children of @self.
17995  *
17996  * The #ClutterActor will take a reference on the passed @manager which
17997  * will be released either when the layout manager is removed, or when
17998  * the actor is destroyed.
17999  *
18000  * Since: 1.10
18001  */
18002 void
clutter_actor_set_layout_manager(ClutterActor * self,ClutterLayoutManager * manager)18003 clutter_actor_set_layout_manager (ClutterActor         *self,
18004                                   ClutterLayoutManager *manager)
18005 {
18006   ClutterActorPrivate *priv;
18007 
18008   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18009   g_return_if_fail (manager == NULL || CLUTTER_IS_LAYOUT_MANAGER (manager));
18010 
18011   priv = self->priv;
18012 
18013   if (priv->layout_manager != NULL)
18014     {
18015       g_signal_handlers_disconnect_by_func (priv->layout_manager,
18016                                             G_CALLBACK (on_layout_manager_changed),
18017                                             self);
18018       clutter_layout_manager_set_container (priv->layout_manager, NULL);
18019       g_clear_object (&priv->layout_manager);
18020     }
18021 
18022   priv->layout_manager = manager;
18023 
18024   if (priv->layout_manager != NULL)
18025     {
18026       g_object_ref_sink (priv->layout_manager);
18027       clutter_layout_manager_set_container (priv->layout_manager,
18028                                             CLUTTER_CONTAINER (self));
18029       g_signal_connect (priv->layout_manager, "layout-changed",
18030                         G_CALLBACK (on_layout_manager_changed),
18031                         self);
18032     }
18033 
18034   clutter_actor_queue_relayout (self);
18035 
18036   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAYOUT_MANAGER]);
18037 }
18038 
18039 /**
18040  * clutter_actor_get_layout_manager:
18041  * @self: a #ClutterActor
18042  *
18043  * Retrieves the #ClutterLayoutManager used by @self.
18044  *
18045  * Return value: (transfer none): a pointer to the #ClutterLayoutManager,
18046  *   or %NULL
18047  *
18048  * Since: 1.10
18049  */
18050 ClutterLayoutManager *
clutter_actor_get_layout_manager(ClutterActor * self)18051 clutter_actor_get_layout_manager (ClutterActor *self)
18052 {
18053   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18054 
18055   return self->priv->layout_manager;
18056 }
18057 
18058 static const ClutterLayoutInfo default_layout_info = {
18059   CLUTTER_POINT_INIT_ZERO,      /* fixed-pos */
18060   { 0, 0, 0, 0 },               /* margin */
18061   CLUTTER_ACTOR_ALIGN_FILL,     /* x-align */
18062   CLUTTER_ACTOR_ALIGN_FILL,     /* y-align */
18063   FALSE, FALSE,                 /* expand */
18064   CLUTTER_SIZE_INIT_ZERO,       /* minimum */
18065   CLUTTER_SIZE_INIT_ZERO,       /* natural */
18066 };
18067 
18068 static void
layout_info_free(gpointer data)18069 layout_info_free (gpointer data)
18070 {
18071   if (G_LIKELY (data != NULL))
18072     g_slice_free (ClutterLayoutInfo, data);
18073 }
18074 
18075 /*< private >
18076  * _clutter_actor_peek_layout_info:
18077  * @self: a #ClutterActor
18078  *
18079  * Retrieves a pointer to the ClutterLayoutInfo structure.
18080  *
18081  * If the actor does not have a ClutterLayoutInfo associated to it, %NULL is returned.
18082  *
18083  * Return value: (transfer none): a pointer to the ClutterLayoutInfo structure
18084  */
18085 ClutterLayoutInfo *
_clutter_actor_peek_layout_info(ClutterActor * self)18086 _clutter_actor_peek_layout_info (ClutterActor *self)
18087 {
18088   return g_object_get_qdata (G_OBJECT (self), quark_actor_layout_info);
18089 }
18090 
18091 /*< private >
18092  * _clutter_actor_get_layout_info:
18093  * @self: a #ClutterActor
18094  *
18095  * Retrieves a pointer to the ClutterLayoutInfo structure.
18096  *
18097  * If the actor does not have a ClutterLayoutInfo associated to it, one
18098  * will be created and initialized to the default values.
18099  *
18100  * This function should be used for setters.
18101  *
18102  * For getters, you should use _clutter_actor_get_layout_info_or_defaults()
18103  * instead.
18104  *
18105  * Return value: (transfer none): a pointer to the ClutterLayoutInfo structure
18106  */
18107 ClutterLayoutInfo *
_clutter_actor_get_layout_info(ClutterActor * self)18108 _clutter_actor_get_layout_info (ClutterActor *self)
18109 {
18110   ClutterLayoutInfo *retval;
18111 
18112   retval = _clutter_actor_peek_layout_info (self);
18113   if (retval == NULL)
18114     {
18115       retval = g_slice_new (ClutterLayoutInfo);
18116 
18117       *retval = default_layout_info;
18118 
18119       g_object_set_qdata_full (G_OBJECT (self), quark_actor_layout_info,
18120                                retval,
18121                                layout_info_free);
18122     }
18123 
18124   return retval;
18125 }
18126 
18127 /*< private >
18128  * _clutter_actor_get_layout_info_or_defaults:
18129  * @self: a #ClutterActor
18130  *
18131  * Retrieves the ClutterLayoutInfo structure associated to an actor.
18132  *
18133  * If the actor does not have a ClutterLayoutInfo structure associated to it,
18134  * then the default structure will be returned.
18135  *
18136  * This function should only be used for getters.
18137  *
18138  * Return value: a const pointer to the ClutterLayoutInfo structure
18139  */
18140 const ClutterLayoutInfo *
_clutter_actor_get_layout_info_or_defaults(ClutterActor * self)18141 _clutter_actor_get_layout_info_or_defaults (ClutterActor *self)
18142 {
18143   const ClutterLayoutInfo *info;
18144 
18145   info = _clutter_actor_peek_layout_info (self);
18146   if (info == NULL)
18147     return &default_layout_info;
18148 
18149   return info;
18150 }
18151 
18152 /**
18153  * clutter_actor_set_x_align:
18154  * @self: a #ClutterActor
18155  * @x_align: the horizontal alignment policy
18156  *
18157  * Sets the horizontal alignment policy of a #ClutterActor, in case the
18158  * actor received extra horizontal space.
18159  *
18160  * See also the #ClutterActor:x-align property.
18161  *
18162  * Since: 1.10
18163  */
18164 void
clutter_actor_set_x_align(ClutterActor * self,ClutterActorAlign x_align)18165 clutter_actor_set_x_align (ClutterActor      *self,
18166                            ClutterActorAlign  x_align)
18167 {
18168   ClutterLayoutInfo *info;
18169 
18170   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18171 
18172   info = _clutter_actor_get_layout_info (self);
18173 
18174   if (info->x_align != x_align)
18175     {
18176       info->x_align = x_align;
18177 
18178       clutter_actor_queue_relayout (self);
18179 
18180       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_X_ALIGN]);
18181     }
18182 }
18183 
18184 /**
18185  * clutter_actor_get_x_align:
18186  * @self: a #ClutterActor
18187  *
18188  * Retrieves the horizontal alignment policy set using
18189  * clutter_actor_set_x_align().
18190  *
18191  * Return value: the horizontal alignment policy.
18192  *
18193  * Since: 1.10
18194  */
18195 ClutterActorAlign
clutter_actor_get_x_align(ClutterActor * self)18196 clutter_actor_get_x_align (ClutterActor *self)
18197 {
18198   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_ACTOR_ALIGN_FILL);
18199 
18200   return _clutter_actor_get_layout_info_or_defaults (self)->x_align;
18201 }
18202 
18203 /**
18204  * clutter_actor_set_y_align:
18205  * @self: a #ClutterActor
18206  * @y_align: the vertical alignment policy
18207  *
18208  * Sets the vertical alignment policy of a #ClutterActor, in case the
18209  * actor received extra vertical space.
18210  *
18211  * See also the #ClutterActor:y-align property.
18212  *
18213  * Since: 1.10
18214  */
18215 void
clutter_actor_set_y_align(ClutterActor * self,ClutterActorAlign y_align)18216 clutter_actor_set_y_align (ClutterActor      *self,
18217                            ClutterActorAlign  y_align)
18218 {
18219   ClutterLayoutInfo *info;
18220 
18221   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18222 
18223   info = _clutter_actor_get_layout_info (self);
18224 
18225   if (info->y_align != y_align)
18226     {
18227       info->y_align = y_align;
18228 
18229       clutter_actor_queue_relayout (self);
18230 
18231       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_Y_ALIGN]);
18232     }
18233 }
18234 
18235 /**
18236  * clutter_actor_get_y_align:
18237  * @self: a #ClutterActor
18238  *
18239  * Retrieves the vertical alignment policy set using
18240  * clutter_actor_set_y_align().
18241  *
18242  * Return value: the vertical alignment policy.
18243  *
18244  * Since: 1.10
18245  */
18246 ClutterActorAlign
clutter_actor_get_y_align(ClutterActor * self)18247 clutter_actor_get_y_align (ClutterActor *self)
18248 {
18249   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_ACTOR_ALIGN_FILL);
18250 
18251   return _clutter_actor_get_layout_info_or_defaults (self)->y_align;
18252 }
18253 
18254 static inline void
clutter_actor_set_margin_internal(ClutterActor * self,gfloat margin,GParamSpec * pspec)18255 clutter_actor_set_margin_internal (ClutterActor *self,
18256                                    gfloat        margin,
18257                                    GParamSpec   *pspec)
18258 {
18259   ClutterLayoutInfo *info;
18260 
18261   info = _clutter_actor_get_layout_info (self);
18262 
18263   if (pspec == obj_props[PROP_MARGIN_TOP])
18264     info->margin.top = margin;
18265   else if (pspec == obj_props[PROP_MARGIN_RIGHT])
18266     info->margin.right = margin;
18267   else if (pspec == obj_props[PROP_MARGIN_BOTTOM])
18268     info->margin.bottom = margin;
18269   else
18270     info->margin.left = margin;
18271 
18272   clutter_actor_queue_relayout (self);
18273   g_object_notify_by_pspec (G_OBJECT (self), pspec);
18274 }
18275 
18276 /**
18277  * clutter_actor_set_margin:
18278  * @self: a #ClutterActor
18279  * @margin: a #ClutterMargin
18280  *
18281  * Sets all the components of the margin of a #ClutterActor.
18282  *
18283  * Since: 1.10
18284  */
18285 void
clutter_actor_set_margin(ClutterActor * self,const ClutterMargin * margin)18286 clutter_actor_set_margin (ClutterActor        *self,
18287                           const ClutterMargin *margin)
18288 {
18289   ClutterLayoutInfo *info;
18290 
18291   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18292   g_return_if_fail (margin != NULL);
18293 
18294   info = _clutter_actor_get_layout_info (self);
18295 
18296   if (info->margin.top != margin->top)
18297     clutter_actor_set_margin_top (self, margin->top);
18298 
18299   if (info->margin.right != margin->right)
18300     clutter_actor_set_margin_right (self, margin->right);
18301 
18302   if (info->margin.bottom != margin->bottom)
18303     clutter_actor_set_margin_bottom (self, margin->bottom);
18304 
18305   if (info->margin.left != margin->left)
18306     clutter_actor_set_margin_left (self, margin->left);
18307 }
18308 
18309 /**
18310  * clutter_actor_get_margin:
18311  * @self: a #ClutterActor
18312  * @margin: (out caller-allocates): return location for a #ClutterMargin
18313  *
18314  * Retrieves all the components of the margin of a #ClutterActor.
18315  *
18316  * Since: 1.10
18317  */
18318 void
clutter_actor_get_margin(ClutterActor * self,ClutterMargin * margin)18319 clutter_actor_get_margin (ClutterActor  *self,
18320                           ClutterMargin *margin)
18321 {
18322   const ClutterLayoutInfo *info;
18323 
18324   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18325   g_return_if_fail (margin != NULL);
18326 
18327   info = _clutter_actor_get_layout_info_or_defaults (self);
18328 
18329   *margin = info->margin;
18330 }
18331 
18332 /**
18333  * clutter_actor_set_margin_top:
18334  * @self: a #ClutterActor
18335  * @margin: the top margin
18336  *
18337  * Sets the margin from the top of a #ClutterActor.
18338  *
18339  * The #ClutterActor:margin-top property is animatable.
18340  *
18341  * Since: 1.10
18342  */
18343 void
clutter_actor_set_margin_top(ClutterActor * self,gfloat margin)18344 clutter_actor_set_margin_top (ClutterActor *self,
18345                               gfloat        margin)
18346 {
18347   const ClutterLayoutInfo *info;
18348 
18349   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18350   g_return_if_fail (margin >= 0.f);
18351 
18352   info = _clutter_actor_get_layout_info_or_defaults (self);
18353   _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_TOP],
18354                                     info->margin.top,
18355                                     margin);
18356 }
18357 
18358 /**
18359  * clutter_actor_get_margin_top:
18360  * @self: a #ClutterActor
18361  *
18362  * Retrieves the top margin of a #ClutterActor.
18363  *
18364  * Return value: the top margin
18365  *
18366  * Since: 1.10
18367  */
18368 gfloat
clutter_actor_get_margin_top(ClutterActor * self)18369 clutter_actor_get_margin_top (ClutterActor *self)
18370 {
18371   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
18372 
18373   return _clutter_actor_get_layout_info_or_defaults (self)->margin.top;
18374 }
18375 
18376 /**
18377  * clutter_actor_set_margin_bottom:
18378  * @self: a #ClutterActor
18379  * @margin: the bottom margin
18380  *
18381  * Sets the margin from the bottom of a #ClutterActor.
18382  *
18383  * The #ClutterActor:margin-bottom property is animatable.
18384  *
18385  * Since: 1.10
18386  */
18387 void
clutter_actor_set_margin_bottom(ClutterActor * self,gfloat margin)18388 clutter_actor_set_margin_bottom (ClutterActor *self,
18389                                  gfloat        margin)
18390 {
18391   const ClutterLayoutInfo *info;
18392 
18393   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18394   g_return_if_fail (margin >= 0.f);
18395 
18396   info = _clutter_actor_get_layout_info_or_defaults (self);
18397   _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_BOTTOM],
18398                                     info->margin.bottom,
18399                                     margin);
18400 }
18401 
18402 /**
18403  * clutter_actor_get_margin_bottom:
18404  * @self: a #ClutterActor
18405  *
18406  * Retrieves the bottom margin of a #ClutterActor.
18407  *
18408  * Return value: the bottom margin
18409  *
18410  * Since: 1.10
18411  */
18412 gfloat
clutter_actor_get_margin_bottom(ClutterActor * self)18413 clutter_actor_get_margin_bottom (ClutterActor *self)
18414 {
18415   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
18416 
18417   return _clutter_actor_get_layout_info_or_defaults (self)->margin.bottom;
18418 }
18419 
18420 /**
18421  * clutter_actor_set_margin_left:
18422  * @self: a #ClutterActor
18423  * @margin: the left margin
18424  *
18425  * Sets the margin from the left of a #ClutterActor.
18426  *
18427  * The #ClutterActor:margin-left property is animatable.
18428  *
18429  * Since: 1.10
18430  */
18431 void
clutter_actor_set_margin_left(ClutterActor * self,gfloat margin)18432 clutter_actor_set_margin_left (ClutterActor *self,
18433                                gfloat        margin)
18434 {
18435   const ClutterLayoutInfo *info;
18436 
18437   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18438   g_return_if_fail (margin >= 0.f);
18439 
18440   info = _clutter_actor_get_layout_info_or_defaults (self);
18441   _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_LEFT],
18442                                     info->margin.left,
18443                                     margin);
18444 }
18445 
18446 /**
18447  * clutter_actor_get_margin_left:
18448  * @self: a #ClutterActor
18449  *
18450  * Retrieves the left margin of a #ClutterActor.
18451  *
18452  * Return value: the left margin
18453  *
18454  * Since: 1.10
18455  */
18456 gfloat
clutter_actor_get_margin_left(ClutterActor * self)18457 clutter_actor_get_margin_left (ClutterActor *self)
18458 {
18459   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
18460 
18461   return _clutter_actor_get_layout_info_or_defaults (self)->margin.left;
18462 }
18463 
18464 /**
18465  * clutter_actor_set_margin_right:
18466  * @self: a #ClutterActor
18467  * @margin: the right margin
18468  *
18469  * Sets the margin from the right of a #ClutterActor.
18470  *
18471  * The #ClutterActor:margin-right property is animatable.
18472  *
18473  * Since: 1.10
18474  */
18475 void
clutter_actor_set_margin_right(ClutterActor * self,gfloat margin)18476 clutter_actor_set_margin_right (ClutterActor *self,
18477                                 gfloat        margin)
18478 {
18479   const ClutterLayoutInfo *info;
18480 
18481   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18482   g_return_if_fail (margin >= 0.f);
18483 
18484   info = _clutter_actor_get_layout_info_or_defaults (self);
18485   _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_RIGHT],
18486                                     info->margin.right,
18487                                     margin);
18488 }
18489 
18490 /**
18491  * clutter_actor_get_margin_right:
18492  * @self: a #ClutterActor
18493  *
18494  * Retrieves the right margin of a #ClutterActor.
18495  *
18496  * Return value: the right margin
18497  *
18498  * Since: 1.10
18499  */
18500 gfloat
clutter_actor_get_margin_right(ClutterActor * self)18501 clutter_actor_get_margin_right (ClutterActor *self)
18502 {
18503   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f);
18504 
18505   return _clutter_actor_get_layout_info_or_defaults (self)->margin.right;
18506 }
18507 
18508 static inline void
clutter_actor_set_background_color_internal(ClutterActor * self,const ClutterColor * color)18509 clutter_actor_set_background_color_internal (ClutterActor *self,
18510                                              const ClutterColor *color)
18511 {
18512   ClutterActorPrivate *priv = self->priv;
18513   GObject *obj;
18514 
18515   if (priv->bg_color_set && clutter_color_equal (color, &priv->bg_color))
18516     return;
18517 
18518   obj = G_OBJECT (self);
18519 
18520   priv->bg_color = *color;
18521   priv->bg_color_set = TRUE;
18522 
18523   clutter_actor_queue_redraw (self);
18524 
18525   g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR_SET]);
18526   g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR]);
18527 }
18528 
18529 /**
18530  * clutter_actor_set_background_color:
18531  * @self: a #ClutterActor
18532  * @color: (allow-none): a #ClutterColor, or %NULL to unset a previously
18533  *  set color
18534  *
18535  * Sets the background color of a #ClutterActor.
18536  *
18537  * The background color will be used to cover the whole allocation of the
18538  * actor. The default background color of an actor is transparent.
18539  *
18540  * To check whether an actor has a background color, you can use the
18541  * #ClutterActor:background-color-set actor property.
18542  *
18543  * The #ClutterActor:background-color property is animatable.
18544  *
18545  * Since: 1.10
18546  */
18547 void
clutter_actor_set_background_color(ClutterActor * self,const ClutterColor * color)18548 clutter_actor_set_background_color (ClutterActor       *self,
18549                                     const ClutterColor *color)
18550 {
18551   ClutterActorPrivate *priv;
18552 
18553   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18554 
18555   priv = self->priv;
18556 
18557   if (color == NULL)
18558     {
18559       GObject *obj = G_OBJECT (self);
18560 
18561       priv->bg_color_set = FALSE;
18562 
18563       clutter_actor_queue_redraw (self);
18564 
18565       g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR_SET]);
18566     }
18567   else
18568     _clutter_actor_create_transition (self,
18569                                       obj_props[PROP_BACKGROUND_COLOR],
18570                                       &priv->bg_color,
18571                                       color);
18572 }
18573 
18574 /**
18575  * clutter_actor_get_background_color:
18576  * @self: a #ClutterActor
18577  * @color: (out caller-allocates): return location for a #ClutterColor
18578  *
18579  * Retrieves the color set using clutter_actor_set_background_color().
18580  *
18581  * Since: 1.10
18582  */
18583 void
clutter_actor_get_background_color(ClutterActor * self,ClutterColor * color)18584 clutter_actor_get_background_color (ClutterActor *self,
18585                                     ClutterColor *color)
18586 {
18587   g_return_if_fail (CLUTTER_IS_ACTOR (self));
18588   g_return_if_fail (color != NULL);
18589 
18590   *color = self->priv->bg_color;
18591 }
18592 
18593 /**
18594  * clutter_actor_get_previous_sibling:
18595  * @self: a #ClutterActor
18596  *
18597  * Retrieves the sibling of @self that comes before it in the list
18598  * of children of @self's parent.
18599  *
18600  * The returned pointer is only valid until the scene graph changes; it
18601  * is not safe to modify the list of children of @self while iterating
18602  * it.
18603  *
18604  * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
18605  *
18606  * Since: 1.10
18607  */
18608 ClutterActor *
clutter_actor_get_previous_sibling(ClutterActor * self)18609 clutter_actor_get_previous_sibling (ClutterActor *self)
18610 {
18611   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18612 
18613   return self->priv->prev_sibling;
18614 }
18615 
18616 /**
18617  * clutter_actor_get_next_sibling:
18618  * @self: a #ClutterActor
18619  *
18620  * Retrieves the sibling of @self that comes after it in the list
18621  * of children of @self's parent.
18622  *
18623  * The returned pointer is only valid until the scene graph changes; it
18624  * is not safe to modify the list of children of @self while iterating
18625  * it.
18626  *
18627  * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
18628  *
18629  * Since: 1.10
18630  */
18631 ClutterActor *
clutter_actor_get_next_sibling(ClutterActor * self)18632 clutter_actor_get_next_sibling (ClutterActor *self)
18633 {
18634   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18635 
18636   return self->priv->next_sibling;
18637 }
18638 
18639 /**
18640  * clutter_actor_get_first_child:
18641  * @self: a #ClutterActor
18642  *
18643  * Retrieves the first child of @self.
18644  *
18645  * The returned pointer is only valid until the scene graph changes; it
18646  * is not safe to modify the list of children of @self while iterating
18647  * it.
18648  *
18649  * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
18650  *
18651  * Since: 1.10
18652  */
18653 ClutterActor *
clutter_actor_get_first_child(ClutterActor * self)18654 clutter_actor_get_first_child (ClutterActor *self)
18655 {
18656   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18657 
18658   return self->priv->first_child;
18659 }
18660 
18661 /**
18662  * clutter_actor_get_last_child:
18663  * @self: a #ClutterActor
18664  *
18665  * Retrieves the last child of @self.
18666  *
18667  * The returned pointer is only valid until the scene graph changes; it
18668  * is not safe to modify the list of children of @self while iterating
18669  * it.
18670  *
18671  * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL
18672  *
18673  * Since: 1.10
18674  */
18675 ClutterActor *
clutter_actor_get_last_child(ClutterActor * self)18676 clutter_actor_get_last_child (ClutterActor *self)
18677 {
18678   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
18679 
18680   return self->priv->last_child;
18681 }
18682 
18683 /* easy way to have properly named fields instead of the dummy ones
18684  * we use in the public structure
18685  */
18686 typedef struct _RealActorIter
18687 {
18688   ClutterActor *root;           /* dummy1 */
18689   ClutterActor *current;        /* dummy2 */
18690   gpointer padding_1;           /* dummy3 */
18691   gint age;                     /* dummy4 */
18692   gpointer padding_2;           /* dummy5 */
18693 } RealActorIter;
18694 
18695 /**
18696  * clutter_actor_iter_init:
18697  * @iter: a #ClutterActorIter
18698  * @root: a #ClutterActor
18699  *
18700  * Initializes a #ClutterActorIter, which can then be used to iterate
18701  * efficiently over a section of the scene graph, and associates it
18702  * with @root.
18703  *
18704  * Modifying the scene graph section that contains @root will invalidate
18705  * the iterator.
18706  *
18707  * |[<!-- language="C" -->
18708  *   ClutterActorIter iter;
18709  *   ClutterActor *child;
18710  *
18711  *   clutter_actor_iter_init (&iter, container);
18712  *   while (clutter_actor_iter_next (&iter, &child))
18713  *     {
18714  *       // do something with child
18715  *     }
18716  * ]|
18717  *
18718  * Since: 1.10
18719  */
18720 void
clutter_actor_iter_init(ClutterActorIter * iter,ClutterActor * root)18721 clutter_actor_iter_init (ClutterActorIter *iter,
18722                          ClutterActor     *root)
18723 {
18724   RealActorIter *ri = (RealActorIter *) iter;
18725 
18726   g_return_if_fail (iter != NULL);
18727   g_return_if_fail (CLUTTER_IS_ACTOR (root));
18728 
18729   ri->root = root;
18730   ri->current = NULL;
18731   ri->age = root->priv->age;
18732 }
18733 
18734 /**
18735  * clutter_actor_iter_is_valid:
18736  * @iter: a #ClutterActorIter
18737  *
18738  * Checks whether a #ClutterActorIter is still valid.
18739  *
18740  * An iterator is considered valid if it has been initialized, and
18741  * if the #ClutterActor that it refers to hasn't been modified after
18742  * the initialization.
18743  *
18744  * Return value: %TRUE if the iterator is valid, and %FALSE otherwise
18745  *
18746  * Since: 1.12
18747  */
18748 gboolean
clutter_actor_iter_is_valid(const ClutterActorIter * iter)18749 clutter_actor_iter_is_valid (const ClutterActorIter *iter)
18750 {
18751   RealActorIter *ri = (RealActorIter *) iter;
18752 
18753   g_return_val_if_fail (iter != NULL, FALSE);
18754 
18755   if (ri->root == NULL)
18756     return FALSE;
18757 
18758   return ri->root->priv->age == ri->age;
18759 }
18760 
18761 /**
18762  * clutter_actor_iter_next:
18763  * @iter: a #ClutterActorIter
18764  * @child: (out) (transfer none): return location for a #ClutterActor
18765  *
18766  * Advances the @iter and retrieves the next child of the root #ClutterActor
18767  * that was used to initialize the #ClutterActorIterator.
18768  *
18769  * If the iterator can advance, this function returns %TRUE and sets the
18770  * @child argument.
18771  *
18772  * If the iterator cannot advance, this function returns %FALSE, and
18773  * the contents of @child are undefined.
18774  *
18775  * Return value: %TRUE if the iterator could advance, and %FALSE otherwise.
18776  *
18777  * Since: 1.10
18778  */
18779 gboolean
clutter_actor_iter_next(ClutterActorIter * iter,ClutterActor ** child)18780 clutter_actor_iter_next (ClutterActorIter  *iter,
18781                          ClutterActor     **child)
18782 {
18783   RealActorIter *ri = (RealActorIter *) iter;
18784 
18785   g_return_val_if_fail (iter != NULL, FALSE);
18786   g_return_val_if_fail (ri->root != NULL, FALSE);
18787 #ifndef G_DISABLE_ASSERT
18788   g_return_val_if_fail (ri->age == ri->root->priv->age, FALSE);
18789 #endif
18790 
18791   if (ri->current == NULL)
18792     ri->current = ri->root->priv->first_child;
18793   else
18794     ri->current = ri->current->priv->next_sibling;
18795 
18796   if (child != NULL)
18797     *child = ri->current;
18798 
18799   return ri->current != NULL;
18800 }
18801 
18802 /**
18803  * clutter_actor_iter_prev:
18804  * @iter: a #ClutterActorIter
18805  * @child: (out) (transfer none): return location for a #ClutterActor
18806  *
18807  * Advances the @iter and retrieves the previous child of the root
18808  * #ClutterActor that was used to initialize the #ClutterActorIterator.
18809  *
18810  * If the iterator can advance, this function returns %TRUE and sets the
18811  * @child argument.
18812  *
18813  * If the iterator cannot advance, this function returns %FALSE, and
18814  * the contents of @child are undefined.
18815  *
18816  * Return value: %TRUE if the iterator could advance, and %FALSE otherwise.
18817  *
18818  * Since: 1.10
18819  */
18820 gboolean
clutter_actor_iter_prev(ClutterActorIter * iter,ClutterActor ** child)18821 clutter_actor_iter_prev (ClutterActorIter  *iter,
18822                          ClutterActor     **child)
18823 {
18824   RealActorIter *ri = (RealActorIter *) iter;
18825 
18826   g_return_val_if_fail (iter != NULL, FALSE);
18827   g_return_val_if_fail (ri->root != NULL, FALSE);
18828 #ifndef G_DISABLE_ASSERT
18829   g_return_val_if_fail (ri->age == ri->root->priv->age, FALSE);
18830 #endif
18831 
18832   if (ri->current == NULL)
18833     ri->current = ri->root->priv->last_child;
18834   else
18835     ri->current = ri->current->priv->prev_sibling;
18836 
18837   if (child != NULL)
18838     *child = ri->current;
18839 
18840   return ri->current != NULL;
18841 }
18842 
18843 /**
18844  * clutter_actor_iter_remove:
18845  * @iter: a #ClutterActorIter
18846  *
18847  * Safely removes the #ClutterActor currently pointer to by the iterator
18848  * from its parent.
18849  *
18850  * This function can only be called after clutter_actor_iter_next() or
18851  * clutter_actor_iter_prev() returned %TRUE, and cannot be called more
18852  * than once for the same actor.
18853  *
18854  * This function will call clutter_actor_remove_child() internally.
18855  *
18856  * Since: 1.10
18857  */
18858 void
clutter_actor_iter_remove(ClutterActorIter * iter)18859 clutter_actor_iter_remove (ClutterActorIter *iter)
18860 {
18861   RealActorIter *ri = (RealActorIter *) iter;
18862   ClutterActor *cur;
18863 
18864   g_return_if_fail (iter != NULL);
18865   g_return_if_fail (ri->root != NULL);
18866 #ifndef G_DISABLE_ASSERT
18867   g_return_if_fail (ri->age == ri->root->priv->age);
18868 #endif
18869   g_return_if_fail (ri->current != NULL);
18870 
18871   cur = ri->current;
18872 
18873   if (cur != NULL)
18874     {
18875       ri->current = cur->priv->prev_sibling;
18876 
18877       clutter_actor_remove_child_internal (ri->root, cur,
18878                                            REMOVE_CHILD_DEFAULT_FLAGS);
18879 
18880       ri->age += 1;
18881     }
18882 }
18883 
18884 /**
18885  * clutter_actor_iter_destroy:
18886  * @iter: a #ClutterActorIter
18887  *
18888  * Safely destroys the #ClutterActor currently pointer to by the iterator
18889  * from its parent.
18890  *
18891  * This function can only be called after clutter_actor_iter_next() or
18892  * clutter_actor_iter_prev() returned %TRUE, and cannot be called more
18893  * than once for the same actor.
18894  *
18895  * This function will call clutter_actor_destroy() internally.
18896  *
18897  * Since: 1.10
18898  */
18899 void
clutter_actor_iter_destroy(ClutterActorIter * iter)18900 clutter_actor_iter_destroy (ClutterActorIter *iter)
18901 {
18902   RealActorIter *ri = (RealActorIter *) iter;
18903   ClutterActor *cur;
18904 
18905   g_return_if_fail (iter != NULL);
18906   g_return_if_fail (ri->root != NULL);
18907 #ifndef G_DISABLE_ASSERT
18908   g_return_if_fail (ri->age == ri->root->priv->age);
18909 #endif
18910   g_return_if_fail (ri->current != NULL);
18911 
18912   cur = ri->current;
18913 
18914   if (cur != NULL)
18915     {
18916       ri->current = cur->priv->prev_sibling;
18917 
18918       clutter_actor_destroy (cur);
18919 
18920       ri->age += 1;
18921     }
18922 }
18923 
18924 static const ClutterAnimationInfo default_animation_info = {
18925   NULL,         /* transitions */
18926   NULL,         /* states */
18927   NULL,         /* cur_state */
18928 };
18929 
18930 static void
clutter_animation_info_free(gpointer data)18931 clutter_animation_info_free (gpointer data)
18932 {
18933   if (data != NULL)
18934     {
18935       ClutterAnimationInfo *info = data;
18936 
18937       if (info->transitions != NULL)
18938         g_hash_table_unref (info->transitions);
18939 
18940       if (info->states != NULL)
18941         g_array_unref (info->states);
18942 
18943       g_slice_free (ClutterAnimationInfo, info);
18944     }
18945 }
18946 
18947 const ClutterAnimationInfo *
_clutter_actor_get_animation_info_or_defaults(ClutterActor * self)18948 _clutter_actor_get_animation_info_or_defaults (ClutterActor *self)
18949 {
18950   const ClutterAnimationInfo *res;
18951   GObject *obj = G_OBJECT (self);
18952 
18953   res = g_object_get_qdata (obj, quark_actor_animation_info);
18954   if (res != NULL)
18955     return res;
18956 
18957   return &default_animation_info;
18958 }
18959 
18960 ClutterAnimationInfo *
_clutter_actor_get_animation_info(ClutterActor * self)18961 _clutter_actor_get_animation_info (ClutterActor *self)
18962 {
18963   GObject *obj = G_OBJECT (self);
18964   ClutterAnimationInfo *res;
18965 
18966   res = g_object_get_qdata (obj, quark_actor_animation_info);
18967   if (res == NULL)
18968     {
18969       res = g_slice_new (ClutterAnimationInfo);
18970 
18971       *res = default_animation_info;
18972 
18973       g_object_set_qdata_full (obj, quark_actor_animation_info,
18974                                res,
18975                                clutter_animation_info_free);
18976     }
18977 
18978   return res;
18979 }
18980 
18981 ClutterTransition *
_clutter_actor_get_transition(ClutterActor * actor,GParamSpec * pspec)18982 _clutter_actor_get_transition (ClutterActor *actor,
18983                                GParamSpec   *pspec)
18984 {
18985   const ClutterAnimationInfo *info;
18986 
18987   info = _clutter_actor_get_animation_info_or_defaults (actor);
18988 
18989   if (info->transitions == NULL)
18990     return NULL;
18991 
18992   return g_hash_table_lookup (info->transitions, pspec->name);
18993 }
18994 
18995 static void
transition_closure_free(gpointer data)18996 transition_closure_free (gpointer data)
18997 {
18998   if (G_LIKELY (data != NULL))
18999     {
19000       TransitionClosure *clos = data;
19001       ClutterTimeline *timeline;
19002 
19003       timeline = CLUTTER_TIMELINE (clos->transition);
19004 
19005       /* we disconnect the signal handler before stopping the timeline,
19006        * so that we don't end up inside on_transition_stopped() from
19007        * a call to g_hash_table_remove().
19008        */
19009       g_signal_handler_disconnect (clos->transition, clos->completed_id);
19010 
19011       if (clutter_timeline_is_playing (timeline))
19012         clutter_timeline_stop (timeline);
19013 
19014       /* remove the reference added in add_transition_internal() */
19015       g_object_unref (clos->transition);
19016 
19017       g_free (clos->name);
19018 
19019       g_slice_free (TransitionClosure, clos);
19020     }
19021 }
19022 
19023 static void
on_transition_stopped(ClutterTransition * transition,gboolean is_finished,TransitionClosure * clos)19024 on_transition_stopped (ClutterTransition *transition,
19025                        gboolean           is_finished,
19026                        TransitionClosure *clos)
19027 {
19028   ClutterActor *actor = clos->actor;
19029   ClutterAnimationInfo *info;
19030   GQuark t_quark;
19031   gchar *t_name;
19032 
19033   if (clos->name == NULL)
19034     return;
19035 
19036   /* reset the caches used by animations */
19037   clutter_actor_store_content_box (actor, NULL);
19038 
19039   info = _clutter_actor_get_animation_info (actor);
19040 
19041   /* we need copies because we emit the signal after the
19042    * TransitionClosure data structure has been freed
19043    */
19044   t_quark = g_quark_from_string (clos->name);
19045   t_name = g_strdup (clos->name);
19046 
19047   if (clutter_transition_get_remove_on_complete (transition))
19048     {
19049       /* this is safe, because the timeline has now stopped,
19050        * so we won't recurse; the reference on the Animatable
19051        * will be dropped by the ::stopped signal closure in
19052        * ClutterTransition, which is RUN_LAST, and thus will
19053        * be called after this handler
19054        */
19055       g_hash_table_remove (info->transitions, clos->name);
19056     }
19057 
19058   /* we emit the ::transition-stopped after removing the
19059    * transition, so that we can chain up new transitions
19060    * without interfering with the one that just finished
19061    */
19062   g_signal_emit (actor, actor_signals[TRANSITION_STOPPED], t_quark,
19063                  t_name,
19064                  is_finished);
19065 
19066   g_free (t_name);
19067 
19068   /* if it's the last transition then we clean up */
19069   if (g_hash_table_size (info->transitions) == 0)
19070     {
19071       g_hash_table_unref (info->transitions);
19072       info->transitions = NULL;
19073 
19074       CLUTTER_NOTE (ANIMATION, "Transitions for '%s' completed",
19075                     _clutter_actor_get_debug_name (actor));
19076 
19077       g_signal_emit (actor, actor_signals[TRANSITIONS_COMPLETED], 0);
19078     }
19079 }
19080 
19081 static void
clutter_actor_add_transition_internal(ClutterActor * self,const gchar * name,ClutterTransition * transition)19082 clutter_actor_add_transition_internal (ClutterActor *self,
19083                                        const gchar  *name,
19084                                        ClutterTransition *transition)
19085 {
19086   ClutterTimeline *timeline;
19087   TransitionClosure *clos;
19088   ClutterAnimationInfo *info;
19089 
19090   info = _clutter_actor_get_animation_info (self);
19091 
19092   if (info->transitions == NULL)
19093     info->transitions = g_hash_table_new_full (g_str_hash, g_str_equal,
19094                                                NULL,
19095                                                transition_closure_free);
19096 
19097   if (g_hash_table_lookup (info->transitions, name) != NULL)
19098     {
19099       g_warning ("A transition with name '%s' already exists for "
19100                  "the actor '%s'",
19101                  name,
19102                  _clutter_actor_get_debug_name (self));
19103       return;
19104     }
19105 
19106   clutter_transition_set_animatable (transition, CLUTTER_ANIMATABLE (self));
19107 
19108   timeline = CLUTTER_TIMELINE (transition);
19109 
19110   clos = g_slice_new (TransitionClosure);
19111   clos->actor = self;
19112   clos->transition = g_object_ref (transition);
19113   clos->name = g_strdup (name);
19114   clos->completed_id = g_signal_connect (timeline, "stopped",
19115                                          G_CALLBACK (on_transition_stopped),
19116                                          clos);
19117 
19118   CLUTTER_NOTE (ANIMATION,
19119                 "Adding transition '%s' [%p] to actor '%s'",
19120                 clos->name,
19121                 clos->transition,
19122                 _clutter_actor_get_debug_name (self));
19123 
19124   g_hash_table_insert (info->transitions, clos->name, clos);
19125   clutter_timeline_start (timeline);
19126 }
19127 
19128 static gboolean
should_skip_implicit_transition(ClutterActor * self,GParamSpec * pspec)19129 should_skip_implicit_transition (ClutterActor *self,
19130                                  GParamSpec   *pspec)
19131 {
19132   ClutterActorPrivate *priv = self->priv;
19133   const ClutterAnimationInfo *info;
19134 
19135   /* this function is called from _clutter_actor_create_transition() which
19136    * calls _clutter_actor_get_animation_info() first, so we're guaranteed
19137    * to have the correct ClutterAnimationInfo pointer
19138    */
19139   info = _clutter_actor_get_animation_info_or_defaults (self);
19140 
19141   /* if the easing state has a non-zero duration we always want an
19142    * implicit transition to occur
19143    */
19144   if (info->cur_state->easing_duration == 0)
19145     return TRUE;
19146 
19147   /* on the other hand, if the actor hasn't been allocated yet, we want to
19148    * skip all transitions on the :allocation, to avoid actors "flying in"
19149    * into their new position and size
19150    */
19151   if (pspec == obj_props[PROP_ALLOCATION] && priv->needs_allocation)
19152     return TRUE;
19153 
19154   /* if the actor is not mapped and is not part of a branch of the scene
19155    * graph that is being cloned, then we always skip implicit transitions
19156    * on the account of the fact that the actor is not going to be visible
19157    * when those transitions happen
19158    */
19159   if (!CLUTTER_ACTOR_IS_MAPPED (self) &&
19160       priv->in_cloned_branch == 0 &&
19161       !clutter_actor_has_mapped_clones (self))
19162     return TRUE;
19163 
19164   return FALSE;
19165 }
19166 
19167 /*< private >*
19168  * _clutter_actor_create_transition:
19169  * @actor: a #ClutterActor
19170  * @pspec: the property used for the transition
19171  * @...: initial and final state
19172  *
19173  * Creates a #ClutterTransition for the property represented by @pspec.
19174  *
19175  * Return value: a #ClutterTransition
19176  */
19177 ClutterTransition *
_clutter_actor_create_transition(ClutterActor * actor,GParamSpec * pspec,...)19178 _clutter_actor_create_transition (ClutterActor *actor,
19179                                   GParamSpec   *pspec,
19180                                   ...)
19181 {
19182   ClutterTimeline *timeline;
19183   ClutterInterval *interval;
19184   ClutterAnimationInfo *info;
19185   ClutterTransition *res = NULL;
19186   gboolean call_restore = FALSE;
19187   TransitionClosure *clos;
19188   va_list var_args;
19189   GValue initial = G_VALUE_INIT;
19190   GValue final = G_VALUE_INIT;
19191   GType ptype;
19192   char *error;
19193 
19194   g_assert (pspec != NULL);
19195   g_assert ((pspec->flags & CLUTTER_PARAM_ANIMATABLE) != 0);
19196 
19197   info = _clutter_actor_get_animation_info (actor);
19198 
19199   /* XXX - this will go away in 2.0
19200    *
19201    * if no state has been pushed, we assume that the easing state is
19202    * in "compatibility mode": all transitions have a duration of 0
19203    * msecs, which means that they happen immediately. in Clutter 2.0
19204    * this will turn into a g_assert(info->states != NULL), as every
19205    * actor will start with a predefined easing state
19206    */
19207   if (info->states == NULL)
19208     {
19209       clutter_actor_save_easing_state (actor);
19210       clutter_actor_set_easing_duration (actor, 0);
19211       call_restore = TRUE;
19212     }
19213 
19214   if (info->transitions == NULL)
19215     info->transitions = g_hash_table_new_full (g_str_hash, g_str_equal,
19216                                                NULL,
19217                                                transition_closure_free);
19218 
19219   va_start (var_args, pspec);
19220 
19221   ptype = G_PARAM_SPEC_VALUE_TYPE (pspec);
19222 
19223   G_VALUE_COLLECT_INIT (&initial, ptype,
19224                         var_args, 0,
19225                         &error);
19226   if (error != NULL)
19227     {
19228       g_critical ("%s: %s", G_STRLOC, error);
19229       g_free (error);
19230       goto out;
19231     }
19232 
19233   G_VALUE_COLLECT_INIT (&final, ptype,
19234                         var_args, 0,
19235                         &error);
19236   if (error != NULL)
19237     {
19238       g_critical ("%s: %s", G_STRLOC, error);
19239       g_value_unset (&initial);
19240       g_free (error);
19241       goto out;
19242     }
19243 
19244   if (should_skip_implicit_transition (actor, pspec))
19245     {
19246       CLUTTER_NOTE (ANIMATION, "Skipping implicit transition for '%s::%s'",
19247                     _clutter_actor_get_debug_name (actor),
19248                     pspec->name);
19249 
19250       /* remove a transition, if one exists */
19251       clutter_actor_remove_transition (actor, pspec->name);
19252 
19253       /* we don't go through the Animatable interface because we
19254        * already know we got here through an animatable property.
19255        */
19256       clutter_actor_set_animatable_property (actor,
19257                                              pspec->param_id,
19258                                              &final,
19259                                              pspec);
19260 
19261       g_value_unset (&initial);
19262       g_value_unset (&final);
19263 
19264       goto out;
19265     }
19266 
19267   clos = g_hash_table_lookup (info->transitions, pspec->name);
19268   if (clos == NULL)
19269     {
19270       res = clutter_property_transition_new (pspec->name);
19271 
19272       clutter_transition_set_remove_on_complete (res, TRUE);
19273 
19274       interval = clutter_interval_new_with_values (ptype, &initial, &final);
19275       clutter_transition_set_interval (res, interval);
19276 
19277       timeline = CLUTTER_TIMELINE (res);
19278       clutter_timeline_set_delay (timeline, info->cur_state->easing_delay);
19279       clutter_timeline_set_duration (timeline, info->cur_state->easing_duration);
19280       clutter_timeline_set_progress_mode (timeline, info->cur_state->easing_mode);
19281 
19282 #ifdef CLUTTER_ENABLE_DEBUG
19283       if (CLUTTER_HAS_DEBUG (ANIMATION))
19284         {
19285           gchar *initial_v, *final_v;
19286 
19287           initial_v = g_strdup_value_contents (&initial);
19288           final_v = g_strdup_value_contents (&final);
19289 
19290           CLUTTER_NOTE (ANIMATION,
19291                         "Created transition for %s:%s "
19292                         "(len:%u, mode:%s, delay:%u) "
19293                         "initial:%s, final:%s",
19294                         _clutter_actor_get_debug_name (actor),
19295                         pspec->name,
19296                         info->cur_state->easing_duration,
19297                         clutter_get_easing_name_for_mode (info->cur_state->easing_mode),
19298                         info->cur_state->easing_delay,
19299                         initial_v, final_v);
19300 
19301           g_free (initial_v);
19302           g_free (final_v);
19303         }
19304 #endif /* CLUTTER_ENABLE_DEBUG */
19305 
19306       /* this will start the transition as well */
19307       clutter_actor_add_transition_internal (actor, pspec->name, res);
19308 
19309       /* the actor now owns the transition */
19310       g_object_unref (res);
19311 
19312       g_value_unset (&initial);
19313       g_value_unset (&final);
19314     }
19315   else
19316     {
19317       ClutterAnimationMode cur_mode;
19318       guint cur_duration;
19319 
19320       CLUTTER_NOTE (ANIMATION, "Existing transition for %s:%s",
19321                     _clutter_actor_get_debug_name (actor),
19322                     pspec->name);
19323 
19324       timeline = CLUTTER_TIMELINE (clos->transition);
19325 
19326       cur_duration = clutter_timeline_get_duration (timeline);
19327       if (cur_duration != info->cur_state->easing_duration)
19328         clutter_timeline_set_duration (timeline, info->cur_state->easing_duration);
19329 
19330       cur_mode = clutter_timeline_get_progress_mode (timeline);
19331       if (cur_mode != info->cur_state->easing_mode)
19332         clutter_timeline_set_progress_mode (timeline, info->cur_state->easing_mode);
19333 
19334       clutter_timeline_rewind (timeline);
19335 
19336       interval = clutter_transition_get_interval (clos->transition);
19337       clutter_interval_set_initial_value (interval, &initial);
19338       clutter_interval_set_final_value (interval, &final);
19339 
19340       res = clos->transition;
19341     }
19342 
19343 out:
19344   if (call_restore)
19345     clutter_actor_restore_easing_state (actor);
19346 
19347   va_end (var_args);
19348 
19349   return res;
19350 }
19351 
19352 /**
19353  * clutter_actor_add_transition:
19354  * @self: a #ClutterActor
19355  * @name: the name of the transition to add
19356  * @transition: the #ClutterTransition to add
19357  *
19358  * Adds a @transition to the #ClutterActor's list of animations.
19359  *
19360  * The @name string is a per-actor unique identifier of the @transition: only
19361  * one #ClutterTransition can be associated to the specified @name.
19362  *
19363  * The @transition will be started once added.
19364  *
19365  * This function will take a reference on the @transition.
19366  *
19367  * This function is usually called implicitly when modifying an animatable
19368  * property.
19369  *
19370  * Since: 1.10
19371  */
19372 void
clutter_actor_add_transition(ClutterActor * self,const char * name,ClutterTransition * transition)19373 clutter_actor_add_transition (ClutterActor      *self,
19374                               const char        *name,
19375                               ClutterTransition *transition)
19376 {
19377   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19378   g_return_if_fail (name != NULL);
19379   g_return_if_fail (CLUTTER_IS_TRANSITION (transition));
19380 
19381   clutter_actor_add_transition_internal (self, name, transition);
19382 }
19383 
19384 /**
19385  * clutter_actor_remove_transition:
19386  * @self: a #ClutterActor
19387  * @name: the name of the transition to remove
19388  *
19389  * Removes the transition stored inside a #ClutterActor using @name
19390  * identifier.
19391  *
19392  * If the transition is currently in progress, it will be stopped.
19393  *
19394  * This function releases the reference acquired when the transition
19395  * was added to the #ClutterActor.
19396  *
19397  * Since: 1.10
19398  */
19399 void
clutter_actor_remove_transition(ClutterActor * self,const char * name)19400 clutter_actor_remove_transition (ClutterActor *self,
19401                                  const char   *name)
19402 {
19403   const ClutterAnimationInfo *info;
19404   TransitionClosure *clos;
19405   gboolean was_playing;
19406   GQuark t_quark;
19407   gchar *t_name;
19408 
19409   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19410   g_return_if_fail (name != NULL);
19411 
19412   info = _clutter_actor_get_animation_info_or_defaults (self);
19413 
19414   if (info->transitions == NULL)
19415     return;
19416 
19417   clos = g_hash_table_lookup (info->transitions, name);
19418   if (clos == NULL)
19419     return;
19420 
19421   was_playing =
19422     clutter_timeline_is_playing (CLUTTER_TIMELINE (clos->transition));
19423   t_quark = g_quark_from_string (clos->name);
19424   t_name = g_strdup (clos->name);
19425 
19426   g_hash_table_remove (info->transitions, name);
19427 
19428   /* we want to maintain the invariant that ::transition-stopped is
19429    * emitted after the transition has been removed, to allow replacing
19430    * or chaining; removing the transition from the hash table will
19431    * stop it, but transition_closure_free() will disconnect the signal
19432    * handler we install in add_transition_internal(), to avoid loops
19433    * or segfaults.
19434    *
19435    * since we know already that a transition will stop once it's removed
19436    * from an actor, we can simply emit the ::transition-stopped here
19437    * ourselves, if the timeline was playing (if it wasn't, then the
19438    * signal was already emitted at least once).
19439    */
19440   if (was_playing)
19441     {
19442       g_signal_emit (self, actor_signals[TRANSITION_STOPPED],
19443                      t_quark,
19444                      t_name,
19445                      FALSE);
19446     }
19447 
19448   g_free (t_name);
19449 }
19450 
19451 /**
19452  * clutter_actor_remove_all_transitions:
19453  * @self: a #ClutterActor
19454  *
19455  * Removes all transitions associated to @self.
19456  *
19457  * Since: 1.10
19458  */
19459 void
clutter_actor_remove_all_transitions(ClutterActor * self)19460 clutter_actor_remove_all_transitions (ClutterActor *self)
19461 {
19462   const ClutterAnimationInfo *info;
19463 
19464   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19465 
19466   info = _clutter_actor_get_animation_info_or_defaults (self);
19467   if (info->transitions == NULL)
19468     return;
19469 
19470   g_hash_table_remove_all (info->transitions);
19471 }
19472 
19473 /**
19474  * clutter_actor_set_easing_duration:
19475  * @self: a #ClutterActor
19476  * @msecs: the duration of the easing, or %NULL
19477  *
19478  * Sets the duration of the tweening for animatable properties
19479  * of @self for the current easing state.
19480  *
19481  * Since: 1.10
19482  */
19483 void
clutter_actor_set_easing_duration(ClutterActor * self,guint msecs)19484 clutter_actor_set_easing_duration (ClutterActor *self,
19485                                    guint         msecs)
19486 {
19487   ClutterAnimationInfo *info;
19488 
19489   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19490 
19491   info = _clutter_actor_get_animation_info (self);
19492 
19493   if (info->cur_state == NULL)
19494     {
19495       g_warning ("You must call clutter_actor_save_easing_state() prior "
19496                  "to calling clutter_actor_set_easing_duration().");
19497       return;
19498     }
19499 
19500   if (info->cur_state->easing_duration != msecs)
19501     info->cur_state->easing_duration = msecs;
19502 }
19503 
19504 /**
19505  * clutter_actor_get_easing_duration:
19506  * @self: a #ClutterActor
19507  *
19508  * Retrieves the duration of the tweening for animatable
19509  * properties of @self for the current easing state.
19510  *
19511  * Return value: the duration of the tweening, in milliseconds
19512  *
19513  * Since: 1.10
19514  */
19515 guint
clutter_actor_get_easing_duration(ClutterActor * self)19516 clutter_actor_get_easing_duration (ClutterActor *self)
19517 {
19518   const ClutterAnimationInfo *info;
19519 
19520   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
19521 
19522   info = _clutter_actor_get_animation_info_or_defaults (self);
19523 
19524   if (info->cur_state != NULL)
19525     return info->cur_state->easing_duration;
19526 
19527   return 0;
19528 }
19529 
19530 /**
19531  * clutter_actor_set_easing_mode:
19532  * @self: a #ClutterActor
19533  * @mode: an easing mode, excluding %CLUTTER_CUSTOM_MODE
19534  *
19535  * Sets the easing mode for the tweening of animatable properties
19536  * of @self.
19537  *
19538  * Since: 1.10
19539  */
19540 void
clutter_actor_set_easing_mode(ClutterActor * self,ClutterAnimationMode mode)19541 clutter_actor_set_easing_mode (ClutterActor         *self,
19542                                ClutterAnimationMode  mode)
19543 {
19544   ClutterAnimationInfo *info;
19545 
19546   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19547   g_return_if_fail (mode != CLUTTER_CUSTOM_MODE);
19548   g_return_if_fail (mode < CLUTTER_ANIMATION_LAST);
19549 
19550   info = _clutter_actor_get_animation_info (self);
19551 
19552   if (info->cur_state == NULL)
19553     {
19554       g_warning ("You must call clutter_actor_save_easing_state() prior "
19555                  "to calling clutter_actor_set_easing_mode().");
19556       return;
19557     }
19558 
19559   if (info->cur_state->easing_mode != mode)
19560     info->cur_state->easing_mode = mode;
19561 }
19562 
19563 /**
19564  * clutter_actor_get_easing_mode:
19565  * @self: a #ClutterActor
19566  *
19567  * Retrieves the easing mode for the tweening of animatable properties
19568  * of @self for the current easing state.
19569  *
19570  * Return value: an easing mode
19571  *
19572  * Since: 1.10
19573  */
19574 ClutterAnimationMode
clutter_actor_get_easing_mode(ClutterActor * self)19575 clutter_actor_get_easing_mode (ClutterActor *self)
19576 {
19577   const ClutterAnimationInfo *info;
19578 
19579   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_EASE_OUT_CUBIC);
19580 
19581   info = _clutter_actor_get_animation_info_or_defaults (self);
19582 
19583   if (info->cur_state != NULL)
19584     return info->cur_state->easing_mode;
19585 
19586   return CLUTTER_EASE_OUT_CUBIC;
19587 }
19588 
19589 /**
19590  * clutter_actor_set_easing_delay:
19591  * @self: a #ClutterActor
19592  * @msecs: the delay before the start of the tweening, in milliseconds
19593  *
19594  * Sets the delay that should be applied before tweening animatable
19595  * properties.
19596  *
19597  * Since: 1.10
19598  */
19599 void
clutter_actor_set_easing_delay(ClutterActor * self,guint msecs)19600 clutter_actor_set_easing_delay (ClutterActor *self,
19601                                 guint         msecs)
19602 {
19603   ClutterAnimationInfo *info;
19604 
19605   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19606 
19607   info = _clutter_actor_get_animation_info (self);
19608 
19609   if (info->cur_state == NULL)
19610     {
19611       g_warning ("You must call clutter_actor_save_easing_state() prior "
19612                  "to calling clutter_actor_set_easing_delay().");
19613       return;
19614     }
19615 
19616   if (info->cur_state->easing_delay != msecs)
19617     info->cur_state->easing_delay = msecs;
19618 }
19619 
19620 /**
19621  * clutter_actor_get_easing_delay:
19622  * @self: a #ClutterActor
19623  *
19624  * Retrieves the delay that should be applied when tweening animatable
19625  * properties.
19626  *
19627  * Return value: a delay, in milliseconds
19628  *
19629  * Since: 1.10
19630  */
19631 guint
clutter_actor_get_easing_delay(ClutterActor * self)19632 clutter_actor_get_easing_delay (ClutterActor *self)
19633 {
19634   const ClutterAnimationInfo *info;
19635 
19636   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0);
19637 
19638   info = _clutter_actor_get_animation_info_or_defaults (self);
19639 
19640   if (info->cur_state != NULL)
19641     return info->cur_state->easing_delay;
19642 
19643   return 0;
19644 }
19645 
19646 /**
19647  * clutter_actor_get_transition:
19648  * @self: a #ClutterActor
19649  * @name: the name of the transition
19650  *
19651  * Retrieves the #ClutterTransition of a #ClutterActor by using the
19652  * transition @name.
19653  *
19654  * Transitions created for animatable properties use the name of the
19655  * property itself, for instance the code below:
19656  *
19657  * |[<!-- language="C" -->
19658  *   clutter_actor_set_easing_duration (actor, 1000);
19659  *   clutter_actor_set_rotation (actor, CLUTTER_Y_AXIS, 360.0, x, y, z);
19660  *
19661  *   transition = clutter_actor_get_transition (actor, "rotation-angle-y");
19662  *   g_signal_connect (transition, "stopped",
19663  *                     G_CALLBACK (on_transition_stopped),
19664  *                     actor);
19665  * ]|
19666  *
19667  * will call the `on_transition_stopped` callback when the transition
19668  * is finished.
19669  *
19670  * If you just want to get notifications of the completion of a transition,
19671  * you should use the #ClutterActor::transition-stopped signal, using the
19672  * transition name as the signal detail.
19673  *
19674  * Return value: (transfer none): a #ClutterTransition, or %NULL is none
19675  *   was found to match the passed name; the returned instance is owned
19676  *   by Clutter and it should not be freed
19677  *
19678  * Since: 1.10
19679  */
19680 ClutterTransition *
clutter_actor_get_transition(ClutterActor * self,const char * name)19681 clutter_actor_get_transition (ClutterActor *self,
19682                               const char   *name)
19683 {
19684   TransitionClosure *clos;
19685   const ClutterAnimationInfo *info;
19686 
19687   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
19688   g_return_val_if_fail (name != NULL, NULL);
19689 
19690   info = _clutter_actor_get_animation_info_or_defaults (self);
19691   if (info->transitions == NULL)
19692     return NULL;
19693 
19694   clos = g_hash_table_lookup (info->transitions, name);
19695   if (clos == NULL)
19696     return NULL;
19697 
19698   return clos->transition;
19699 }
19700 
19701 /**
19702  * clutter_actor_save_easing_state:
19703  * @self: a #ClutterActor
19704  *
19705  * Saves the current easing state for animatable properties, and creates
19706  * a new state with the default values for easing mode and duration.
19707  *
19708  * New transitions created after calling this function will inherit the
19709  * duration, easing mode, and delay of the new easing state; this also
19710  * applies to transitions modified in flight.
19711  *
19712  * Since: 1.10
19713  */
19714 void
clutter_actor_save_easing_state(ClutterActor * self)19715 clutter_actor_save_easing_state (ClutterActor *self)
19716 {
19717   ClutterAnimationInfo *info;
19718   AState new_state;
19719 
19720   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19721 
19722   info = _clutter_actor_get_animation_info (self);
19723 
19724   if (info->states == NULL)
19725     info->states = g_array_new (FALSE, FALSE, sizeof (AState));
19726 
19727   new_state.easing_mode = CLUTTER_EASE_OUT_CUBIC;
19728   new_state.easing_duration = 250;
19729   new_state.easing_delay = 0;
19730 
19731   g_array_append_val (info->states, new_state);
19732 
19733   info->cur_state = &g_array_index (info->states, AState, info->states->len - 1);
19734 }
19735 
19736 /**
19737  * clutter_actor_restore_easing_state:
19738  * @self: a #ClutterActor
19739  *
19740  * Restores the easing state as it was prior to a call to
19741  * clutter_actor_save_easing_state().
19742  *
19743  * Since: 1.10
19744  */
19745 void
clutter_actor_restore_easing_state(ClutterActor * self)19746 clutter_actor_restore_easing_state (ClutterActor *self)
19747 {
19748   ClutterAnimationInfo *info;
19749 
19750   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19751 
19752   info = _clutter_actor_get_animation_info (self);
19753 
19754   if (info->states == NULL)
19755     {
19756       g_critical ("The function clutter_actor_restore_easing_state() has "
19757                   "been called without a previous call to "
19758                   "clutter_actor_save_easing_state().");
19759       return;
19760     }
19761 
19762   g_array_remove_index (info->states, info->states->len - 1);
19763 
19764   if (info->states->len > 0)
19765     info->cur_state = &g_array_index (info->states, AState, info->states->len - 1);
19766   else
19767     {
19768       g_array_unref (info->states);
19769       info->states = NULL;
19770       info->cur_state = NULL;
19771     }
19772 }
19773 
19774 /**
19775  * clutter_actor_set_content:
19776  * @self: a #ClutterActor
19777  * @content: (allow-none): a #ClutterContent, or %NULL
19778  *
19779  * Sets the contents of a #ClutterActor.
19780  *
19781  * Since: 1.10
19782  */
19783 void
clutter_actor_set_content(ClutterActor * self,ClutterContent * content)19784 clutter_actor_set_content (ClutterActor   *self,
19785                            ClutterContent *content)
19786 {
19787   ClutterActorPrivate *priv;
19788 
19789   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19790   g_return_if_fail (content == NULL || CLUTTER_IS_CONTENT (content));
19791 
19792   priv = self->priv;
19793 
19794   if (priv->content == content)
19795     return;
19796 
19797   if (priv->content != NULL)
19798     {
19799       _clutter_content_detached (priv->content, self);
19800       g_clear_object (&priv->content);
19801     }
19802 
19803   priv->content = content;
19804 
19805   if (priv->content != NULL)
19806     {
19807       g_object_ref (priv->content);
19808       _clutter_content_attached (priv->content, self);
19809     }
19810 
19811   /* if the actor's preferred size is the content's preferred size,
19812    * then we need to conditionally queue a relayout here...
19813    */
19814   if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE)
19815     _clutter_actor_queue_only_relayout (self);
19816 
19817   clutter_actor_queue_redraw (self);
19818 
19819   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT]);
19820 
19821   /* if the content gravity is not resize-fill, and the new content has a
19822    * different preferred size than the previous one, then the content box
19823    * may have been changed. since we compute that lazily, we just notify
19824    * here, and let whomever watches :content-box do whatever they need to
19825    * do.
19826    */
19827   if (priv->content_gravity != CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
19828     {
19829       if (priv->content_box_valid)
19830         {
19831           ClutterActorBox from_box, to_box;
19832 
19833           clutter_actor_get_content_box (self, &from_box);
19834 
19835           /* invalidate the cached content box */
19836           priv->content_box_valid = FALSE;
19837           clutter_actor_get_content_box (self, &to_box);
19838 
19839           if (!clutter_actor_box_equal (&from_box, &to_box))
19840             _clutter_actor_create_transition (self, obj_props[PROP_CONTENT_BOX],
19841                                               &from_box,
19842                                               &to_box);
19843         }
19844 
19845       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
19846    }
19847 }
19848 
19849 /**
19850  * clutter_actor_get_content:
19851  * @self: a #ClutterActor
19852  *
19853  * Retrieves the contents of @self.
19854  *
19855  * Return value: (transfer none): a pointer to the #ClutterContent instance,
19856  *   or %NULL if none was set
19857  *
19858  * Since: 1.10
19859  */
19860 ClutterContent *
clutter_actor_get_content(ClutterActor * self)19861 clutter_actor_get_content (ClutterActor *self)
19862 {
19863   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
19864 
19865   return self->priv->content;
19866 }
19867 
19868 /**
19869  * clutter_actor_set_content_gravity:
19870  * @self: a #ClutterActor
19871  * @gravity: the #ClutterContentGravity
19872  *
19873  * Sets the gravity of the #ClutterContent used by @self.
19874  *
19875  * See the description of the #ClutterActor:content-gravity property for
19876  * more information.
19877  *
19878  * The #ClutterActor:content-gravity property is animatable.
19879  *
19880  * Since: 1.10
19881  */
19882 void
clutter_actor_set_content_gravity(ClutterActor * self,ClutterContentGravity gravity)19883 clutter_actor_set_content_gravity (ClutterActor *self,
19884                                    ClutterContentGravity  gravity)
19885 {
19886   ClutterActorPrivate *priv;
19887   ClutterActorBox from_box, to_box;
19888 
19889   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19890 
19891   priv = self->priv;
19892 
19893   if (priv->content_gravity == gravity)
19894     return;
19895 
19896   priv->content_box_valid = FALSE;
19897 
19898   clutter_actor_get_content_box (self, &from_box);
19899 
19900   priv->content_gravity = gravity;
19901 
19902   clutter_actor_get_content_box (self, &to_box);
19903 
19904   _clutter_actor_create_transition (self, obj_props[PROP_CONTENT_BOX],
19905                                     &from_box,
19906                                     &to_box);
19907 
19908   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_GRAVITY]);
19909 }
19910 
19911 /**
19912  * clutter_actor_get_content_gravity:
19913  * @self: a #ClutterActor
19914  *
19915  * Retrieves the content gravity as set using
19916  * clutter_actor_set_content_gravity().
19917  *
19918  * Return value: the content gravity
19919  *
19920  * Since: 1.10
19921  */
19922 ClutterContentGravity
clutter_actor_get_content_gravity(ClutterActor * self)19923 clutter_actor_get_content_gravity (ClutterActor *self)
19924 {
19925   g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
19926                         CLUTTER_CONTENT_GRAVITY_RESIZE_FILL);
19927 
19928   return self->priv->content_gravity;
19929 }
19930 
19931 /**
19932  * clutter_actor_get_content_box:
19933  * @self: a #ClutterActor
19934  * @box: (out caller-allocates): the return location for the bounding
19935  *   box for the #ClutterContent
19936  *
19937  * Retrieves the bounding box for the #ClutterContent of @self.
19938  *
19939  * The bounding box is relative to the actor's allocation.
19940  *
19941  * If no #ClutterContent is set for @self, or if @self has not been
19942  * allocated yet, then the result is undefined.
19943  *
19944  * The content box is guaranteed to be, at most, as big as the allocation
19945  * of the #ClutterActor.
19946  *
19947  * If the #ClutterContent used by the actor has a preferred size, then
19948  * it is possible to modify the content box by using the
19949  * #ClutterActor:content-gravity property.
19950  *
19951  * Since: 1.10
19952  */
19953 void
clutter_actor_get_content_box(ClutterActor * self,ClutterActorBox * box)19954 clutter_actor_get_content_box (ClutterActor    *self,
19955                                ClutterActorBox *box)
19956 {
19957   ClutterActorPrivate *priv;
19958   gfloat content_w, content_h;
19959   gfloat alloc_w, alloc_h;
19960 
19961   g_return_if_fail (CLUTTER_IS_ACTOR (self));
19962   g_return_if_fail (box != NULL);
19963 
19964   priv = self->priv;
19965 
19966   box->x1 = 0.f;
19967   box->y1 = 0.f;
19968   box->x2 = priv->allocation.x2 - priv->allocation.x1;
19969   box->y2 = priv->allocation.y2 - priv->allocation.y1;
19970 
19971   if (priv->content_box_valid)
19972     {
19973       *box = priv->content_box;
19974       return;
19975     }
19976 
19977   /* no need to do any more work */
19978   if (priv->content_gravity == CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
19979     return;
19980 
19981   if (priv->content == NULL)
19982     return;
19983 
19984   /* if the content does not have a preferred size then there is
19985    * no point in computing the content box
19986    */
19987   if (!clutter_content_get_preferred_size (priv->content,
19988                                            &content_w,
19989                                            &content_h))
19990     return;
19991 
19992   alloc_w = box->x2;
19993   alloc_h = box->y2;
19994 
19995   switch (priv->content_gravity)
19996     {
19997     case CLUTTER_CONTENT_GRAVITY_TOP_LEFT:
19998       box->x2 = box->x1 + MIN (content_w, alloc_w);
19999       box->y2 = box->y1 + MIN (content_h, alloc_h);
20000       break;
20001 
20002     case CLUTTER_CONTENT_GRAVITY_TOP:
20003       if (alloc_w > content_w)
20004         {
20005           box->x1 += ceilf ((alloc_w - content_w) / 2.0);
20006           box->x2 = box->x1 + content_w;
20007         }
20008       box->y2 = box->y1 + MIN (content_h, alloc_h);
20009       break;
20010 
20011     case CLUTTER_CONTENT_GRAVITY_TOP_RIGHT:
20012       if (alloc_w > content_w)
20013         {
20014           box->x1 += (alloc_w - content_w);
20015           box->x2 = box->x1 + content_w;
20016         }
20017       box->y2 = box->y1 + MIN (content_h, alloc_h);
20018       break;
20019 
20020     case CLUTTER_CONTENT_GRAVITY_LEFT:
20021       box->x2 = box->x1 + MIN (content_w, alloc_w);
20022       if (alloc_h > content_h)
20023         {
20024           box->y1 += ceilf ((alloc_h - content_h) / 2.0);
20025           box->y2 = box->y1 + content_h;
20026         }
20027       break;
20028 
20029     case CLUTTER_CONTENT_GRAVITY_CENTER:
20030       if (alloc_w > content_w)
20031         {
20032           box->x1 += ceilf ((alloc_w - content_w) / 2.0);
20033           box->x2 = box->x1 + content_w;
20034         }
20035       if (alloc_h > content_h)
20036         {
20037           box->y1 += ceilf ((alloc_h - content_h) / 2.0);
20038           box->y2 = box->y1 + content_h;
20039         }
20040       break;
20041 
20042     case CLUTTER_CONTENT_GRAVITY_RIGHT:
20043       if (alloc_w > content_w)
20044         {
20045           box->x1 += (alloc_w - content_w);
20046           box->x2 = box->x1 + content_w;
20047         }
20048       if (alloc_h > content_h)
20049         {
20050           box->y1 += ceilf ((alloc_h - content_h) / 2.0);
20051           box->y2 = box->y1 + content_h;
20052         }
20053       break;
20054 
20055     case CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT:
20056       box->x2 = box->x1 + MIN (content_w, alloc_w);
20057       if (alloc_h > content_h)
20058         {
20059           box->y1 += (alloc_h - content_h);
20060           box->y2 = box->y1 + content_h;
20061         }
20062       break;
20063 
20064     case CLUTTER_CONTENT_GRAVITY_BOTTOM:
20065       if (alloc_w > content_w)
20066         {
20067           box->x1 += ceilf ((alloc_w - content_w) / 2.0);
20068           box->x2 = box->x1 + content_w;
20069         }
20070       if (alloc_h > content_h)
20071         {
20072           box->y1 += (alloc_h - content_h);
20073           box->y2 = box->y1 + content_h;
20074         }
20075       break;
20076 
20077     case CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT:
20078       if (alloc_w > content_w)
20079         {
20080           box->x1 += (alloc_w - content_w);
20081           box->x2 = box->x1 + content_w;
20082         }
20083       if (alloc_h > content_h)
20084         {
20085           box->y1 += (alloc_h - content_h);
20086           box->y2 = box->y1 + content_h;
20087         }
20088       break;
20089 
20090     case CLUTTER_CONTENT_GRAVITY_RESIZE_FILL:
20091       g_assert_not_reached ();
20092       break;
20093 
20094     case CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT:
20095       {
20096         double r_c = content_w / content_h;
20097 
20098         if ((alloc_w / r_c) > alloc_h)
20099           {
20100             box->y1 = 0.f;
20101             box->y2 = alloc_h;
20102 
20103             box->x1 = (alloc_w - (alloc_h * r_c)) / 2.0f;
20104             box->x2 = box->x1 + (alloc_h * r_c);
20105           }
20106         else
20107           {
20108             box->x1 = 0.f;
20109             box->x2 = alloc_w;
20110 
20111             box->y1 = (alloc_h - (alloc_w / r_c)) / 2.0f;
20112             box->y2 = box->y1 + (alloc_w / r_c);
20113           }
20114 
20115         CLUTTER_NOTE (LAYOUT,
20116                       "r_c: %.3f, r_a: %.3f\t"
20117                       "a: [%.2fx%.2f], c: [%.2fx%.2f]\t"
20118                       "b: [%.2f, %.2f, %.2f, %.2f]",
20119                       r_c, alloc_w / alloc_h,
20120                       alloc_w, alloc_h,
20121                       content_w, content_h,
20122                       box->x1, box->y1, box->x2, box->y2);
20123       }
20124       break;
20125     }
20126 }
20127 
20128 /**
20129  * clutter_actor_set_content_scaling_filters:
20130  * @self: a #ClutterActor
20131  * @min_filter: the minification filter for the content
20132  * @mag_filter: the magnification filter for the content
20133  *
20134  * Sets the minification and magnification filter to be applied when
20135  * scaling the #ClutterActor:content of a #ClutterActor.
20136  *
20137  * The #ClutterActor:minification-filter will be used when reducing
20138  * the size of the content; the #ClutterActor:magnification-filter
20139  * will be used when increasing the size of the content.
20140  *
20141  * Since: 1.10
20142  */
20143 void
clutter_actor_set_content_scaling_filters(ClutterActor * self,ClutterScalingFilter min_filter,ClutterScalingFilter mag_filter)20144 clutter_actor_set_content_scaling_filters (ClutterActor         *self,
20145                                            ClutterScalingFilter  min_filter,
20146                                            ClutterScalingFilter  mag_filter)
20147 {
20148   ClutterActorPrivate *priv;
20149   gboolean changed;
20150   GObject *obj;
20151 
20152   g_return_if_fail (CLUTTER_IS_ACTOR (self));
20153 
20154   priv = self->priv;
20155   obj = G_OBJECT (self);
20156 
20157   g_object_freeze_notify (obj);
20158 
20159   changed = FALSE;
20160 
20161   if (priv->min_filter != min_filter)
20162     {
20163       priv->min_filter = min_filter;
20164       changed = TRUE;
20165 
20166       g_object_notify_by_pspec (obj, obj_props[PROP_MINIFICATION_FILTER]);
20167     }
20168 
20169   if (priv->mag_filter != mag_filter)
20170     {
20171       priv->mag_filter = mag_filter;
20172       changed = TRUE;
20173 
20174       g_object_notify_by_pspec (obj, obj_props[PROP_MAGNIFICATION_FILTER]);
20175     }
20176 
20177   if (changed)
20178     clutter_actor_queue_redraw (self);
20179 
20180   g_object_thaw_notify (obj);
20181 }
20182 
20183 /**
20184  * clutter_actor_get_content_scaling_filters:
20185  * @self: a #ClutterActor
20186  * @min_filter: (out) (allow-none): return location for the minification
20187  *   filter, or %NULL
20188  * @mag_filter: (out) (allow-none): return location for the magnification
20189  *   filter, or %NULL
20190  *
20191  * Retrieves the values set using clutter_actor_set_content_scaling_filters().
20192  *
20193  * Since: 1.10
20194  */
20195 void
clutter_actor_get_content_scaling_filters(ClutterActor * self,ClutterScalingFilter * min_filter,ClutterScalingFilter * mag_filter)20196 clutter_actor_get_content_scaling_filters (ClutterActor         *self,
20197                                            ClutterScalingFilter *min_filter,
20198                                            ClutterScalingFilter *mag_filter)
20199 {
20200   g_return_if_fail (CLUTTER_IS_ACTOR (self));
20201 
20202   if (min_filter != NULL)
20203     *min_filter = self->priv->min_filter;
20204 
20205   if (mag_filter != NULL)
20206     *mag_filter = self->priv->mag_filter;
20207 }
20208 
20209 /*
20210  * clutter_actor_queue_compute_expand:
20211  * @self: a #ClutterActor
20212  *
20213  * Invalidates the needs_x_expand and needs_y_expand flags on @self
20214  * and its parents up to the top-level actor.
20215  *
20216  * This function also queues a relayout if anything changed.
20217  */
20218 static inline void
clutter_actor_queue_compute_expand(ClutterActor * self)20219 clutter_actor_queue_compute_expand (ClutterActor *self)
20220 {
20221   ClutterActor *parent;
20222   gboolean changed;
20223 
20224   if (self->priv->needs_compute_expand)
20225     return;
20226 
20227   changed = FALSE;
20228   parent = self;
20229   while (parent != NULL)
20230     {
20231       if (!parent->priv->needs_compute_expand)
20232         {
20233           parent->priv->needs_compute_expand = TRUE;
20234           changed = TRUE;
20235         }
20236 
20237       parent = parent->priv->parent;
20238     }
20239 
20240   if (changed)
20241     clutter_actor_queue_relayout (self);
20242 }
20243 
20244 /**
20245  * clutter_actor_set_x_expand:
20246  * @self: a #ClutterActor
20247  * @expand: whether the actor should expand horizontally
20248  *
20249  * Sets whether a #ClutterActor should expand horizontally; this means
20250  * that layout manager should allocate extra space for the actor, if
20251  * possible.
20252  *
20253  * Setting an actor to expand will also make all its parent expand, so
20254  * that it's possible to build an actor tree and only set this flag on
20255  * its leaves and not on every single actor.
20256  *
20257  * Since: 1.12
20258  */
20259 void
clutter_actor_set_x_expand(ClutterActor * self,gboolean expand)20260 clutter_actor_set_x_expand (ClutterActor *self,
20261                             gboolean      expand)
20262 {
20263   ClutterLayoutInfo *info;
20264 
20265   g_return_if_fail (CLUTTER_IS_ACTOR (self));
20266 
20267   expand = !!expand;
20268 
20269   info = _clutter_actor_get_layout_info (self);
20270   if (info->x_expand != expand)
20271     {
20272       info->x_expand = expand;
20273 
20274       self->priv->x_expand_set = TRUE;
20275 
20276       clutter_actor_queue_compute_expand (self);
20277 
20278       g_object_notify_by_pspec (G_OBJECT (self),
20279                                 obj_props[PROP_X_EXPAND]);
20280     }
20281 }
20282 
20283 /**
20284  * clutter_actor_get_x_expand:
20285  * @self: a #ClutterActor
20286  *
20287  * Retrieves the value set with clutter_actor_set_x_expand().
20288  *
20289  * See also: clutter_actor_needs_expand()
20290  *
20291  * Return value: %TRUE if the actor has been set to expand
20292  *
20293  * Since: 1.12
20294  */
20295 gboolean
clutter_actor_get_x_expand(ClutterActor * self)20296 clutter_actor_get_x_expand (ClutterActor *self)
20297 {
20298   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
20299 
20300   return _clutter_actor_get_layout_info_or_defaults (self)->x_expand;
20301 }
20302 
20303 /**
20304  * clutter_actor_set_y_expand:
20305  * @self: a #ClutterActor
20306  * @expand: whether the actor should expand vertically
20307  *
20308  * Sets whether a #ClutterActor should expand horizontally; this means
20309  * that layout manager should allocate extra space for the actor, if
20310  * possible.
20311  *
20312  * Setting an actor to expand will also make all its parent expand, so
20313  * that it's possible to build an actor tree and only set this flag on
20314  * its leaves and not on every single actor.
20315  *
20316  * Since: 1.12
20317  */
20318 void
clutter_actor_set_y_expand(ClutterActor * self,gboolean expand)20319 clutter_actor_set_y_expand (ClutterActor *self,
20320                             gboolean      expand)
20321 {
20322   ClutterLayoutInfo *info;
20323 
20324   g_return_if_fail (CLUTTER_IS_ACTOR (self));
20325 
20326   expand = !!expand;
20327 
20328   info = _clutter_actor_get_layout_info (self);
20329   if (info->y_expand != expand)
20330     {
20331       info->y_expand = expand;
20332 
20333       self->priv->y_expand_set = TRUE;
20334 
20335       clutter_actor_queue_compute_expand (self);
20336 
20337       g_object_notify_by_pspec (G_OBJECT (self),
20338                                 obj_props[PROP_Y_EXPAND]);
20339     }
20340 }
20341 
20342 /**
20343  * clutter_actor_get_y_expand:
20344  * @self: a #ClutterActor
20345  *
20346  * Retrieves the value set with clutter_actor_set_y_expand().
20347  *
20348  * See also: clutter_actor_needs_expand()
20349  *
20350  * Return value: %TRUE if the actor has been set to expand
20351  *
20352  * Since: 1.12
20353  */
20354 gboolean
clutter_actor_get_y_expand(ClutterActor * self)20355 clutter_actor_get_y_expand (ClutterActor *self)
20356 {
20357   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
20358 
20359   return _clutter_actor_get_layout_info_or_defaults (self)->y_expand;
20360 }
20361 
20362 static void
clutter_actor_compute_expand_recursive(ClutterActor * self,gboolean * x_expand_p,gboolean * y_expand_p)20363 clutter_actor_compute_expand_recursive (ClutterActor *self,
20364                                         gboolean     *x_expand_p,
20365                                         gboolean     *y_expand_p)
20366 {
20367   ClutterActorIter iter;
20368   ClutterActor *child;
20369   gboolean x_expand, y_expand;
20370 
20371   x_expand = y_expand = FALSE;
20372 
20373   /* note that we don't recurse into children if we're already set to expand;
20374    * this avoids traversing the whole actor tree, even if it may lead to some
20375    * child left with the needs_compute_expand flag set.
20376    */
20377   clutter_actor_iter_init (&iter, self);
20378   while (clutter_actor_iter_next (&iter, &child))
20379     {
20380       x_expand = x_expand ||
20381         clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL);
20382 
20383       y_expand = y_expand ||
20384         clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL);
20385     }
20386 
20387   *x_expand_p = x_expand;
20388   *y_expand_p = y_expand;
20389 }
20390 
20391 static void
clutter_actor_compute_expand(ClutterActor * self)20392 clutter_actor_compute_expand (ClutterActor *self)
20393 {
20394   if (self->priv->needs_compute_expand)
20395     {
20396       const ClutterLayoutInfo *info;
20397       gboolean x_expand, y_expand;
20398 
20399       info = _clutter_actor_get_layout_info_or_defaults (self);
20400 
20401       if (self->priv->x_expand_set)
20402         x_expand = info->x_expand;
20403       else
20404         x_expand = FALSE;
20405 
20406       if (self->priv->y_expand_set)
20407         y_expand = info->y_expand;
20408       else
20409         y_expand = FALSE;
20410 
20411       /* we don't need to recurse down to the children if the
20412        * actor has been forcibly set to expand
20413        */
20414       if (!(self->priv->x_expand_set && self->priv->y_expand_set))
20415         {
20416           if (self->priv->n_children != 0)
20417             {
20418               gboolean *x_expand_p, *y_expand_p;
20419               gboolean ignored = FALSE;
20420 
20421               x_expand_p = self->priv->x_expand_set ? &ignored : &x_expand;
20422               y_expand_p = self->priv->y_expand_set ? &ignored : &y_expand;
20423 
20424               clutter_actor_compute_expand_recursive (self,
20425                                                       x_expand_p,
20426                                                       y_expand_p);
20427             }
20428         }
20429 
20430       self->priv->needs_compute_expand = FALSE;
20431       self->priv->needs_x_expand = (x_expand != FALSE);
20432       self->priv->needs_y_expand = (y_expand != FALSE);
20433     }
20434 }
20435 
20436 /**
20437  * clutter_actor_needs_expand:
20438  * @self: a #ClutterActor
20439  * @orientation: the direction of expansion
20440  *
20441  * Checks whether an actor, or any of its children, is set to expand
20442  * horizontally or vertically.
20443  *
20444  * This function should only be called by layout managers that can
20445  * assign extra space to their children.
20446  *
20447  * If you want to know whether the actor was explicitly set to expand,
20448  * use clutter_actor_get_x_expand() or clutter_actor_get_y_expand().
20449  *
20450  * Return value: %TRUE if the actor should expand
20451  *
20452  * Since: 1.12
20453  */
20454 gboolean
clutter_actor_needs_expand(ClutterActor * self,ClutterOrientation orientation)20455 clutter_actor_needs_expand (ClutterActor       *self,
20456                             ClutterOrientation  orientation)
20457 {
20458   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
20459 
20460   if (!CLUTTER_ACTOR_IS_VISIBLE (self))
20461     return FALSE;
20462 
20463   if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
20464     return FALSE;
20465 
20466   clutter_actor_compute_expand (self);
20467 
20468   switch (orientation)
20469     {
20470     case CLUTTER_ORIENTATION_HORIZONTAL:
20471       return self->priv->needs_x_expand;
20472 
20473     case CLUTTER_ORIENTATION_VERTICAL:
20474       return self->priv->needs_y_expand;
20475     }
20476 
20477   return FALSE;
20478 }
20479 
20480 /**
20481  * clutter_actor_set_content_repeat:
20482  * @self: a #ClutterActor
20483  * @repeat: the repeat policy
20484  *
20485  * Sets the policy for repeating the #ClutterActor:content of a
20486  * #ClutterActor. The behaviour is deferred to the #ClutterContent
20487  * implementation.
20488  *
20489  * Since: 1.12
20490  */
20491 void
clutter_actor_set_content_repeat(ClutterActor * self,ClutterContentRepeat repeat)20492 clutter_actor_set_content_repeat (ClutterActor         *self,
20493                                   ClutterContentRepeat  repeat)
20494 {
20495   g_return_if_fail (CLUTTER_IS_ACTOR (self));
20496 
20497   if (self->priv->content_repeat == repeat)
20498     return;
20499 
20500   self->priv->content_repeat = repeat;
20501 
20502   clutter_actor_queue_redraw (self);
20503 }
20504 
20505 /**
20506  * clutter_actor_get_content_repeat:
20507  * @self: a #ClutterActor
20508  *
20509  * Retrieves the repeat policy for a #ClutterActor set by
20510  * clutter_actor_set_content_repeat().
20511  *
20512  * Return value: the content repeat policy
20513  *
20514  * Since: 1.12
20515  */
20516 ClutterContentRepeat
clutter_actor_get_content_repeat(ClutterActor * self)20517 clutter_actor_get_content_repeat (ClutterActor *self)
20518 {
20519   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_REPEAT_NONE);
20520 
20521   return self->priv->content_repeat;
20522 }
20523 
20524 void
_clutter_actor_handle_event(ClutterActor * self,const ClutterEvent * event)20525 _clutter_actor_handle_event (ClutterActor       *self,
20526                              const ClutterEvent *event)
20527 {
20528   GPtrArray *event_tree;
20529   ClutterActor *iter;
20530   gboolean is_key_event;
20531   gint i = 0;
20532 
20533   /* XXX - for historical reasons that are now lost in the mists of time,
20534    * key events are delivered regardless of whether an actor is set as
20535    * reactive; this should be changed for 2.0.
20536    */
20537   is_key_event = event->type == CLUTTER_KEY_PRESS ||
20538                  event->type == CLUTTER_KEY_RELEASE;
20539 
20540   event_tree = g_ptr_array_sized_new (64);
20541   g_ptr_array_set_free_func (event_tree, (GDestroyNotify) g_object_unref);
20542 
20543   /* build the list of of emitters for the event */
20544   iter = self;
20545   while (iter != NULL)
20546     {
20547       ClutterActor *parent = iter->priv->parent;
20548 
20549       if (CLUTTER_ACTOR_IS_REACTIVE (iter) || /* an actor must be reactive */
20550           parent == NULL ||                       /* unless it's the stage */
20551           is_key_event)                          /* or this is a key event */
20552         {
20553           /* keep a reference on the actor, so that it remains valid
20554            * for the duration of the signal emission
20555            */
20556           g_ptr_array_add (event_tree, g_object_ref (iter));
20557         }
20558 
20559       iter = parent;
20560     }
20561 
20562   /* Capture: from top-level downwards */
20563   for (i = event_tree->len - 1; i >= 0; i--)
20564     if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, TRUE))
20565       goto done;
20566 
20567   /* Bubble: from source upwards */
20568   for (i = 0; i < event_tree->len; i++)
20569     if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, FALSE))
20570       goto done;
20571 
20572 done:
20573   g_ptr_array_free (event_tree, TRUE);
20574 }
20575 
20576 static void
clutter_actor_set_child_transform_internal(ClutterActor * self,const ClutterMatrix * transform)20577 clutter_actor_set_child_transform_internal (ClutterActor        *self,
20578                                             const ClutterMatrix *transform)
20579 {
20580   ClutterTransformInfo *info = _clutter_actor_get_transform_info (self);
20581   ClutterActorIter iter;
20582   ClutterActor *child;
20583   GObject *obj;
20584   gboolean was_set = info->child_transform_set;
20585 
20586   clutter_matrix_init_from_matrix (&info->child_transform, transform);
20587 
20588   /* if it's the identity matrix, we need to toggle the boolean flag */
20589   info->child_transform_set = !cogl_matrix_is_identity (transform);
20590 
20591   /* we need to reset the transform_valid flag on each child */
20592   clutter_actor_iter_init (&iter, self);
20593   while (clutter_actor_iter_next (&iter, &child))
20594     child->priv->transform_valid = FALSE;
20595 
20596   clutter_actor_queue_redraw (self);
20597 
20598   obj = G_OBJECT (self);
20599   g_object_notify_by_pspec (obj, obj_props[PROP_CHILD_TRANSFORM]);
20600 
20601   if (was_set != info->child_transform_set)
20602     g_object_notify_by_pspec (obj, obj_props[PROP_CHILD_TRANSFORM_SET]);
20603 }
20604 
20605 /**
20606  * clutter_actor_set_child_transform:
20607  * @self: a #ClutterActor
20608  * @transform: (allow-none): a #ClutterMatrix, or %NULL
20609  *
20610  * Sets the transformation matrix to be applied to all the children
20611  * of @self prior to their own transformations. The default child
20612  * transformation is the identity matrix.
20613  *
20614  * If @transform is %NULL, the child transform will be unset.
20615  *
20616  * The #ClutterActor:child-transform property is animatable.
20617  *
20618  * Since: 1.12
20619  */
20620 void
clutter_actor_set_child_transform(ClutterActor * self,const ClutterMatrix * transform)20621 clutter_actor_set_child_transform (ClutterActor        *self,
20622                                    const ClutterMatrix *transform)
20623 {
20624   const ClutterTransformInfo *info;
20625   ClutterMatrix new_transform;
20626 
20627   g_return_if_fail (CLUTTER_IS_ACTOR (self));
20628 
20629   info = _clutter_actor_get_transform_info_or_defaults (self);
20630 
20631   if (transform != NULL)
20632     clutter_matrix_init_from_matrix (&new_transform, transform);
20633   else
20634     clutter_matrix_init_identity (&new_transform);
20635 
20636   _clutter_actor_create_transition (self, obj_props[PROP_CHILD_TRANSFORM],
20637                                     &info->child_transform,
20638                                     &new_transform);
20639 }
20640 
20641 /**
20642  * clutter_actor_get_child_transform:
20643  * @self: a #ClutterActor
20644  * @transform: (out caller-allocates): a #ClutterMatrix
20645  *
20646  * Retrieves the child transformation matrix set using
20647  * clutter_actor_set_child_transform(); if none is currently set,
20648  * the @transform matrix will be initialized to the identity matrix.
20649  *
20650  * Since: 1.12
20651  */
20652 void
clutter_actor_get_child_transform(ClutterActor * self,ClutterMatrix * transform)20653 clutter_actor_get_child_transform (ClutterActor  *self,
20654                                    ClutterMatrix *transform)
20655 {
20656   const ClutterTransformInfo *info;
20657 
20658   g_return_if_fail (CLUTTER_IS_ACTOR (self));
20659   g_return_if_fail (transform != NULL);
20660 
20661   info = _clutter_actor_get_transform_info_or_defaults (self);
20662 
20663   if (info->child_transform_set)
20664     clutter_matrix_init_from_matrix (transform, &info->child_transform);
20665   else
20666     clutter_matrix_init_identity (transform);
20667 }
20668 
20669 static void
clutter_actor_push_in_cloned_branch(ClutterActor * self)20670 clutter_actor_push_in_cloned_branch (ClutterActor *self)
20671 {
20672   ClutterActor *iter;
20673 
20674   for (iter = self->priv->first_child;
20675        iter != NULL;
20676        iter = iter->priv->next_sibling)
20677     clutter_actor_push_in_cloned_branch (iter);
20678 
20679   self->priv->in_cloned_branch += 1;
20680 }
20681 
20682 static void
clutter_actor_pop_in_cloned_branch(ClutterActor * self)20683 clutter_actor_pop_in_cloned_branch (ClutterActor *self)
20684 {
20685   ClutterActor *iter;
20686 
20687   self->priv->in_cloned_branch -= 1;
20688 
20689   for (iter = self->priv->first_child;
20690        iter != NULL;
20691        iter = iter->priv->next_sibling)
20692     clutter_actor_pop_in_cloned_branch (iter);
20693 }
20694 
20695 void
_clutter_actor_attach_clone(ClutterActor * actor,ClutterActor * clone)20696 _clutter_actor_attach_clone (ClutterActor *actor,
20697                              ClutterActor *clone)
20698 {
20699   ClutterActorPrivate *priv = actor->priv;
20700 
20701   g_assert (clone != NULL);
20702 
20703   if (priv->clones == NULL)
20704     priv->clones = g_hash_table_new (NULL, NULL);
20705 
20706   g_hash_table_add (priv->clones, clone);
20707 
20708   clutter_actor_push_in_cloned_branch (actor);
20709 }
20710 
20711 void
_clutter_actor_detach_clone(ClutterActor * actor,ClutterActor * clone)20712 _clutter_actor_detach_clone (ClutterActor *actor,
20713                              ClutterActor *clone)
20714 {
20715   ClutterActorPrivate *priv = actor->priv;
20716 
20717   g_assert (clone != NULL);
20718 
20719   if (priv->clones == NULL ||
20720       g_hash_table_lookup (priv->clones, clone) == NULL)
20721     return;
20722 
20723   clutter_actor_pop_in_cloned_branch (actor);
20724 
20725   g_hash_table_remove (priv->clones, clone);
20726 
20727   if (g_hash_table_size (priv->clones) == 0)
20728     {
20729       g_hash_table_unref (priv->clones);
20730       priv->clones = NULL;
20731     }
20732 }
20733 
20734 void
_clutter_actor_queue_redraw_on_clones(ClutterActor * self)20735 _clutter_actor_queue_redraw_on_clones (ClutterActor *self)
20736 {
20737   ClutterActorPrivate *priv = self->priv;
20738   GHashTableIter iter;
20739   gpointer key;
20740 
20741   if (priv->clones == NULL)
20742     return;
20743 
20744   g_hash_table_iter_init (&iter, priv->clones);
20745   while (g_hash_table_iter_next (&iter, &key, NULL))
20746     clutter_actor_queue_redraw (key);
20747 }
20748 
20749 void
_clutter_actor_queue_relayout_on_clones(ClutterActor * self)20750 _clutter_actor_queue_relayout_on_clones (ClutterActor *self)
20751 {
20752   ClutterActorPrivate *priv = self->priv;
20753   GHashTableIter iter;
20754   gpointer key;
20755 
20756   if (priv->clones == NULL)
20757     return;
20758 
20759   g_hash_table_iter_init (&iter, priv->clones);
20760   while (g_hash_table_iter_next (&iter, &key, NULL))
20761     clutter_actor_queue_relayout (key);
20762 }
20763 
20764 /**
20765  * clutter_actor_has_mapped_clones:
20766  * @self: a #ClutterActor
20767  *
20768  * Returns whether a #ClutterActor has any mapped clones.
20769  *
20770  * Return: %TRUE if the actor has mapped clones, and %FALSE otherwise
20771  *
20772  * Since: 1.16
20773  */
20774 gboolean
clutter_actor_has_mapped_clones(ClutterActor * self)20775 clutter_actor_has_mapped_clones (ClutterActor *self)
20776 {
20777   ClutterActorPrivate *priv;
20778   GHashTableIter iter;
20779   gpointer key;
20780 
20781   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
20782 
20783   priv = self->priv;
20784 
20785   if (priv->clones == NULL)
20786     return FALSE;
20787 
20788   g_hash_table_iter_init (&iter, priv->clones);
20789   while (g_hash_table_iter_next (&iter, &key, NULL))
20790     {
20791       if (CLUTTER_ACTOR_IS_MAPPED (key))
20792         return TRUE;
20793     }
20794 
20795   return FALSE;
20796 }
20797 
20798 CoglFramebuffer *
_clutter_actor_get_active_framebuffer(ClutterActor * self)20799 _clutter_actor_get_active_framebuffer (ClutterActor *self)
20800 {
20801   ClutterStage *stage;
20802 
20803   if (!CLUTTER_ACTOR_IN_PAINT (self))
20804     {
20805       g_critical ("The active framebuffer of actor '%s' can only be "
20806                   "retrieved during the paint sequence. Please, check "
20807                   "your code.",
20808                   _clutter_actor_get_debug_name (self));
20809       return NULL;
20810     }
20811 
20812   stage = (ClutterStage *) _clutter_actor_get_stage_internal (self);
20813   if (stage == NULL)
20814     {
20815       g_critical ("The active framebuffer of actor '%s' is only available "
20816                   "if the actor is associated to a ClutterStage.",
20817                   _clutter_actor_get_debug_name (self));
20818       return NULL;
20819     }
20820 
20821   return _clutter_stage_get_active_framebuffer (stage);
20822 }
20823 
20824 static void
clutter_actor_child_model__items_changed(GListModel * model,guint position,guint removed,guint added,gpointer user_data)20825 clutter_actor_child_model__items_changed (GListModel *model,
20826                                           guint       position,
20827                                           guint       removed,
20828                                           guint       added,
20829                                           gpointer    user_data)
20830 {
20831   ClutterActor *parent = user_data;
20832   ClutterActorPrivate *priv = parent->priv;
20833   guint i;
20834 
20835   while (removed--)
20836     {
20837       ClutterActor *child = clutter_actor_get_child_at_index (parent, position);
20838       clutter_actor_destroy (child);
20839     }
20840 
20841   for (i = 0; i < added; i++)
20842     {
20843       GObject *item = g_list_model_get_item (model, position + i);
20844       ClutterActor *child = priv->create_child_func (item, priv->create_child_data);
20845 
20846       /* The actor returned by the function can have a floating reference,
20847        * if the implementation is in pure C, or have a full reference, usually
20848        * the case for language bindings. To avoid leaking references, we
20849        * try to assume ownership of the instance, and release the reference
20850        * at the end unconditionally, leaving the only reference to the actor
20851        * itself.
20852        */
20853       if (g_object_is_floating (child))
20854         g_object_ref_sink (child);
20855 
20856       clutter_actor_insert_child_at_index (parent, child, position + i);
20857 
20858       g_object_unref (child);
20859       g_object_unref (item);
20860     }
20861 }
20862 
20863 /**
20864  * clutter_actor_bind_model:
20865  * @self: a #ClutterActor
20866  * @model: (nullable): a #GListModel
20867  * @create_child_func: a function that creates #ClutterActor instances
20868  *   from the contents of the @model
20869  * @user_data: user data passed to @create_child_func
20870  * @notify: function called when unsetting the @model
20871  *
20872  * Binds a #GListModel to a #ClutterActor.
20873  *
20874  * If the #ClutterActor was already bound to a #GListModel, the previous
20875  * binding is destroyed.
20876  *
20877  * The existing children of #ClutterActor are destroyed when setting a
20878  * model, and new children are created and added, representing the contents
20879  * of the @model. The #ClutterActor is updated whenever the @model changes.
20880  * If @model is %NULL, the #ClutterActor is left empty.
20881  *
20882  * When a #ClutterActor is bound to a model, adding and removing children
20883  * directly is undefined behaviour.
20884  *
20885  * Since: 1.24
20886  */
20887 void
clutter_actor_bind_model(ClutterActor * self,GListModel * model,ClutterActorCreateChildFunc create_child_func,gpointer user_data,GDestroyNotify notify)20888 clutter_actor_bind_model (ClutterActor                *self,
20889                           GListModel                  *model,
20890                           ClutterActorCreateChildFunc  create_child_func,
20891                           gpointer                     user_data,
20892                           GDestroyNotify               notify)
20893 {
20894   ClutterActorPrivate *priv = clutter_actor_get_instance_private (self);
20895 
20896   g_return_if_fail (CLUTTER_IS_ACTOR (self));
20897   g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
20898   g_return_if_fail (model == NULL || create_child_func != NULL);
20899 
20900   if (priv->child_model != NULL)
20901     {
20902       if (priv->create_child_notify != NULL)
20903         priv->create_child_notify (priv->create_child_data);
20904 
20905       g_signal_handlers_disconnect_by_func (priv->child_model,
20906                                             clutter_actor_child_model__items_changed,
20907                                             self);
20908       g_clear_object (&priv->child_model);
20909       priv->create_child_func = NULL;
20910       priv->create_child_data = NULL;
20911       priv->create_child_notify = NULL;
20912     }
20913 
20914   clutter_actor_destroy_all_children (self);
20915 
20916   if (model == NULL)
20917     return;
20918 
20919   priv->child_model = g_object_ref (model);
20920   priv->create_child_func = create_child_func;
20921   priv->create_child_data = user_data;
20922   priv->create_child_notify = notify;
20923 
20924   g_signal_connect (priv->child_model, "items-changed",
20925                     G_CALLBACK (clutter_actor_child_model__items_changed),
20926                     self);
20927 
20928   clutter_actor_child_model__items_changed (priv->child_model,
20929                                             0,
20930                                             0,
20931                                             g_list_model_get_n_items (priv->child_model),
20932                                             self);
20933 }
20934 
20935 typedef struct {
20936   GType child_type;
20937   GArray *props;
20938 } BindClosure;
20939 
20940 typedef struct {
20941   const char *model_property;
20942   const char *child_property;
20943   GBindingFlags flags;
20944 } BindProperty;
20945 
20946 static void
bind_closure_free(gpointer data_)20947 bind_closure_free (gpointer data_)
20948 {
20949   BindClosure *data = data_;
20950 
20951   if (data == NULL)
20952     return;
20953 
20954   g_array_unref (data->props);
20955   g_slice_free (BindClosure, data);
20956 }
20957 
20958 static ClutterActor *
bind_child_with_properties(gpointer item,gpointer data_)20959 bind_child_with_properties (gpointer item,
20960                             gpointer data_)
20961 {
20962   BindClosure *data = data_;
20963   ClutterActor *res;
20964   guint i;
20965 
20966   res = g_object_new (data->child_type, NULL);
20967 
20968   for (i = 0; i < data->props->len; i++)
20969     {
20970       const BindProperty *prop = &g_array_index (data->props, BindProperty, i);
20971 
20972       g_object_bind_property (item, prop->model_property,
20973                               res, prop->child_property,
20974                               prop->flags);
20975     }
20976 
20977   return res;
20978 }
20979 
20980 /**
20981  * clutter_actor_bind_model_with_properties:
20982  * @self: a #ClutterActor
20983  * @model: a #GListModel
20984  * @child_type: the type of #ClutterActor to use when creating
20985  *   children mapping to items inside the @model
20986  * @first_model_property: the first property of @model to bind
20987  * @...: tuples of property names on the @model, on the child, and the
20988  *   #GBindingFlags used to bind them, terminated by %NULL
20989  *
20990  * Binds a #GListModel to a #ClutterActor.
20991  *
20992  * Unlike clutter_actor_bind_model(), this function automatically creates
20993  * a child #ClutterActor of type @child_type, and binds properties on the
20994  * items inside the @model to the corresponding properties on the child,
20995  * for instance:
20996  *
20997  * |[<!-- language="C" -->
20998  *   clutter_actor_bind_model_with_properties (actor, model,
20999  *                                             MY_TYPE_CHILD_VIEW,
21000  *                                             "label", "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
21001  *                                             "icon", "image", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
21002  *                                             "selected", "selected", G_BINDING_BIDIRECTIONAL,
21003  *                                             "active", "active", G_BINDING_BIDIRECTIONAL,
21004  *                                             NULL);
21005  * ]|
21006  *
21007  * is the equivalent of calling clutter_actor_bind_model() with a
21008  * #ClutterActorCreateChildFunc of:
21009  *
21010  * |[<!-- language="C" -->
21011  *   ClutterActor *res = g_object_new (MY_TYPE_CHILD_VIEW, NULL);
21012  *
21013  *   g_object_bind_property (item, "label", res, "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
21014  *   g_object_bind_property (item, "icon", res, "image", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
21015  *   g_object_bind_property (item, "selected", res, "selected", G_BINDING_BIDIRECTIONAL);
21016  *   g_object_bind_property (item, "active", res, "active", G_BINDING_BIDIRECTIONAL);
21017  *
21018  *   return res;
21019  * ]|
21020  *
21021  * If the #ClutterActor was already bound to a #GListModel, the previous
21022  * binding is destroyed.
21023  *
21024  * When a #ClutterActor is bound to a model, adding and removing children
21025  * directly is undefined behaviour.
21026  *
21027  * See also: clutter_actor_bind_model()
21028  *
21029  * Since: 1.24
21030  */
21031 void
clutter_actor_bind_model_with_properties(ClutterActor * self,GListModel * model,GType child_type,const char * first_model_property,...)21032 clutter_actor_bind_model_with_properties (ClutterActor *self,
21033                                           GListModel   *model,
21034                                           GType         child_type,
21035                                           const char   *first_model_property,
21036                                           ...)
21037 {
21038   va_list args;
21039   BindClosure *clos;
21040   const char *model_property;
21041 
21042   g_return_if_fail (CLUTTER_IS_ACTOR (self));
21043   g_return_if_fail (G_IS_LIST_MODEL (model));
21044   g_return_if_fail (g_type_is_a (child_type, CLUTTER_TYPE_ACTOR));
21045 
21046   clos = g_slice_new0 (BindClosure);
21047   clos->child_type = child_type;
21048   clos->props = g_array_new (FALSE, FALSE, sizeof (BindProperty));
21049 
21050   va_start (args, first_model_property);
21051   model_property = first_model_property;
21052   while (model_property != NULL)
21053     {
21054       const char *child_property = va_arg (args, char *);
21055       GBindingFlags binding_flags = va_arg (args, guint);
21056       BindProperty bind;
21057 
21058       bind.model_property = g_intern_string (model_property);
21059       bind.child_property = g_intern_string (child_property);
21060       bind.flags = binding_flags;
21061 
21062       g_array_append_val (clos->props, bind);
21063 
21064       model_property = va_arg (args, char *);
21065     }
21066 
21067   clutter_actor_bind_model (self, model, bind_child_with_properties, clos, bind_closure_free);
21068 }
21069 
21070 /*< private >
21071  * clutter_actor_create_texture_paint_node:
21072  * @self: a #ClutterActor
21073  * @texture: a #CoglTexture
21074  *
21075  * Creates a #ClutterPaintNode initialized using the state of the
21076  * given #ClutterActor, ready to be used inside the implementation
21077  * of the #ClutterActorClass.paint_node virtual function.
21078  *
21079  * The returned paint node has the geometry set to the size of the
21080  * #ClutterActor:content-box property; it uses the filters specified
21081  * in the #ClutterActor:minification-filter and #ClutterActor:magnification-filter
21082  * properties; and respects the #ClutterActor:content-repeat property.
21083  *
21084  * Returns: (transfer full): The newly created #ClutterPaintNode
21085  *
21086  * Since: 1.24
21087  */
21088 ClutterPaintNode *
clutter_actor_create_texture_paint_node(ClutterActor * self,CoglTexture * texture)21089 clutter_actor_create_texture_paint_node (ClutterActor *self,
21090                                          CoglTexture  *texture)
21091 {
21092   ClutterActorPrivate *priv = clutter_actor_get_instance_private (self);
21093   ClutterPaintNode *node;
21094   ClutterActorBox box;
21095   ClutterColor color;
21096 
21097   g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
21098   g_return_val_if_fail (texture != NULL, NULL);
21099 
21100   clutter_actor_get_content_box (self, &box);
21101 
21102   /* ClutterTextureNode will premultiply the blend color, so we
21103    * want it to be white with the paint opacity
21104    */
21105   color.red = 255;
21106   color.green = 255;
21107   color.blue = 255;
21108   color.alpha = clutter_actor_get_paint_opacity_internal (self);
21109 
21110   node = clutter_texture_node_new (texture, &color, priv->min_filter, priv->mag_filter);
21111   clutter_paint_node_set_name (node, "Texture");
21112 
21113   if (priv->content_repeat == CLUTTER_REPEAT_NONE)
21114     clutter_paint_node_add_rectangle (node, &box);
21115   else
21116     {
21117       float t_w = 1.f, t_h = 1.f;
21118 
21119       if ((priv->content_repeat & CLUTTER_REPEAT_X_AXIS) != FALSE)
21120         t_w = (box.x2 - box.x1) / cogl_texture_get_width (texture);
21121 
21122       if ((priv->content_repeat & CLUTTER_REPEAT_Y_AXIS) != FALSE)
21123         t_h = (box.y2 - box.y1) / cogl_texture_get_height (texture);
21124 
21125       clutter_paint_node_add_texture_rectangle (node, &box,
21126                                                 0.f, 0.f,
21127                                                 t_w, t_h);
21128     }
21129 
21130   return node;
21131 }
21132