Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-layout-manager.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2009  Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  * Author:
22  *   Emmanuele Bassi <ebassi@linux.intel.com>
23  */
24
25 /**
26  * SECTION:clutter-layout-manager
27  * @short_description: Layout managers base class
28  *
29  * #ClutterLayoutManager is a base abstract class for layout managers. A
30  * layout manager implements the layouting policy for a composite or a
31  * container actor: it controls the preferred size of the actor to which
32  * it has been paired, and it controls the allocation of its children.
33  *
34  * Any composite or container #ClutterActor subclass can delegate the
35  * layouting of its children to a #ClutterLayoutManager. Clutter provides
36  * a generic container using #ClutterLayoutManager called #ClutterBox.
37  *
38  * Clutter provides some simple #ClutterLayoutManager sub-classes, like
39  * #ClutterFlowLayout and #ClutterBinLayout.
40  *
41  * <refsect2 id="ClutterLayoutManager-use-in-Actor">
42  *   <title>Using a Layout Manager inside an Actor</title>
43  *   <para>In order to use a #ClutterLayoutManager inside a #ClutterActor
44  *   sub-class you should invoke clutter_layout_manager_get_preferred_width()
45  *   inside the #ClutterActorClass.get_preferred_width() virtual function and
46  *   clutter_layout_manager_get_preferred_height() inside the
47  *   #ClutterActorClass.get_preferred_height() virtual functions implementation.
48  *   You should also call clutter_layout_manager_allocate() inside the
49  *   implementation of the #ClutterActorClass.allocate() virtual function.</para>
50  *   <para>In order to receive notifications for changes in the layout
51  *   manager policies you should also connect to the
52  *   #ClutterLayoutManager::layout-changed signal and queue a relayout
53  *   on your actor. The following code should be enough if the actor
54  *   does not need to perform specific operations whenever a layout
55  *   manager changes:</para>
56  *   <informalexample><programlisting>
57  *  g_signal_connect_swapped (layout_manager,
58  *                            "layout-changed",
59  *                            G_CALLBACK (clutter_actor_queue_relayout),
60  *                            actor);
61  *   </programlisting></informalexample>
62  * </refsect2>
63  *
64  * <refsect2 id="ClutterLayoutManager-implementation">
65  *   <title>Implementing a ClutterLayoutManager</title>
66  *   <para>The implementation of a layout manager does not differ from
67  *   the implementation of the size requisition and allocation bits of
68  *   #ClutterActor, so you should read the relative documentation
69  *   <link linkend="clutter-subclassing-ClutterActor">for subclassing
70  *   ClutterActor</link>.</para>
71  *   <para>The layout manager implementation can hold a back pointer
72  *   to the #ClutterContainer by implementing the
73  *   <function>set_container()</function> virtual function. The layout manager
74  *   should not hold a real reference (i.e. call g_object_ref()) on the
75  *   container actor, to avoid reference cycles.</para>
76  *   <para>If a layout manager has properties affecting the layout
77  *   policies then it should emit the #ClutterLayoutManager::layout-changed
78  *   signal on itself by using the clutter_layout_manager_layout_changed()
79  *   function whenever one of these properties changes.</para>
80  * </refsect2>
81  *
82  * <refsect2 id="ClutterLayoutManager-animation">
83  *   <title>Animating a ClutterLayoutManager</title>
84  *   <para>A layout manager is used to let a #ClutterContainer take complete
85  *   ownership over the layout (that is: the position and sizing) of its
86  *   children; this means that using the Clutter animation API, like
87  *   clutter_actor_animate(), to animate the position and sizing of a child of
88  *   a layout manager it is not going to work properly, as the animation will
89  *   automatically override any setting done by the layout manager
90  *   itself.</para>
91  *   <para>It is possible for a #ClutterLayoutManager sub-class to animate its
92  *   children layout by using the base class animation support. The
93  *   #ClutterLayoutManager animation support consists of three virtual
94  *   functions: #ClutterLayoutManagerClass.begin_animation(),
95  *   #ClutterLayoutManagerClass.get_animation_progress(), and
96  *   #ClutterLayoutManagerClass.end_animation().</para>
97  *   <variablelist>
98  *     <varlistentry>
99  *       <term><function>begin_animation (duration, easing)</function></term>
100  *       <listitem><para>This virtual function is invoked when the layout
101  *       manager should begin an animation. The implementation should set up
102  *       the state for the animation and create the ancillary objects for
103  *       animating the layout. The default implementation creates a
104  *       #ClutterTimeline for the given duration and a #ClutterAlpha binding
105  *       the timeline to the given easing mode. This function returns a
106  *       #ClutterAlpha which should be used to control the animation from
107  *       the caller perspective.</para></listitem>
108  *     </varlistentry>
109  *     <varlistentry>
110  *       <term><function>get_animation_progress()</function></term>
111  *       <listitem><para>This virtual function should be invoked when animating
112  *       a layout manager. It returns the progress of the animation, using the
113  *       same semantics as the #ClutterAlpha:alpha value.</para></listitem>
114  *     </varlistentry>
115  *     <varlistentry>
116  *       <term><function>end_animation()</function></term>
117  *       <listitem><para>This virtual function is invoked when the animation of
118  *       a layout manager ends, and it is meant to be used for bookkeeping the
119  *       objects created in the <function>begin_animation()</function>
120  *       function. The default implementation will call it implicitly when the
121  *       timeline is complete.</para></listitem>
122  *     </varlistentry>
123  *   </variablelist>
124  *   <para>The simplest way to animate a layout is to create a #ClutterTimeline
125  *   inside the <function>begin_animation()</function> virtual function, along
126  *   with a #ClutterAlpha, and for each #ClutterTimeline::new-frame signal
127  *   emission call clutter_layout_manager_layout_changed(), which will cause a
128  *   relayout. The #ClutterTimeline::completed signal emission should cause
129  *   clutter_layout_manager_end_animation() to be called. The default
130  *   implementation provided internally by #ClutterLayoutManager does exactly
131  *   this, so most sub-classes should either not override any animation-related
132  *   virtual function or simply override #ClutterLayoutManagerClass.begin_animation()
133  *   and #ClutterLayoutManagerClass.end_animation() to set up ad hoc state, and then
134  *   chain up to the parent's implementation.</para>
135  *   <example id="example-ClutterLayoutManager-animation">
136  *     <title>Animation of a Layout Manager</title>
137  *     <para>The code below shows how a #ClutterLayoutManager sub-class should
138  *     provide animating the allocation of its children from within the
139  *     #ClutterLayoutManagerClass.allocate() virtual function implementation. The
140  *     animation is computed between the last stable allocation performed
141  *     before the animation started and the desired final allocation.</para>
142  *     <para>The <varname>is_animating</varname> variable is stored inside the
143  *     #ClutterLayoutManager sub-class and it is updated by overriding the
144  *     #ClutterLayoutManagerClass.begin_animation() and the
145  *     #ClutterLayoutManagerClass.end_animation() virtual functions and chaining up
146  *     to the base class implementation.</para>
147  *     <para>The last stable allocation is stored within a #ClutterLayoutMeta
148  *     sub-class used by the implementation.</para>
149  *     <programlisting>
150  * static void
151  * my_layout_manager_allocate (ClutterLayoutManager   *manager,
152  *                             ClutterContainer       *container,
153  *                             const ClutterActorBox  *allocation,
154  *                             ClutterAllocationFlags  flags)
155  * {
156  *   MyLayoutManager *self = MY_LAYOUT_MANAGER (manager);
157  *   ClutterActor *child;
158  *
159  *   for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (container));
160  *        child != NULL;
161  *        child = clutter_actor_get_next_sibling (child))
162  *     {
163  *       ClutterLayoutMeta *meta;
164  *       MyLayoutMeta *my_meta;
165  *
166  *       /&ast; retrieve the layout meta-object &ast;/
167  *       meta = clutter_layout_manager_get_child_meta (manager,
168  *                                                     container,
169  *                                                     child);
170  *       my_meta = MY_LAYOUT_META (meta);
171  *
172  *       /&ast; compute the desired allocation for the child &ast;/
173  *       compute_allocation (self, my_meta, child,
174  *                           allocation, flags,
175  *                           &amp;child_box);
176  *
177  *       /&ast; this is the additional code that deals with the animation
178  *        &ast; of the layout manager
179  *        &ast;/
180  *       if (!self-&gt;is_animating)
181  *         {
182  *           /&ast; store the last stable allocation for later use &ast;/
183  *           my_meta-&gt;last_alloc = clutter_actor_box_copy (&amp;child_box);
184  *         }
185  *       else
186  *         {
187  *           ClutterActorBox end = { 0, };
188  *           gdouble p;
189  *
190  *           /&ast; get the progress of the animation &ast;/
191  *           p = clutter_layout_manager_get_animation_progress (manager);
192  *
193  *           if (my_meta-&gt;last_alloc != NULL)
194  *             {
195  *               /&ast; copy the desired allocation as the final state &ast;/
196  *               end = child_box;
197  *
198  *               /&ast; then interpolate the initial and final state
199  *                &ast; depending on the progress of the animation,
200  *                &ast; and put the result inside the box we will use
201  *                &ast; to allocate the child
202  *                &ast;/
203  *               clutter_actor_box_interpolate (my_meta-&gt;last_alloc,
204  *                                              &amp;end,
205  *                                              p,
206  *                                              &amp;child_box);
207  *             }
208  *           else
209  *             {
210  *               /&ast; if there is no stable allocation then the child was
211  *                &ast; added while animating; one possible course of action
212  *                &ast; is to just bail out and fall through to the allocation
213  *                &ast; to position the child directly at its final state
214  *                &ast;/
215  *               my_meta-&gt;last_alloc =
216  *                 clutter_actor_box_copy (&amp;child_box);
217  *             }
218  *         }
219  *
220  *       /&ast; allocate the child &ast;/
221  *       clutter_actor_allocate (child, &child_box, flags);
222  *     }
223  * }
224  *     </programlisting>
225  *   </example>
226  *   <para>Sub-classes of #ClutterLayoutManager that support animations of the
227  *   layout changes should call clutter_layout_manager_begin_animation()
228  *   whenever a layout property changes value, e.g.:</para>
229  *   <informalexample>
230  *     <programlisting>
231  * if (self->orientation != new_orientation)
232  *   {
233  *     ClutterLayoutManager *manager;
234  *
235  *     self->orientation = new_orientation;
236  *
237  *     manager = CLUTTER_LAYOUT_MANAGER (self);
238  *     clutter_layout_manager_layout_changed (manager);
239  *     clutter_layout_manager_begin_animation (manager, 500, CLUTTER_LINEAR);
240  *
241  *     g_object_notify (G_OBJECT (self), "orientation");
242  *   }
243  *     </programlisting>
244  *   </informalexample>
245  *   <para>The code above will animate a change in the
246  *   <varname>orientation</varname> layout property of a layout manager.</para>
247  * </refsect2>
248  *
249  * <refsect2 id="clutter-layout-properties">
250  *   <title>Layout Properties</title>
251  *   <para>If a layout manager has layout properties, that is properties that
252  *   should exist only as the result of the presence of a specific (layout
253  *   manager, container actor, child actor) combination, and it wishes to store
254  *   those properties inside a #ClutterLayoutMeta, then it should override the
255  *   #ClutterLayoutManagerClass.get_child_meta_type() virtual function to return
256  *   the #GType of the #ClutterLayoutMeta sub-class used to store the layout
257  *   properties; optionally, the #ClutterLayoutManager sub-class might also
258  *   override the #ClutterLayoutManagerClass.create_child_meta() virtual function
259  *   to control how the #ClutterLayoutMeta instance is created, otherwise the
260  *   default implementation will be equivalent to:</para>
261  *   <informalexample><programlisting>
262  *  ClutterLayoutManagerClass *klass;
263  *  GType meta_type;
264  *
265  *  klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
266  *  meta_type = klass->get_child_meta_type (manager);
267  *
268  *  return g_object_new (meta_type,
269  *                       "manager", manager,
270  *                       "container", container,
271  *                       "actor", actor,
272  *                       NULL);
273  *   </programlisting></informalexample>
274  *   <para>Where <varname>manager</varname> is the  #ClutterLayoutManager,
275  *   <varname>container</varname> is the #ClutterContainer using the
276  *   #ClutterLayoutManager and <varname>actor</varname> is the #ClutterActor
277  *   child of the #ClutterContainer.</para>
278  * </refsect2>
279  *
280  * <refsect2 id="clutter-layout-script">
281  *   <title>Using ClutterLayoutManager with ClutterScript</title>
282  *   <para>#ClutterLayoutManager instance can be created in the same way
283  *   as other objects in #ClutterScript; properties can be set using the
284  *   common syntax.</para>
285  *   <para>Layout properties can be set on children of a container with
286  *   a #ClutterLayoutManager using the <emphasis>layout::</emphasis>
287  *   modifier on the property name, for instance:</para>
288  *   <informalexample><programlisting>
289  * {
290  *   "type" : "ClutterBox",
291  *   "layout-manager" : { "type" : "ClutterTableLayout" },
292  *   "children" : [
293  *     {
294  *       "type" : "ClutterTexture",
295  *       "filename" : "image-00.png",
296  *
297  *       "layout::row" : 0,
298  *       "layout::column" : 0,
299  *       "layout::x-align" : "left",
300  *       "layout::y-align" : "center",
301  *       "layout::x-expand" : true,
302  *       "layout::y-expand" : true
303  *     },
304  *     {
305  *       "type" : "ClutterTexture",
306  *       "filename" : "image-01.png",
307  *
308  *       "layout::row" : 0,
309  *       "layout::column" : 1,
310  *       "layout::x-align" : "right",
311  *       "layout::y-align" : "center",
312  *       "layout::x-expand" : true,
313  *       "layout::y-expand" : true
314  *     }
315  *   ]
316  * }
317  *   </programlisting></informalexample>
318  * </refsect2>
319  *
320  * #ClutterLayoutManager is available since Clutter 1.2
321  */
322
323 #ifdef HAVE_CONFIG_H
324 #include "config.h"
325 #endif
326
327 #include <glib-object.h>
328 #include <gobject/gvaluecollector.h>
329
330 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
331 #include "deprecated/clutter-container.h"
332 #include "deprecated/clutter-alpha.h"
333
334 #include "clutter-debug.h"
335 #include "clutter-enum-types.h"
336 #include "clutter-layout-manager.h"
337 #include "clutter-layout-meta.h"
338 #include "clutter-marshal.h"
339 #include "clutter-private.h"
340 #include "clutter-timeline.h"
341
342 #define LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED(m,method)   G_STMT_START {  \
343         GObject *_obj = G_OBJECT (m);                                   \
344         g_warning ("Layout managers of type %s do not implement "       \
345                    "the ClutterLayoutManager::%s method",               \
346                    G_OBJECT_TYPE_NAME (_obj),                           \
347                    (method));                           } G_STMT_END
348
349 struct _ClutterLayoutManagerPrivate
350 {
351   ClutterAnimationMode easing_mode;
352   guint easing_duration;
353   guint easing_delay;
354   guint use_animations : 1;
355 };
356
357 enum
358 {
359   PROP_0,
360
361   PROP_USE_ANIMATIONS,
362   PROP_EASING_MODE,
363   PROP_EASING_DURATION,
364   PROP_EASING_DELAY,
365
366   PROP_LAST
367 };
368
369 static GParamSpec *layout_props[PROP_LAST];
370
371 enum
372 {
373   LAYOUT_CHANGED,
374
375   LAST_SIGNAL
376 };
377
378 G_DEFINE_ABSTRACT_TYPE (ClutterLayoutManager,
379                         clutter_layout_manager,
380                         G_TYPE_INITIALLY_UNOWNED);
381
382 static GQuark quark_layout_meta  = 0;
383 static GQuark quark_layout_alpha = 0;
384
385 static guint manager_signals[LAST_SIGNAL] = { 0, };
386
387 static void
388 layout_manager_freeze_layout_change (ClutterLayoutManager *manager)
389 {
390   gpointer is_frozen;
391
392   CLUTTER_NOTE (LAYOUT, "Freezing changes for manager '%s'[%p]",
393                 G_OBJECT_TYPE_NAME (manager),
394                 manager);
395
396   is_frozen = g_object_get_data (G_OBJECT (manager), "freeze-change");
397   if (is_frozen == NULL)
398     g_object_set_data (G_OBJECT (manager), "freeze-change",
399                        GUINT_TO_POINTER (1));
400   else
401     {
402       guint level = GPOINTER_TO_UINT (is_frozen) + 1;
403
404       g_object_set_data (G_OBJECT (manager), "freeze-change",
405                          GUINT_TO_POINTER (level));
406     }
407 }
408
409 static void
410 layout_manager_thaw_layout_change (ClutterLayoutManager *manager)
411 {
412   gpointer is_frozen;
413
414   is_frozen = g_object_get_data (G_OBJECT (manager), "freeze-change");
415   if (is_frozen == NULL)
416     g_critical (G_STRLOC ": Mismatched thaw; you have to call "
417                 "clutter_layout_manager_freeze_layout_change() prior to "
418                 "calling clutter_layout_manager_thaw_layout_change()");
419   else
420     {
421       guint level = GPOINTER_TO_UINT (is_frozen);
422
423       g_assert (level > 0);
424
425       CLUTTER_NOTE (LAYOUT, "Thawing changes for manager '%s'[%p]",
426                     G_OBJECT_TYPE_NAME (manager),
427                     manager);
428
429       level -= 1;
430       if (level == 0)
431         g_object_set_data (G_OBJECT (manager), "freeze-change", NULL);
432       else
433         g_object_set_data (G_OBJECT (manager), "freeze-change",
434                            GUINT_TO_POINTER (level));
435     }
436
437 }
438
439 static void
440 layout_manager_real_get_preferred_width (ClutterLayoutManager *manager,
441                                          ClutterContainer     *container,
442                                          gfloat                for_height,
443                                          gfloat               *min_width_p,
444                                          gfloat               *nat_width_p)
445 {
446   LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "get_preferred_width");
447
448   if (min_width_p)
449     *min_width_p = 0.0;
450
451   if (nat_width_p)
452     *nat_width_p = 0.0;
453 }
454
455 static void
456 layout_manager_real_get_preferred_height (ClutterLayoutManager *manager,
457                                           ClutterContainer     *container,
458                                           gfloat                for_width,
459                                           gfloat               *min_height_p,
460                                           gfloat               *nat_height_p)
461 {
462   LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "get_preferred_height");
463
464   if (min_height_p)
465     *min_height_p = 0.0;
466
467   if (nat_height_p)
468     *nat_height_p = 0.0;
469 }
470
471 static void
472 layout_manager_real_allocate (ClutterLayoutManager   *manager,
473                               ClutterContainer       *container,
474                               const ClutterActorBox  *allocation,
475                               ClutterAllocationFlags  flags)
476 {
477   LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "allocate");
478 }
479
480 static void
481 layout_manager_real_set_container (ClutterLayoutManager *manager,
482                                    ClutterContainer     *container)
483 {
484   if (container != NULL)
485     g_object_set_data (G_OBJECT (container), "clutter-layout-manager", manager);
486 }
487
488 static ClutterLayoutMeta *
489 layout_manager_real_create_child_meta (ClutterLayoutManager *manager,
490                                        ClutterContainer     *container,
491                                        ClutterActor         *actor)
492 {
493   ClutterLayoutManagerClass *klass;
494   GType meta_type;
495
496   klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
497   meta_type = klass->get_child_meta_type (manager);
498
499   /* provide a default implementation to reduce common code */
500   if (meta_type != G_TYPE_INVALID)
501     {
502       g_assert (g_type_is_a (meta_type, CLUTTER_TYPE_LAYOUT_META));
503
504       return g_object_new (meta_type,
505                            "manager", manager,
506                            "container", container,
507                            "actor", actor,
508                            NULL);
509     }
510
511   return NULL;
512 }
513
514 static GType
515 layout_manager_real_get_child_meta_type (ClutterLayoutManager *manager)
516 {
517   return G_TYPE_INVALID;
518 }
519
520 static ClutterAlpha *
521 layout_manager_real_begin_animation (ClutterLayoutManager *manager,
522                                      guint                 duration,
523                                      gulong                mode)
524 {
525   ClutterTimeline *timeline;
526   ClutterAlpha *alpha;
527
528   alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha);
529   if (alpha != NULL)
530     {
531       clutter_alpha_set_mode (alpha, mode);
532
533       timeline = clutter_alpha_get_timeline (alpha);
534       clutter_timeline_set_duration (timeline, duration);
535       clutter_timeline_rewind (timeline);
536
537       return alpha;
538     };
539
540   timeline = clutter_timeline_new (duration);
541
542   alpha = clutter_alpha_new_full (timeline, mode);
543
544   /* let the alpha take ownership of the timeline */
545   g_object_unref (timeline);
546
547   g_signal_connect_swapped (timeline, "completed",
548                             G_CALLBACK (clutter_layout_manager_end_animation),
549                             manager);
550   g_signal_connect_swapped (timeline, "new-frame",
551                             G_CALLBACK (clutter_layout_manager_layout_changed),
552                             manager);
553
554   g_object_set_qdata_full (G_OBJECT (manager),
555                            quark_layout_alpha, alpha,
556                            (GDestroyNotify) g_object_unref);
557
558   clutter_timeline_start (timeline);
559
560   return alpha;
561 }
562
563 static gdouble
564 layout_manager_real_get_animation_progress (ClutterLayoutManager *manager)
565 {
566   ClutterAlpha *alpha;
567
568   alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha);
569   if (alpha == NULL)
570     return 1.0;
571
572   return clutter_alpha_get_alpha (alpha);
573 }
574
575 static void
576 layout_manager_real_end_animation (ClutterLayoutManager *manager)
577 {
578   ClutterTimeline *timeline;
579   ClutterAlpha *alpha;
580
581   alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha);
582   if (alpha == NULL)
583     return;
584
585   timeline = clutter_alpha_get_timeline (alpha);
586   g_assert (timeline != NULL);
587
588   if (clutter_timeline_is_playing (timeline))
589     clutter_timeline_stop (timeline);
590
591   g_signal_handlers_disconnect_by_func (timeline,
592                                         G_CALLBACK (clutter_layout_manager_end_animation),
593                                         manager);
594   g_signal_handlers_disconnect_by_func (timeline,
595                                         G_CALLBACK (clutter_layout_manager_layout_changed),
596                                         manager);
597
598   g_object_set_qdata (G_OBJECT (manager), quark_layout_alpha, NULL);
599
600   clutter_layout_manager_layout_changed (manager);
601 }
602
603 static void
604 layout_manager_set_property (GObject      *gobject,
605                              guint         prop_id,
606                              const GValue *value,
607                              GParamSpec   *pspec)
608 {
609   ClutterLayoutManager *self = CLUTTER_LAYOUT_MANAGER (gobject);
610
611   switch (prop_id)
612     {
613     case PROP_USE_ANIMATIONS:
614       clutter_layout_manager_set_use_animations (self,
615                                                  g_value_get_boolean (value));
616       break;
617
618     case PROP_EASING_MODE:
619       clutter_layout_manager_set_easing_mode (self, g_value_get_enum (value));
620       break;
621
622     case PROP_EASING_DURATION:
623       clutter_layout_manager_set_easing_duration (self,
624                                                   g_value_get_uint (value));
625       break;
626
627     case PROP_EASING_DELAY:
628       clutter_layout_manager_set_easing_delay (self, g_value_get_uint (value));
629       break;
630
631     default:
632       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
633       break;
634     }
635 }
636
637 static void
638 layout_manager_get_property (GObject    *gobject,
639                              guint       prop_id,
640                              GValue     *value,
641                              GParamSpec *pspec)
642 {
643   ClutterLayoutManagerPrivate *priv = CLUTTER_LAYOUT_MANAGER (gobject)->priv;
644
645   switch (prop_id)
646     {
647     case PROP_USE_ANIMATIONS:
648       g_value_set_boolean (value, priv->use_animations);
649       break;
650
651     case PROP_EASING_MODE:
652       g_value_set_enum (value, priv->easing_mode);
653       break;
654
655     case PROP_EASING_DURATION:
656       g_value_set_uint (value, priv->easing_duration);
657       break;
658
659     case PROP_EASING_DELAY:
660       g_value_set_uint (value, priv->easing_delay);
661       break;
662
663     default:
664       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
665       break;
666     }
667 }
668
669 static void
670 clutter_layout_manager_class_init (ClutterLayoutManagerClass *klass)
671 {
672   GObjectClass *object_class = G_OBJECT_CLASS (klass);
673
674   quark_layout_meta =
675     g_quark_from_static_string ("clutter-layout-manager-child-meta");
676   quark_layout_alpha =
677     g_quark_from_static_string ("clutter-layout-manager-alpha");
678
679   g_type_class_add_private (klass, sizeof (ClutterLayoutManagerPrivate));
680
681   object_class->set_property = layout_manager_set_property;
682   object_class->get_property = layout_manager_get_property;
683
684   klass->get_preferred_width = layout_manager_real_get_preferred_width;
685   klass->get_preferred_height = layout_manager_real_get_preferred_height;
686   klass->allocate = layout_manager_real_allocate;
687   klass->create_child_meta = layout_manager_real_create_child_meta;
688   klass->get_child_meta_type = layout_manager_real_get_child_meta_type;
689   klass->begin_animation = layout_manager_real_begin_animation;
690   klass->get_animation_progress = layout_manager_real_get_animation_progress;
691   klass->end_animation = layout_manager_real_end_animation;
692   klass->set_container = layout_manager_real_set_container;
693
694   /**
695    * ClutterLayoutManager:use-animations:
696    *
697    * Whether the #ClutterLayoutManager should animate changes in the
698    * layout, overriding the easing state of the children.
699    *
700    * Since: 1.12
701    */
702   layout_props[PROP_USE_ANIMATIONS] =
703     g_param_spec_boolean ("use-animations",
704                           P_("Use Animations"),
705                           P_("Whether layout changes should be animated"),
706                           FALSE,
707                           CLUTTER_PARAM_READWRITE);
708
709   /**
710    * ClutterLayoutManager:easing-mode:
711    *
712    * The easing mode for the animations, in case
713    * #ClutterLayoutManager:use-animations is set to %TRUE.
714    *
715    * The easing mode has the same semantics of #ClutterAnimation:mode: it can
716    * either be a value from the #ClutterAnimationMode enumeration, like
717    * %CLUTTER_EASE_OUT_CUBIC, or a logical id as returned by
718    * clutter_alpha_register_func().
719    *
720    * The default value is %CLUTTER_EASE_OUT_CUBIC.
721    *
722    * Since: 1.12
723    */
724   layout_props[PROP_EASING_MODE] =
725     g_param_spec_enum ("easing-mode",
726                        P_("Easing Mode"),
727                        P_("The easing mode of the animations"),
728                        CLUTTER_TYPE_ANIMATION_MODE,
729                        CLUTTER_EASE_OUT_CUBIC,
730                        CLUTTER_PARAM_READWRITE);
731
732   /**
733    * ClutterLayoutManager:easing-duration:
734    *
735    * The duration of the animations, in case
736    * #ClutterLayoutManager:use-animations is set to %TRUE.
737    *
738    * The duration is expressed in milliseconds.
739    *
740    * Since: 1.12
741    */
742   layout_props[PROP_EASING_DURATION] =
743     g_param_spec_uint ("easing-duration",
744                        P_("Easing Duration"),
745                        P_("The duration of the animations"),
746                        0, G_MAXUINT, 250,
747                        CLUTTER_PARAM_READWRITE);
748
749   /**
750    * ClutterLayoutManager:easing-delay:
751    *
752    * The delay befor the animations will start,
753    * #ClutterLayoutManager:use-animations is set to %TRUE.
754    *
755    * The duration is expressed in milliseconds.
756    *
757    * Since: 1.12
758    */
759   layout_props[PROP_EASING_DELAY] =
760     g_param_spec_uint ("easing-delay",
761                        P_("Easing Delay"),
762                        P_("The delay befor the animations start"),
763                        0, G_MAXUINT, 0,
764                        CLUTTER_PARAM_READWRITE);
765
766   g_object_class_install_properties (object_class, PROP_LAST, layout_props);
767
768   /**
769    * ClutterLayoutManager::layout-changed:
770    * @manager: the #ClutterLayoutManager that emitted the signal
771    *
772    * The ::layout-changed signal is emitted each time a layout manager
773    * has been changed. Every #ClutterActor using the @manager instance
774    * as a layout manager should connect a handler to the ::layout-changed
775    * signal and queue a relayout on themselves:
776    *
777    * |[
778    *   static void layout_changed (ClutterLayoutManager *manager,
779    *                               ClutterActor         *self)
780    *   {
781    *     clutter_actor_queue_relayout (self);
782    *   }
783    *   ...
784    *     self->manager = g_object_ref_sink (manager);
785    *     g_signal_connect (self->manager, "layout-changed",
786    *                       G_CALLBACK (layout_changed),
787    *                       self);
788    * ]|
789    *
790    * Sub-classes of #ClutterLayoutManager that implement a layout that
791    * can be controlled or changed using parameters should emit the
792    * ::layout-changed signal whenever one of the parameters changes,
793    * by using clutter_layout_manager_layout_changed().
794    *
795    * Since: 1.2
796    */
797   manager_signals[LAYOUT_CHANGED] =
798     g_signal_new (I_("layout-changed"),
799                   G_TYPE_FROM_CLASS (klass),
800                   G_SIGNAL_RUN_LAST,
801                   G_STRUCT_OFFSET (ClutterLayoutManagerClass,
802                                    layout_changed),
803                   NULL, NULL,
804                   _clutter_marshal_VOID__VOID,
805                   G_TYPE_NONE, 0);
806 }
807
808 static void
809 clutter_layout_manager_init (ClutterLayoutManager *manager)
810 {
811   ClutterLayoutManagerPrivate *priv;
812
813   manager->priv = priv =
814     G_TYPE_INSTANCE_GET_PRIVATE (manager, CLUTTER_TYPE_LAYOUT_MANAGER,
815                                  ClutterLayoutManagerPrivate);
816
817   priv->use_animations = FALSE;
818   priv->easing_mode = CLUTTER_EASE_OUT_CUBIC;
819   priv->easing_duration = 250;
820   priv->easing_delay = 0;
821 }
822
823 /**
824  * clutter_layout_manager_get_preferred_width:
825  * @manager: a #ClutterLayoutManager
826  * @container: the #ClutterContainer using @manager
827  * @for_height: the height for which the width should be computed, or -1
828  * @min_width_p: (out) (allow-none): return location for the minimum width
829  *   of the layout, or %NULL
830  * @nat_width_p: (out) (allow-none): return location for the natural width
831  *   of the layout, or %NULL
832  *
833  * Computes the minimum and natural widths of the @container according
834  * to @manager.
835  *
836  * See also clutter_actor_get_preferred_width()
837  *
838  * Since: 1.2
839  */
840 void
841 clutter_layout_manager_get_preferred_width (ClutterLayoutManager *manager,
842                                             ClutterContainer     *container,
843                                             gfloat                for_height,
844                                             gfloat               *min_width_p,
845                                             gfloat               *nat_width_p)
846 {
847   ClutterLayoutManagerClass *klass;
848
849   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
850   g_return_if_fail (CLUTTER_IS_CONTAINER (container));
851
852   klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
853   klass->get_preferred_width (manager, container, for_height,
854                               min_width_p,
855                               nat_width_p);
856 }
857
858 /**
859  * clutter_layout_manager_get_preferred_height:
860  * @manager: a #ClutterLayoutManager
861  * @container: the #ClutterContainer using @manager
862  * @for_width: the width for which the height should be computed, or -1
863  * @min_height_p: (out) (allow-none): return location for the minimum height
864  *   of the layout, or %NULL
865  * @nat_height_p: (out) (allow-none): return location for the natural height
866  *   of the layout, or %NULL
867  *
868  * Computes the minimum and natural heights of the @container according
869  * to @manager.
870  *
871  * See also clutter_actor_get_preferred_height()
872  *
873  * Since: 1.2
874  */
875 void
876 clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager,
877                                              ClutterContainer     *container,
878                                              gfloat                for_width,
879                                              gfloat               *min_height_p,
880                                              gfloat               *nat_height_p)
881 {
882   ClutterLayoutManagerClass *klass;
883
884   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
885   g_return_if_fail (CLUTTER_IS_CONTAINER (container));
886
887   klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
888   klass->get_preferred_height (manager, container, for_width,
889                                min_height_p,
890                                nat_height_p);
891 }
892
893 /**
894  * clutter_layout_manager_allocate:
895  * @manager: a #ClutterLayoutManager
896  * @container: the #ClutterContainer using @manager
897  * @allocation: the #ClutterActorBox containing the allocated area
898  *   of @container
899  * @flags: the allocation flags
900  *
901  * Allocates the children of @container given an area
902  *
903  * See also clutter_actor_allocate()
904  *
905  * Since: 1.2
906  */
907 void
908 clutter_layout_manager_allocate (ClutterLayoutManager   *manager,
909                                  ClutterContainer       *container,
910                                  const ClutterActorBox  *allocation,
911                                  ClutterAllocationFlags  flags)
912 {
913   ClutterLayoutManagerClass *klass;
914
915   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
916   g_return_if_fail (CLUTTER_IS_CONTAINER (container));
917   g_return_if_fail (allocation != NULL);
918
919   klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
920   klass->allocate (manager, container, allocation, flags);
921 }
922
923 /**
924  * clutter_layout_manager_layout_changed:
925  * @manager: a #ClutterLayoutManager
926  *
927  * Emits the #ClutterLayoutManager::layout-changed signal on @manager
928  *
929  * This function should only be called by implementations of the
930  * #ClutterLayoutManager class
931  *
932  * Since: 1.2
933  */
934 void
935 clutter_layout_manager_layout_changed (ClutterLayoutManager *manager)
936 {
937   gpointer is_frozen;
938
939   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
940
941   is_frozen = g_object_get_data (G_OBJECT (manager), "freeze-change");
942   if (is_frozen == NULL)
943     g_signal_emit (manager, manager_signals[LAYOUT_CHANGED], 0);
944   else
945     CLUTTER_NOTE (LAYOUT, "Layout manager '%s'[%p] has been frozen",
946                   G_OBJECT_TYPE_NAME (manager),
947                   manager);
948 }
949
950 /**
951  * clutter_layout_manager_set_container:
952  * @manager: a #ClutterLayoutManager
953  * @container: (allow-none): a #ClutterContainer using @manager
954  *
955  * If the #ClutterLayoutManager sub-class allows it, allow
956  * adding a weak reference of the @container using @manager
957  * from within the layout manager
958  *
959  * The layout manager should not increase the reference
960  * count of the @container
961  *
962  * Since: 1.2
963  */
964 void
965 clutter_layout_manager_set_container (ClutterLayoutManager *manager,
966                                       ClutterContainer     *container)
967 {
968   ClutterLayoutManagerClass *klass;
969
970   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
971   g_return_if_fail (container == NULL || CLUTTER_IS_CONTAINER (container));
972
973   klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
974   if (klass->set_container)
975     klass->set_container (manager, container);
976 }
977
978 GType
979 _clutter_layout_manager_get_child_meta_type (ClutterLayoutManager *manager)
980 {
981   return CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager)->get_child_meta_type (manager);
982 }
983
984 static inline ClutterLayoutMeta *
985 create_child_meta (ClutterLayoutManager *manager,
986                    ClutterContainer     *container,
987                    ClutterActor         *actor)
988 {
989   ClutterLayoutManagerClass *klass;
990   ClutterLayoutMeta *meta = NULL;
991
992   layout_manager_freeze_layout_change (manager);
993
994   klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
995   if (klass->get_child_meta_type (manager) != G_TYPE_INVALID)
996     meta = klass->create_child_meta (manager, container, actor);
997
998   layout_manager_thaw_layout_change (manager);
999
1000   return meta;
1001 }
1002
1003 static inline ClutterLayoutMeta *
1004 get_child_meta (ClutterLayoutManager *manager,
1005                 ClutterContainer     *container,
1006                 ClutterActor         *actor)
1007 {
1008   ClutterLayoutMeta *layout = NULL;
1009
1010   layout = g_object_get_qdata (G_OBJECT (actor), quark_layout_meta);
1011   if (layout != NULL)
1012     {
1013       ClutterChildMeta *child = CLUTTER_CHILD_META (layout);
1014
1015       if (layout->manager == manager &&
1016           child->container == container &&
1017           child->actor == actor)
1018         return layout;
1019
1020       /* if the LayoutMeta referenced is not attached to the
1021        * layout manager then we simply ask the layout manager
1022        * to replace it with the right one
1023        */
1024     }
1025
1026   layout = create_child_meta (manager, container, actor);
1027   if (layout != NULL)
1028     {
1029       g_assert (CLUTTER_IS_LAYOUT_META (layout));
1030       g_object_set_qdata_full (G_OBJECT (actor), quark_layout_meta,
1031                                layout,
1032                                (GDestroyNotify) g_object_unref);
1033       return layout;
1034     }
1035
1036   return NULL;
1037 }
1038
1039 /**
1040  * clutter_layout_manager_get_child_meta:
1041  * @manager: a #ClutterLayoutManager
1042  * @container: a #ClutterContainer using @manager
1043  * @actor: a #ClutterActor child of @container
1044  *
1045  * Retrieves the #ClutterLayoutMeta that the layout @manager associated
1046  * to the @actor child of @container, eventually by creating one if the
1047  * #ClutterLayoutManager supports layout properties
1048  *
1049  * Return value: (transfer none): a #ClutterLayoutMeta, or %NULL if the
1050  *   #ClutterLayoutManager does not have layout properties. The returned
1051  *   layout meta instance is owned by the #ClutterLayoutManager and it
1052  *   should not be unreferenced
1053  *
1054  * Since: 1.0
1055  */
1056 ClutterLayoutMeta *
1057 clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager,
1058                                        ClutterContainer     *container,
1059                                        ClutterActor         *actor)
1060 {
1061   g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), NULL);
1062   g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), NULL);
1063   g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL);
1064
1065   return get_child_meta (manager, container, actor);
1066 }
1067
1068 static inline gboolean
1069 layout_set_property_internal (ClutterLayoutManager *manager,
1070                               GObject              *gobject,
1071                               GParamSpec           *pspec,
1072                               const GValue         *value)
1073 {
1074   if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
1075     {
1076       g_warning ("%s: Child property '%s' of the layout manager of "
1077                  "type '%s' is constructor-only",
1078                  G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager));
1079       return FALSE;
1080     }
1081
1082   if (!(pspec->flags & G_PARAM_WRITABLE))
1083     {
1084       g_warning ("%s: Child property '%s' of the layout manager of "
1085                  "type '%s' is not writable",
1086                  G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager));
1087       return FALSE;
1088     }
1089
1090   g_object_set_property (gobject, pspec->name, value);
1091
1092   return TRUE;
1093 }
1094
1095 static inline gboolean
1096 layout_get_property_internal (ClutterLayoutManager *manager,
1097                               GObject              *gobject,
1098                               GParamSpec           *pspec,
1099                               GValue               *value)
1100 {
1101   if (!(pspec->flags & G_PARAM_READABLE))
1102     {
1103       g_warning ("%s: Child property '%s' of the layout manager of "
1104                  "type '%s' is not readable",
1105                  G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager));
1106       return FALSE;
1107     }
1108
1109   g_object_get_property (gobject, pspec->name, value);
1110
1111   return TRUE;
1112 }
1113
1114 /**
1115  * clutter_layout_manager_child_set:
1116  * @manager: a #ClutterLayoutManager
1117  * @container: a #ClutterContainer using @manager
1118  * @actor: a #ClutterActor child of @container
1119  * @first_property: the first property name
1120  * @...: a list of property name and value pairs
1121  *
1122  * Sets a list of properties and their values on the #ClutterLayoutMeta
1123  * associated by @manager to a child of @container
1124  *
1125  * Languages bindings should use clutter_layout_manager_child_set_property()
1126  * instead
1127  *
1128  * Since: 1.2
1129  */
1130 void
1131 clutter_layout_manager_child_set (ClutterLayoutManager *manager,
1132                                   ClutterContainer     *container,
1133                                   ClutterActor         *actor,
1134                                   const gchar          *first_property,
1135                                   ...)
1136 {
1137   ClutterLayoutMeta *meta;
1138   GObjectClass *klass;
1139   const gchar *pname;
1140   va_list var_args;
1141
1142   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
1143   g_return_if_fail (CLUTTER_IS_CONTAINER (container));
1144   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
1145   g_return_if_fail (first_property != NULL);
1146
1147   meta = get_child_meta (manager, container, actor);
1148   if (meta == NULL)
1149     {
1150       g_warning ("Layout managers of type '%s' do not support "
1151                  "layout metadata",
1152                  g_type_name (G_OBJECT_TYPE (manager)));
1153       return;
1154     }
1155
1156   klass = G_OBJECT_GET_CLASS (meta);
1157
1158   va_start (var_args, first_property);
1159
1160   pname = first_property;
1161   while (pname)
1162     {
1163       GValue value = G_VALUE_INIT;
1164       GParamSpec *pspec;
1165       gchar *error;
1166       gboolean res;
1167
1168       pspec = g_object_class_find_property (klass, pname);
1169       if (pspec == NULL)
1170         {
1171           g_warning ("%s: Layout managers of type '%s' have no layout "
1172                      "property named '%s'",
1173                      G_STRLOC, G_OBJECT_TYPE_NAME (manager), pname);
1174           break;
1175         }
1176
1177       G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec),
1178                             var_args, 0,
1179                             &error);
1180
1181       if (error)
1182         {
1183           g_warning ("%s: %s", G_STRLOC, error);
1184           g_free (error);
1185           break;
1186         }
1187
1188       res = layout_set_property_internal (manager, G_OBJECT (meta),
1189                                           pspec,
1190                                           &value);
1191
1192       g_value_unset (&value);
1193
1194       if (!res)
1195         break;
1196
1197       pname = va_arg (var_args, gchar*);
1198     }
1199
1200   va_end (var_args);
1201 }
1202
1203 /**
1204  * clutter_layout_manager_child_set_property:
1205  * @manager: a #ClutterLayoutManager
1206  * @container: a #ClutterContainer using @manager
1207  * @actor: a #ClutterActor child of @container
1208  * @property_name: the name of the property to set
1209  * @value: a #GValue with the value of the property to set
1210  *
1211  * Sets a property on the #ClutterLayoutMeta created by @manager and
1212  * attached to a child of @container
1213  *
1214  * Since: 1.2
1215  */
1216 void
1217 clutter_layout_manager_child_set_property (ClutterLayoutManager *manager,
1218                                            ClutterContainer     *container,
1219                                            ClutterActor         *actor,
1220                                            const gchar          *property_name,
1221                                            const GValue         *value)
1222 {
1223   ClutterLayoutMeta *meta;
1224   GObjectClass *klass;
1225   GParamSpec *pspec;
1226
1227   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
1228   g_return_if_fail (CLUTTER_IS_CONTAINER (container));
1229   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
1230   g_return_if_fail (property_name != NULL);
1231   g_return_if_fail (value != NULL);
1232
1233   meta = get_child_meta (manager, container, actor);
1234   if (meta == NULL)
1235     {
1236       g_warning ("Layout managers of type '%s' do not support "
1237                  "layout metadata",
1238                  g_type_name (G_OBJECT_TYPE (manager)));
1239       return;
1240     }
1241
1242   klass = G_OBJECT_GET_CLASS (meta);
1243
1244   pspec = g_object_class_find_property (klass, property_name);
1245   if (pspec == NULL)
1246     {
1247       g_warning ("%s: Layout managers of type '%s' have no layout "
1248                  "property named '%s'",
1249                  G_STRLOC, G_OBJECT_TYPE_NAME (manager), property_name);
1250       return;
1251     }
1252
1253   layout_set_property_internal (manager, G_OBJECT (meta), pspec, value);
1254 }
1255
1256 /**
1257  * clutter_layout_manager_child_get:
1258  * @manager: a #ClutterLayoutManager
1259  * @container: a #ClutterContainer using @manager
1260  * @actor: a #ClutterActor child of @container
1261  * @first_property: the name of the first property
1262  * @...: a list of property name and return location for the value pairs
1263  *
1264  * Retrieves the values for a list of properties out of the
1265  * #ClutterLayoutMeta created by @manager and attached to the
1266  * child of a @container
1267  *
1268  * Since: 1.2
1269  */
1270 void
1271 clutter_layout_manager_child_get (ClutterLayoutManager *manager,
1272                                   ClutterContainer     *container,
1273                                   ClutterActor         *actor,
1274                                   const gchar          *first_property,
1275                                   ...)
1276 {
1277   ClutterLayoutMeta *meta;
1278   GObjectClass *klass;
1279   const gchar *pname;
1280   va_list var_args;
1281
1282   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
1283   g_return_if_fail (CLUTTER_IS_CONTAINER (container));
1284   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
1285   g_return_if_fail (first_property != NULL);
1286
1287   meta = get_child_meta (manager, container, actor);
1288   if (meta == NULL)
1289     {
1290       g_warning ("Layout managers of type '%s' do not support "
1291                  "layout metadata",
1292                  g_type_name (G_OBJECT_TYPE (manager)));
1293       return;
1294     }
1295
1296   klass = G_OBJECT_GET_CLASS (meta);
1297
1298   va_start (var_args, first_property);
1299
1300   pname = first_property;
1301   while (pname)
1302     {
1303       GValue value = G_VALUE_INIT;
1304       GParamSpec *pspec;
1305       gchar *error;
1306       gboolean res;
1307
1308       pspec = g_object_class_find_property (klass, pname);
1309       if (pspec == NULL)
1310         {
1311           g_warning ("%s: Layout managers of type '%s' have no layout "
1312                      "property named '%s'",
1313                      G_STRLOC, G_OBJECT_TYPE_NAME (manager), pname);
1314           break;
1315         }
1316
1317       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1318
1319       res = layout_get_property_internal (manager, G_OBJECT (meta),
1320                                           pspec,
1321                                           &value);
1322       if (!res)
1323         {
1324           g_value_unset (&value);
1325           break;
1326         }
1327
1328       G_VALUE_LCOPY (&value, var_args, 0, &error);
1329       if (error)
1330         {
1331           g_warning ("%s: %s", G_STRLOC, error);
1332           g_free (error);
1333           g_value_unset (&value);
1334           break;
1335         }
1336
1337       g_value_unset (&value);
1338
1339       pname = va_arg (var_args, gchar*);
1340     }
1341
1342   va_end (var_args);
1343 }
1344
1345 /**
1346  * clutter_layout_manager_child_get_property:
1347  * @manager: a #ClutterLayoutManager
1348  * @container: a #ClutterContainer using @manager
1349  * @actor: a #ClutterActor child of @container
1350  * @property_name: the name of the property to get
1351  * @value: a #GValue with the value of the property to get
1352  *
1353  * Gets a property on the #ClutterLayoutMeta created by @manager and
1354  * attached to a child of @container
1355  *
1356  * The #GValue must already be initialized to the type of the property
1357  * and has to be unset with g_value_unset() after extracting the real
1358  * value out of it
1359  *
1360  * Since: 1.2
1361  */
1362 void
1363 clutter_layout_manager_child_get_property (ClutterLayoutManager *manager,
1364                                            ClutterContainer     *container,
1365                                            ClutterActor         *actor,
1366                                            const gchar          *property_name,
1367                                            GValue               *value)
1368 {
1369   ClutterLayoutMeta *meta;
1370   GObjectClass *klass;
1371   GParamSpec *pspec;
1372
1373   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
1374   g_return_if_fail (CLUTTER_IS_CONTAINER (container));
1375   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
1376   g_return_if_fail (property_name != NULL);
1377   g_return_if_fail (value != NULL);
1378
1379   meta = get_child_meta (manager, container, actor);
1380   if (meta == NULL)
1381     {
1382       g_warning ("Layout managers of type %s do not support "
1383                  "layout metadata",
1384                  g_type_name (G_OBJECT_TYPE (manager)));
1385       return;
1386     }
1387
1388   klass = G_OBJECT_GET_CLASS (meta);
1389
1390   pspec = g_object_class_find_property (klass, property_name);
1391   if (pspec == NULL)
1392     {
1393       g_warning ("%s: Layout managers of type '%s' have no layout "
1394                  "property named '%s'",
1395                  G_STRLOC, G_OBJECT_TYPE_NAME (manager), property_name);
1396       return;
1397     }
1398
1399   layout_get_property_internal (manager, G_OBJECT (meta), pspec, value);
1400 }
1401
1402 /**
1403  * clutter_layout_manager_find_child_property:
1404  * @manager: a #ClutterLayoutManager
1405  * @name: the name of the property
1406  *
1407  * Retrieves the #GParamSpec for the layout property @name inside
1408  * the #ClutterLayoutMeta sub-class used by @manager
1409  *
1410  * Return value: (transfer none): a #GParamSpec describing the property,
1411  *   or %NULL if no property with that name exists. The returned
1412  *   #GParamSpec is owned by the layout manager and should not be
1413  *   modified or freed
1414  *
1415  * Since: 1.2
1416  */
1417 GParamSpec *
1418 clutter_layout_manager_find_child_property (ClutterLayoutManager *manager,
1419                                             const gchar          *name)
1420 {
1421   ClutterLayoutManagerClass *klass;
1422   GObjectClass *meta_klass;
1423   GParamSpec *pspec;
1424   GType meta_type;
1425
1426   klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
1427   meta_type = klass->get_child_meta_type (manager);
1428   if (meta_type == G_TYPE_INVALID)
1429     return NULL;
1430
1431   meta_klass = g_type_class_ref (meta_type);
1432
1433   pspec = g_object_class_find_property (meta_klass, name);
1434
1435   g_type_class_unref (meta_klass);
1436
1437   return pspec;
1438 }
1439
1440 /**
1441  * clutter_layout_manager_list_child_properties:
1442  * @manager: a #ClutterLayoutManager
1443  * @n_pspecs: (out): return location for the number of returned
1444  *   #GParamSpec<!-- -->s
1445  *
1446  * Retrieves all the #GParamSpec<!-- -->s for the layout properties
1447  * stored inside the #ClutterLayoutMeta sub-class used by @manager
1448  *
1449  * Return value: (transfer full) (array length=n_pspecs): the newly-allocated,
1450  *   %NULL-terminated array of #GParamSpec<!-- -->s. Use g_free() to free the
1451  *   resources allocated for the array
1452  *
1453  * Since: 1.2
1454  */
1455 GParamSpec **
1456 clutter_layout_manager_list_child_properties (ClutterLayoutManager *manager,
1457                                               guint                *n_pspecs)
1458 {
1459   ClutterLayoutManagerClass *klass;
1460   GObjectClass *meta_klass;
1461   GParamSpec **pspecs;
1462   GType meta_type;
1463
1464   klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
1465   meta_type = klass->get_child_meta_type (manager);
1466   if (meta_type == G_TYPE_INVALID)
1467     return NULL;
1468
1469   meta_klass = g_type_class_ref (meta_type);
1470
1471   pspecs = g_object_class_list_properties (meta_klass, n_pspecs);
1472
1473   g_type_class_unref (meta_klass);
1474
1475   return pspecs;
1476 }
1477
1478 /**
1479  * clutter_layout_manager_begin_animation:
1480  * @manager: a #ClutterLayoutManager
1481  * @duration: the duration of the animation, in milliseconds
1482  * @mode: the easing mode of the animation
1483  *
1484  * Begins an animation of @duration milliseconds, using the provided
1485  * easing @mode
1486  *
1487  * The easing mode can be specified either as a #ClutterAnimationMode
1488  * or as a logical id returned by clutter_alpha_register_func()
1489  *
1490  * The result of this function depends on the @manager implementation
1491  *
1492  * Return value: (transfer none): The #ClutterAlpha created by the
1493  *   layout manager; the returned instance is owned by the layout
1494  *   manager and should not be unreferenced
1495  *
1496  * Since: 1.2
1497  *
1498  * Deprecated: 1.12
1499  */
1500 ClutterAlpha *
1501 clutter_layout_manager_begin_animation (ClutterLayoutManager *manager,
1502                                         guint                 duration,
1503                                         gulong                mode)
1504 {
1505   ClutterLayoutManagerClass *klass;
1506
1507   g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), NULL);
1508
1509   klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
1510
1511   return klass->begin_animation (manager, duration, mode);
1512 }
1513
1514 /**
1515  * clutter_layout_manager_end_animation:
1516  * @manager: a #ClutterLayoutManager
1517  *
1518  * Ends an animation started by clutter_layout_manager_begin_animation()
1519  *
1520  * The result of this call depends on the @manager implementation
1521  *
1522  * Since: 1.2
1523  *
1524  * Deprecated: 1.12
1525  */
1526 void
1527 clutter_layout_manager_end_animation (ClutterLayoutManager *manager)
1528 {
1529   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
1530
1531   CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager)->end_animation (manager);
1532 }
1533
1534 /**
1535  * clutter_layout_manager_get_animation_progress:
1536  * @manager: a #ClutterLayoutManager
1537  *
1538  * Retrieves the progress of the animation, if one has been started by
1539  * clutter_layout_manager_begin_animation()
1540  *
1541  * The returned value has the same semantics of the #ClutterAlpha:alpha
1542  * value
1543  *
1544  * Return value: the progress of the animation
1545  *
1546  * Since: 1.2
1547  *
1548  * Deprecated: 1.12
1549  */
1550 gdouble
1551 clutter_layout_manager_get_animation_progress (ClutterLayoutManager *manager)
1552 {
1553   ClutterLayoutManagerClass *klass;
1554
1555   g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), 1.0);
1556
1557   klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager);
1558
1559   return klass->get_animation_progress (manager);
1560 }
1561
1562 /**
1563  * clutter_layout_manager_set_use_animations:
1564  * @manager: a #ClutterLayoutManager
1565  * @animate: %TRUE if the layout should use animations
1566  *
1567  * Sets whether @manager should animate changes in the layout properties
1568  *
1569  * The duration and delay of the animations are controlled by
1570  * clutter_layout_manager_set_easing_duration() and
1571  * clutter_layout_manager_set_easing_delay(); the easing mode to be used
1572  * by the animations is controlled by clutter_layout_manager_set_easing_mode()
1573  *
1574  * Since: 1.12
1575  */
1576 void
1577 clutter_layout_manager_set_use_animations (ClutterLayoutManager *manager,
1578                                            gboolean              animate)
1579 {
1580   ClutterLayoutManagerPrivate *priv;
1581
1582   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
1583
1584   priv = manager->priv;
1585
1586   if (priv->use_animations == animate)
1587     return;
1588
1589   priv->use_animations = animate;
1590
1591   g_object_notify_by_pspec (G_OBJECT (manager),
1592                             layout_props[PROP_USE_ANIMATIONS]);
1593 }
1594
1595 /**
1596  * clutter_layout_manager_get_use_animations:
1597  * @manager: a #ClutterLayoutManager
1598  *
1599  * Retrieves whether @manager should animate changes in the layout properties.
1600
1601  * Return value: %TRUE if the animations should be used, %FALSE otherwise
1602  *
1603  * Since: 1.12
1604  */
1605 gboolean
1606 clutter_layout_manager_get_use_animations (ClutterLayoutManager *manager)
1607 {
1608   g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), FALSE);
1609
1610   return manager->priv->use_animations;
1611 }
1612
1613 /**
1614  * clutter_layout_manager_set_easing_mode:
1615  * @manager: a #ClutterLayoutManager
1616  * @mode: an easing mode, either from #ClutterAnimationMode or a logical id
1617  *   from clutter_alpha_register_func()
1618
1619  * Sets the easing mode to be used by @manager when animating changes in layout
1620  * properties.
1621  *
1622  * Since: 1.12
1623  */
1624 void
1625 clutter_layout_manager_set_easing_mode (ClutterLayoutManager *manager,
1626                                         ClutterAnimationMode  mode)
1627 {
1628   ClutterLayoutManagerPrivate *priv;
1629
1630   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
1631   g_return_if_fail (mode < CLUTTER_ANIMATION_LAST);
1632   g_return_if_fail (mode != CLUTTER_CUSTOM_MODE);
1633
1634   priv = manager->priv;
1635
1636   if (priv->easing_mode == mode)
1637     return;
1638
1639   priv->easing_mode = mode;
1640
1641   g_object_notify_by_pspec (G_OBJECT (manager), layout_props[PROP_EASING_MODE]);
1642 }
1643
1644 /**
1645  * clutter_layout_manager_get_easing_mode:
1646  * @manager: a #ClutterLayoutManager
1647  *
1648  * Retrieves the easing mode set using clutter_layout_manager_set_easing_mode()
1649  *
1650  * Return value: an easing mode
1651  *
1652  * Since: 1.12
1653  */
1654 ClutterAnimationMode
1655 clutter_layout_manager_get_easing_mode (ClutterLayoutManager *manager)
1656 {
1657   g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), CLUTTER_LINEAR);
1658
1659   return manager->priv->easing_mode;
1660 }
1661
1662
1663 /**
1664  * clutter_layout_manager_set_easing_duration:
1665  * @manager: a #ClutterLayoutManager
1666  * @duration: the duration of the animations, in milliseconds
1667  *
1668  * Sets the duration of the animations used by @manager when animating changes
1669  * in the layout properties.
1670  *
1671  * Since: 1.12
1672  */
1673 void
1674 clutter_layout_manager_set_easing_duration (ClutterLayoutManager *manager,
1675                                             guint                 duration)
1676 {
1677   ClutterLayoutManagerPrivate *priv;
1678
1679   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
1680
1681   priv = manager->priv;
1682
1683   if (priv->easing_duration == duration)
1684     return;
1685
1686   priv->easing_duration = duration;
1687
1688   g_object_notify_by_pspec (G_OBJECT (manager),
1689                             layout_props[PROP_EASING_DURATION]);
1690 }
1691
1692 /**
1693  * clutter_layout_manager_get_easing_duration:
1694  * @manager: a #ClutterLayoutManager
1695  *
1696  * Retrieves the duration set using clutter_layout_manager_set_easing_duration()
1697  *
1698  * Return value: the duration of the animations, in milliseconds
1699  *
1700  * Since: 1.12
1701  */
1702 guint
1703 clutter_layout_manager_get_easing_duration (ClutterLayoutManager *manager)
1704 {
1705   g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), 0);
1706
1707   return manager->priv->easing_duration;
1708 }
1709
1710 /**
1711  * clutter_layout_manager_set_easing_delay:
1712  * @manager: a #ClutterLayoutManager
1713  * @delay: the delay in milliseconds
1714  *
1715  * Sets the delay before the animations used by @manager will start.
1716  *
1717  * Since: 1.12
1718  */
1719 void
1720 clutter_layout_manager_set_easing_delay (ClutterLayoutManager *manager,
1721                                          guint                 delay)
1722 {
1723   ClutterLayoutManagerPrivate *priv;
1724
1725   g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager));
1726
1727   priv = manager->priv;
1728
1729   if (priv->easing_delay == delay)
1730     return;
1731
1732   priv->easing_delay = delay;
1733
1734   g_object_notify_by_pspec (G_OBJECT (manager),
1735                             layout_props[PROP_EASING_DELAY]);
1736 }
1737
1738 /**
1739  * clutter_layout_manager_get_easing_delay:
1740  * @manager: a #ClutterLayoutManager
1741  *
1742  * Retrieves the delay set using clutter_layout_manager_set_easing_delay()
1743
1744  * Return value: the delay in milliseconds
1745  *
1746  * Since: 1.12
1747  */
1748 guint
1749 clutter_layout_manager_get_easing_delay (ClutterLayoutManager *manager)
1750 {
1751   g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), 0);
1752
1753   return manager->priv->easing_delay;
1754 }
1755
1756 /**
1757  * clutter_layout_manager_get_easing_state:
1758  * @manager: a #ClutterLayoutManager
1759  * @mode: (out): the #ClutterAnimationMode
1760  * @duration: (out): the easing duration
1761  * @delay: (out): the easing delay
1762  *
1763  * Retrieves all the necessary information if and how the @manager should
1764  * animate allocation changes. This function is meant to be called by
1765  * implementations of the #ClutterLayoutManager class in the
1766  * #ClutterLayoutManagerClass.allocate() virtual function.
1767  *
1768  * Return value: %TRUE if the @manager should animate allocation changes
1769  *
1770  * Since: 1.12
1771  */
1772 gboolean
1773 clutter_layout_manager_get_easing_state (ClutterLayoutManager *manager,
1774                                          ClutterAnimationMode *mode,
1775                                          guint                *duration,
1776                                          guint                *delay)
1777 {
1778   ClutterLayoutManagerPrivate *priv;
1779
1780   g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), FALSE);
1781
1782   priv = manager->priv;
1783
1784   if (mode)
1785     *mode = priv->easing_mode;
1786   if (duration)
1787     *duration = priv->easing_duration;
1788   if (delay)
1789     *delay = priv->easing_delay;
1790
1791   return priv->use_animations;
1792 }