Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-table-layout.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  *   Jose Dapena Paz <jdapena@igalia.com>
23  *
24  * Based on the MX MxTable actor by:
25  *   Thomas Wood <thomas.wood@intel.com>
26  * and ClutterBoxLayout by:
27  *   Emmanuele Bassi <ebassi@linux.intel.com>
28  */
29
30 /**
31  * SECTION:clutter-table-layout
32  * @title: ClutterTableLayout
33  * @short_description: A layout manager arranging children in rows
34  *   and columns
35  *
36  * The #ClutterTableLayout is a #ClutterLayoutManager implementing the
37  * following layout policy:
38  *
39  * <itemizedlist>
40  *   <listitem><para>children are arranged in a table</para></listitem>
41  *   <listitem><para>each child specifies the specific row and column
42  *   cell to appear;</para></listitem>
43  *   <listitem><para>a child can also set a span, and this way, take
44  *   more than one cell both horizontally and vertically;</para></listitem>
45  *   <listitem><para>each child will be allocated to its natural
46  *   size or, if set to expand, the available size;</para></listitem>
47  *   <listitem><para>if a child is set to fill on either (or both)
48  *   axis, its allocation will match all the available size; the
49  *   fill layout property only makes sense if the expand property is
50  *   also set;</para></listitem>
51  *   <listitem><para>if a child is set to expand but not to fill then
52  *   it is possible to control the alignment using the horizontal and
53  *   vertical alignment layout properties.</para></listitem>
54  * </itemizedlist>
55  *
56  * It is possible to control the spacing between children of a
57  * #ClutterTableLayout by using clutter_table_layout_set_row_spacing()
58  * and clutter_table_layout_set_column_spacing().
59  *
60  * In order to set the layout properties when packing an actor inside a
61  * #ClutterTableLayout you should use the clutter_table_layout_pack()
62  * function.
63  *
64  * A #ClutterTableLayout can use animations to transition between different
65  * values of the layout management properties; the easing mode and duration
66  * used for the animations are controlled by the
67  * #ClutterTableLayout:easing-mode and #ClutterTableLayout:easing-duration
68  * properties and their accessor functions.
69  *
70  * <figure id="table-layout-image">
71  *   <title>Table layout</title>
72  *   <para>The image shows a #ClutterTableLayout.</para>
73  *   <graphic fileref="table-layout.png" format="PNG"/>
74  * </figure>
75  *
76  * #ClutterTableLayout is available since Clutter 1.4
77  */
78
79 #ifdef HAVE_CONFIG_H
80 #include "config.h"
81 #endif
82
83 #include <math.h>
84
85 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
86 #include "deprecated/clutter-container.h"
87 #include "deprecated/clutter-alpha.h"
88
89 #include "clutter-table-layout.h"
90
91 #include "clutter-debug.h"
92 #include "clutter-enum-types.h"
93 #include "clutter-layout-meta.h"
94 #include "clutter-private.h"
95 #include "clutter-types.h"
96
97 #define CLUTTER_TYPE_TABLE_CHILD          (clutter_table_child_get_type ())
98 #define CLUTTER_TABLE_CHILD(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TABLE_CHILD, ClutterTableChild))
99 #define CLUTTER_IS_TABLE_CHILD(obj)       (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TABLE_CHILD))
100
101 #define CLUTTER_TABLE_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TABLE_LAYOUT, ClutterTableLayoutPrivate))
102
103 typedef struct _ClutterTableChild         ClutterTableChild;
104 typedef struct _ClutterLayoutMetaClass    ClutterTableChildClass;
105
106 typedef struct _DimensionData {
107   gfloat min_size;
108   gfloat pref_size;
109   gfloat final_size;
110
111   guint expand  : 1;
112   guint visible : 1;
113 } DimensionData;
114
115 struct _ClutterTableLayoutPrivate
116 {
117   ClutterContainer *container;
118
119   guint col_spacing;
120   guint row_spacing;
121
122   gint n_rows;
123   gint n_cols;
124   gint active_row;
125   gint active_col;
126   gint visible_rows;
127   gint visible_cols;
128
129   GArray *columns;
130   GArray *rows;
131 };
132
133 struct _ClutterTableChild
134 {
135   ClutterLayoutMeta parent_instance;
136
137   gint col;
138   gint row;
139
140   gint col_span;
141   gint row_span;
142
143   ClutterTableAlignment x_align;
144   ClutterTableAlignment y_align;
145
146   guint x_expand            : 1;
147   guint y_expand            : 1;
148   guint x_fill              : 1;
149   guint y_fill              : 1;
150 };
151
152 enum
153 {
154   PROP_CHILD_0,
155
156   PROP_CHILD_ROW,
157   PROP_CHILD_COLUMN,
158   PROP_CHILD_ROW_SPAN,
159   PROP_CHILD_COLUMN_SPAN,
160   PROP_CHILD_X_ALIGN,
161   PROP_CHILD_Y_ALIGN,
162   PROP_CHILD_X_FILL,
163   PROP_CHILD_Y_FILL,
164   PROP_CHILD_X_EXPAND,
165   PROP_CHILD_Y_EXPAND
166 };
167
168 enum
169 {
170   PROP_0,
171
172   PROP_ROW_SPACING,
173   PROP_COLUMN_SPACING,
174   PROP_EASING_DURATION
175 };
176
177 GType clutter_table_child_get_type (void);
178
179 G_DEFINE_TYPE (ClutterTableChild,
180                clutter_table_child,
181                CLUTTER_TYPE_LAYOUT_META);
182
183 G_DEFINE_TYPE (ClutterTableLayout,
184                clutter_table_layout,
185                CLUTTER_TYPE_LAYOUT_MANAGER);
186
187 /*
188  * ClutterBoxChild
189  */
190
191 static void
192 table_child_set_position (ClutterTableChild *self,
193                           gint               col,
194                           gint               row)
195 {
196   gboolean row_changed = FALSE, col_changed = FALSE;
197
198   if (self->col != col)
199     {
200       self->col = col;
201
202       col_changed = TRUE;
203     }
204
205   if (self->row != row)
206     {
207       self->row = row;
208
209       row_changed = TRUE;
210     }
211
212   if (row_changed || col_changed)
213     {
214       ClutterLayoutManager *layout;
215
216       layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
217       clutter_layout_manager_layout_changed (layout);
218
219       g_object_freeze_notify (G_OBJECT (self));
220
221       if (row_changed)
222         g_object_notify (G_OBJECT (self), "row");
223
224       if (col_changed)
225         g_object_notify (G_OBJECT (self), "column");
226
227       g_object_thaw_notify (G_OBJECT (self));
228     }
229 }
230
231 static void
232 table_child_set_span (ClutterTableChild  *self,
233                       gint                col_span,
234                       gint                row_span)
235 {
236   gboolean row_changed = FALSE, col_changed = FALSE;
237
238   if (self->col_span != col_span)
239     {
240       self->col_span = col_span;
241
242       col_changed = TRUE;
243     }
244
245   if (self->row_span != row_span)
246     {
247       self->row_span = row_span;
248
249       row_changed = TRUE;
250     }
251
252   if (row_changed || col_changed)
253     {
254       ClutterLayoutManager *layout;
255
256       layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
257       clutter_layout_manager_layout_changed (layout);
258
259       if (row_changed)
260         g_object_notify (G_OBJECT (self), "row-span");
261
262       if (col_changed)
263         g_object_notify (G_OBJECT (self), "column-span");
264     }
265 }
266
267 static void
268 table_child_set_align (ClutterTableChild     *self,
269                        ClutterTableAlignment  x_align,
270                        ClutterTableAlignment  y_align)
271 {
272   gboolean x_changed = FALSE, y_changed = FALSE;
273
274   if (self->x_align != x_align)
275     {
276       self->x_align = x_align;
277
278       x_changed = TRUE;
279     }
280
281   if (self->y_align != y_align)
282     {
283       self->y_align = y_align;
284
285       y_changed = TRUE;
286     }
287
288   if (x_changed || y_changed)
289     {
290       ClutterLayoutManager *layout;
291
292       layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
293       clutter_layout_manager_layout_changed (layout);
294
295       g_object_freeze_notify (G_OBJECT (self));
296
297       if (x_changed)
298         g_object_notify (G_OBJECT (self), "x-align");
299
300       if (y_changed)
301         g_object_notify (G_OBJECT (self), "y-align");
302
303       g_object_thaw_notify (G_OBJECT (self));
304     }
305 }
306
307 static void
308 table_child_set_fill (ClutterTableChild *self,
309                       gboolean           x_fill,
310                       gboolean           y_fill)
311 {
312   gboolean x_changed = FALSE, y_changed = FALSE;
313
314   x_fill = !!x_fill;
315   y_fill = !!y_fill;
316
317   if (self->x_fill != x_fill)
318     {
319       self->x_fill = x_fill;
320
321       x_changed = TRUE;
322     }
323
324   if (self->y_fill != y_fill)
325     {
326       self->y_fill = y_fill;
327
328       y_changed = TRUE;
329     }
330
331   if (x_changed || y_changed)
332     {
333       ClutterLayoutManager *layout;
334
335       layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
336       clutter_layout_manager_layout_changed (layout);
337
338       g_object_freeze_notify (G_OBJECT (self));
339
340       if (x_changed)
341         g_object_notify (G_OBJECT (self), "x-fill");
342
343       if (y_changed)
344         g_object_notify (G_OBJECT (self), "y-fill");
345
346       g_object_thaw_notify (G_OBJECT (self));
347     }
348 }
349
350 static void
351 table_child_set_expand (ClutterTableChild *self,
352                         gboolean           x_expand,
353                         gboolean           y_expand)
354 {
355   gboolean x_changed = FALSE, y_changed = FALSE;
356
357   x_expand = !!x_expand;
358   y_expand = !!y_expand;
359
360   if (self->x_expand != x_expand)
361     {
362       self->x_expand = x_expand;
363
364       x_changed = TRUE;
365     }
366
367   if (self->y_expand != y_expand)
368     {
369       self->y_expand = y_expand;
370
371       y_changed = TRUE;
372     }
373
374   if (x_changed || y_changed)
375     {
376       ClutterLayoutManager *layout;
377
378       layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
379       clutter_layout_manager_layout_changed (layout);
380
381       g_object_freeze_notify (G_OBJECT (self));
382
383       if (x_changed)
384         g_object_notify (G_OBJECT (self), "x-expand");
385
386       if (y_changed)
387         g_object_notify (G_OBJECT (self), "y-expand");
388
389       g_object_thaw_notify (G_OBJECT (self));
390     }
391 }
392
393 static void
394 clutter_table_child_set_property (GObject      *gobject,
395                                   guint         prop_id,
396                                   const GValue *value,
397                                   GParamSpec   *pspec)
398 {
399   ClutterTableChild *self = CLUTTER_TABLE_CHILD (gobject);
400
401   switch (prop_id)
402     {
403     case PROP_CHILD_COLUMN:
404       table_child_set_position (self,
405                                 g_value_get_int (value),
406                                 self->row);
407       break;
408
409     case PROP_CHILD_ROW:
410       table_child_set_position (self,
411                                 self->col,
412                                 g_value_get_int (value));
413       break;
414
415     case PROP_CHILD_COLUMN_SPAN:
416       table_child_set_span (self,
417                             g_value_get_int (value),
418                             self->row_span);
419       break;
420
421     case PROP_CHILD_ROW_SPAN:
422       table_child_set_span (self,
423                             self->col_span,
424                             g_value_get_int (value));
425       break;
426
427     case PROP_CHILD_X_ALIGN:
428       table_child_set_align (self,
429                              g_value_get_enum (value),
430                              self->y_align);
431       break;
432
433     case PROP_CHILD_Y_ALIGN:
434       table_child_set_align (self,
435                              self->x_align,
436                              g_value_get_enum (value));
437       break;
438
439     case PROP_CHILD_X_FILL:
440       table_child_set_fill (self,
441                             g_value_get_boolean (value),
442                             self->y_fill);
443       break;
444
445     case PROP_CHILD_Y_FILL:
446       table_child_set_fill (self,
447                             self->x_fill,
448                             g_value_get_boolean (value));
449       break;
450
451     case PROP_CHILD_X_EXPAND:
452       table_child_set_expand (self,
453                               g_value_get_boolean (value),
454                               self->y_expand);
455       break;
456
457     case PROP_CHILD_Y_EXPAND:
458       table_child_set_expand (self,
459                               self->x_expand,
460                               g_value_get_boolean (value));
461       break;
462
463     default:
464       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
465       break;
466     }
467 }
468
469 static void
470 clutter_table_child_get_property (GObject    *gobject,
471                                   guint       prop_id,
472                                   GValue     *value,
473                                   GParamSpec *pspec)
474 {
475   ClutterTableChild *self = CLUTTER_TABLE_CHILD (gobject);
476
477   switch (prop_id)
478     {
479     case PROP_CHILD_ROW:
480       g_value_set_int (value, self->row);
481       break;
482
483     case PROP_CHILD_COLUMN:
484       g_value_set_int (value, self->col);
485       break;
486
487     case PROP_CHILD_ROW_SPAN:
488       g_value_set_int (value, self->row_span);
489       break;
490
491     case PROP_CHILD_COLUMN_SPAN:
492       g_value_set_int (value, self->col_span);
493       break;
494
495     case PROP_CHILD_X_ALIGN:
496       g_value_set_enum (value, self->x_align);
497       break;
498
499     case PROP_CHILD_Y_ALIGN:
500       g_value_set_enum (value, self->y_align);
501       break;
502
503     case PROP_CHILD_X_FILL:
504       g_value_set_boolean (value, self->x_fill);
505       break;
506
507     case PROP_CHILD_Y_FILL:
508       g_value_set_boolean (value, self->y_fill);
509       break;
510
511     case PROP_CHILD_X_EXPAND:
512       g_value_set_boolean (value, self->x_expand);
513       break;
514
515     case PROP_CHILD_Y_EXPAND:
516       g_value_set_boolean (value, self->y_expand);
517       break;
518
519     default:
520       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
521       break;
522     }
523 }
524
525 static void
526 clutter_table_child_class_init (ClutterTableChildClass *klass)
527 {
528   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
529   GParamSpec *pspec;
530
531   gobject_class->set_property = clutter_table_child_set_property;
532   gobject_class->get_property = clutter_table_child_get_property;
533
534   pspec = g_param_spec_int ("column",
535                             P_("Column Number"),
536                             P_("The column the widget resides in"),
537                             0, G_MAXINT,
538                             0,
539                             CLUTTER_PARAM_READWRITE);
540   g_object_class_install_property (gobject_class, PROP_CHILD_COLUMN, pspec);
541
542   pspec = g_param_spec_int ("row",
543                             P_("Row Number"),
544                             P_("The row the widget resides in"),
545                             0, G_MAXINT,
546                             0,
547                             CLUTTER_PARAM_READWRITE);
548   g_object_class_install_property (gobject_class, PROP_CHILD_ROW, pspec);
549
550   pspec = g_param_spec_int ("column-span",
551                             P_("Column Span"),
552                             P_("The number of columns the widget should span"),
553                             1, G_MAXINT,
554                             1,
555                             CLUTTER_PARAM_READWRITE);
556   g_object_class_install_property (gobject_class, PROP_CHILD_COLUMN_SPAN, pspec);
557
558   pspec = g_param_spec_int ("row-span",
559                             P_("Row Span"),
560                             P_("The number of rows the widget should span"),
561                             1, G_MAXINT,
562                             1,
563                             CLUTTER_PARAM_READWRITE);
564   g_object_class_install_property (gobject_class, PROP_CHILD_ROW_SPAN, pspec);
565
566   pspec = g_param_spec_boolean ("x-expand",
567                                 P_("Horizontal Expand"),
568                                 P_("Allocate extra space for the child in horizontal axis"),
569                                 FALSE,
570                                 CLUTTER_PARAM_READWRITE);
571   g_object_class_install_property (gobject_class, PROP_CHILD_X_EXPAND, pspec);
572
573   pspec = g_param_spec_boolean ("y-expand",
574                                 P_("Vertical Expand"),
575                                 P_("Allocate extra space for the child in vertical axis"),
576                                 FALSE,
577                                 CLUTTER_PARAM_READWRITE);
578   g_object_class_install_property (gobject_class, PROP_CHILD_Y_EXPAND, pspec);
579
580   pspec = g_param_spec_boolean ("x-fill",
581                                 P_("Horizontal Fill"),
582                                 P_("Whether the child should receive priority when the container is allocating spare space on the horizontal axis"),
583                                 FALSE,
584                                 CLUTTER_PARAM_READWRITE);
585   g_object_class_install_property (gobject_class, PROP_CHILD_X_FILL, pspec);
586
587   pspec = g_param_spec_boolean ("y-fill",
588                                 P_("Vertical Fill"),
589                                 P_("Whether the child should receive priority when the container is allocating spare space on the vertical axis"),
590                                 FALSE,
591                                 CLUTTER_PARAM_READWRITE);
592   g_object_class_install_property (gobject_class, PROP_CHILD_Y_FILL, pspec);
593
594   /**
595    * ClutterTableLayout:x-align:
596    *
597    * The horizontal alignment of the actor within the cell
598    *
599    * Since: 1.4
600    */
601   pspec = g_param_spec_enum ("x-align",
602                              P_("Horizontal Alignment"),
603                              P_("Horizontal alignment of the actor within the cell"),
604                              CLUTTER_TYPE_TABLE_ALIGNMENT,
605                              CLUTTER_TABLE_ALIGNMENT_CENTER,
606                              CLUTTER_PARAM_READWRITE);
607   g_object_class_install_property (gobject_class, PROP_CHILD_X_ALIGN, pspec);
608
609   /**
610    * ClutterTableLayout:y-align:
611    *
612    * The vertical alignment of the actor within the cell
613    *
614    * Since: 1.4
615    */
616   pspec = g_param_spec_enum ("y-align",
617                              P_("Vertical Alignment"),
618                              P_("Vertical alignment of the actor within the cell"),
619                              CLUTTER_TYPE_TABLE_ALIGNMENT,
620                              CLUTTER_TABLE_ALIGNMENT_CENTER,
621                              CLUTTER_PARAM_READWRITE);
622   g_object_class_install_property (gobject_class, PROP_CHILD_Y_ALIGN, pspec);
623 }
624
625 static void
626 clutter_table_child_init (ClutterTableChild *self)
627 {
628   self->col_span = 1;
629   self->row_span = 1;
630
631   self->x_align = CLUTTER_TABLE_ALIGNMENT_CENTER;
632   self->y_align = CLUTTER_TABLE_ALIGNMENT_CENTER;
633
634   self->x_expand = TRUE;
635   self->y_expand = TRUE;
636
637   self->x_fill = TRUE;
638   self->y_fill = TRUE;
639 }
640
641 static GType
642 clutter_table_layout_get_child_meta_type (ClutterLayoutManager *manager)
643 {
644   return CLUTTER_TYPE_TABLE_CHILD;
645 }
646
647 static void
648 clutter_table_layout_set_container (ClutterLayoutManager *layout,
649                                     ClutterContainer     *container)
650 {
651   ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (layout)->priv;
652
653   priv->container = container;
654 }
655
656
657 static void
658 update_row_col (ClutterTableLayout *layout,
659                 ClutterContainer   *container)
660 {
661   ClutterTableLayoutPrivate *priv = layout->priv;
662   ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (layout);
663   ClutterActor *actor, *child;
664   gint n_cols, n_rows;
665
666   n_cols = n_rows = 0;
667
668   if (container == NULL)
669     goto out;
670
671   actor = CLUTTER_ACTOR (container);
672   for (child = clutter_actor_get_first_child (actor);
673        child != NULL;
674        child = clutter_actor_get_next_sibling (child))
675     {
676       ClutterTableChild *meta;
677
678       meta =
679         CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager,
680                                                                     container,
681                                                                     child));
682
683       n_cols = MAX (n_cols, meta->col + meta->col_span);
684       n_rows = MAX (n_rows, meta->row + meta->row_span);
685     }
686
687 out:
688   priv->n_cols = n_cols;
689   priv->n_rows = n_rows;
690
691 }
692
693 static void
694 calculate_col_widths (ClutterTableLayout *self,
695                       ClutterContainer   *container,
696                       gint                for_width)
697 {
698   ClutterTableLayoutPrivate *priv = self->priv;
699   ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self);
700   ClutterActor *actor, *child;
701   gint i;
702   DimensionData *columns;
703   ClutterOrientation orientation = CLUTTER_ORIENTATION_HORIZONTAL;
704
705   update_row_col (self, container);
706   g_array_set_size (priv->columns, 0);
707   g_array_set_size (priv->columns, priv->n_cols);
708   columns = (DimensionData *) (void *) priv->columns->data;
709
710   /* reset the visibility of all columns */
711   priv->visible_cols = 0;
712   for (i = 0; i < priv->n_cols; i++)
713     {
714       columns[i].expand = FALSE;
715       columns[i].visible = FALSE;
716     }
717
718   actor = CLUTTER_ACTOR (container);
719
720   /* STAGE ONE: calculate column widths for non-spanned children */
721   for (child = clutter_actor_get_first_child (actor);
722        child != NULL;
723        child = clutter_actor_get_next_sibling (child))
724     {
725       ClutterTableChild *meta;
726       DimensionData *col;
727       gfloat c_min, c_pref;
728
729       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
730         continue;
731
732       meta =
733         CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager,
734                                                                     container,
735                                                                     child));
736
737       if (meta->col_span > 1)
738         continue;
739
740       col = &columns[meta->col];
741
742       if (!col->visible)
743         {
744           col->visible = TRUE;
745           priv->visible_cols += 1;
746         }
747
748       clutter_actor_get_preferred_width (child, -1, &c_min, &c_pref);
749
750       col->min_size = MAX (col->min_size, c_min);
751       col->pref_size = MAX (col->pref_size, c_pref);
752
753       if (!col->expand)
754         {
755           col->expand = clutter_actor_needs_expand (child, orientation) ||
756             meta->x_expand;
757         }
758     }
759
760   /* STAGE TWO: take spanning children into account */
761   for (child = clutter_actor_get_first_child (actor);
762        child != NULL;
763        child = clutter_actor_get_next_sibling (child))
764     {
765       ClutterTableChild *meta;
766       gfloat c_min, c_pref;
767       gfloat min_width, pref_width;
768       gint start_col, end_col;
769       gint n_expand;
770
771       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
772         continue;
773
774       meta =
775         CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager,
776                                                                     container,
777                                                                     child));
778
779       if (meta->col_span < 2)
780         continue;
781
782       start_col = meta->col;
783       end_col = meta->col + meta->col_span - 1;
784
785       clutter_actor_get_preferred_width (child, -1, &c_min, &c_pref);
786
787       /* check there is enough room for this actor */
788       min_width = 0;
789       pref_width = 0;
790       n_expand = 0;
791       for (i = start_col; i <= end_col; i++)
792         {
793           min_width += columns[i].min_size;
794           pref_width += columns[i].pref_size;
795
796           if (columns[i].expand)
797             n_expand++;
798
799           if (!columns[i].visible)
800             {
801               columns[i].visible = TRUE;
802               priv->visible_cols += 1;
803             }
804
805           if (!columns[i].expand)
806             {
807               columns[i].expand = clutter_actor_needs_expand (child,
808                                                               orientation) ||
809                 meta->x_expand;
810             }
811         }
812       min_width += priv->col_spacing * (meta->col_span - 1);
813       pref_width += priv->col_spacing * (meta->col_span - 1);
814
815       /* see calculate_row_heights() for comments */
816       /* (1) */
817       if (c_min > min_width)
818         {
819
820           /* (2) */
821           /* we can start from preferred width and decrease */
822           if (pref_width > c_min)
823             {
824               for (i = start_col; i <= end_col; i++)
825                 columns[i].final_size = columns[i].pref_size;
826
827               while (pref_width > c_min)
828                 {
829                   for (i = start_col; i <= end_col; i++)
830                     {
831                       if (columns[i].final_size > columns[i].min_size)
832                         {
833                           columns[i].final_size--;
834                           pref_width--;
835                         }
836                     }
837                 }
838
839               for (i = start_col; i <= end_col; i++)
840                 columns[i].min_size = columns[i].final_size;
841             }
842           else
843             {
844               /* (3) */
845               /* we can expand from preferred size */
846               gfloat expand_by;
847
848               expand_by = c_pref - pref_width;
849
850               for (i = start_col; i <= end_col; i++)
851                 {
852                   if (n_expand)
853                     {
854                       if (columns[i].expand)
855                         columns[i].min_size = columns[i].pref_size
856                                             + expand_by / n_expand;
857                     }
858                   else
859                     columns[i].min_size = columns[i].pref_size
860                                         + expand_by / meta->col_span;
861                 }
862             }
863         }
864     }
865
866   /* calculate final widths */
867   if (for_width >= 0)
868     {
869       gfloat min_width, pref_width;
870       gint n_expand;
871
872       min_width = 0;
873       pref_width = 0;
874       n_expand = 0;
875
876       for (i = 0; i < self->priv->n_cols; i++)
877         {
878           pref_width += columns[i].pref_size;
879           min_width += columns[i].min_size;
880           if (columns[i].expand)
881             n_expand++;
882         }
883
884       pref_width += priv->col_spacing * (priv->n_cols - 1);
885       min_width += priv->col_spacing * (priv->n_cols - 1);
886
887       if (for_width <= min_width)
888         {
889           /* erk, we can't shrink this! */
890           for (i = 0; i < priv->n_cols; i++)
891             columns[i].final_size = columns[i].min_size;
892
893           return;
894         }
895
896       if (for_width == pref_width)
897         {
898           /* perfect! */
899           for (i = 0; i < self->priv->n_cols; i++)
900             columns[i].final_size = columns[i].pref_size;
901
902           return;
903         }
904
905       /* for_width is between min_width and pref_width */
906       if (for_width < pref_width && for_width > min_width)
907         {
908           gfloat width;
909
910           /* shrink columns until they reach min_width */
911
912           /* start with all columns at preferred size */
913           for (i = 0; i < self->priv->n_cols; i++)
914             columns[i].final_size = columns[i].pref_size;
915
916           width = pref_width;
917
918           while (width > for_width)
919             {
920               for (i = 0; i < self->priv->n_cols; i++)
921                 {
922                   if (columns[i].final_size > columns[i].min_size)
923                     {
924                       columns[i].final_size--;
925                       width--;
926                     }
927                 }
928             }
929
930           return;
931         }
932
933       /* expand columns */
934       if (for_width > pref_width)
935         {
936           gfloat extra_width = for_width - pref_width;
937           gint remaining;
938
939           if (n_expand)
940             remaining = (gint) extra_width % n_expand;
941           else
942             remaining = (gint) extra_width % priv->n_cols;
943
944           for (i = 0; i < self->priv->n_cols; i++)
945             {
946               if (columns[i].expand)
947                 {
948                   columns[i].final_size = columns[i].pref_size
949                                         + (extra_width / n_expand);
950                 }
951               else
952                 columns[i].final_size = columns[i].pref_size;
953             }
954
955           /* distribute the remainder among children */
956           i = 0;
957           while (remaining)
958             {
959               columns[i].final_size++;
960               i++;
961               remaining--;
962             }
963         }
964     }
965
966 }
967
968 static void
969 calculate_row_heights (ClutterTableLayout *self,
970                        ClutterContainer   *container,
971                        gint                for_height)
972 {
973   ClutterTableLayoutPrivate *priv = self->priv;
974   ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self);
975   ClutterActor *actor, *child;
976   gint i;
977   DimensionData *rows, *columns;
978   ClutterOrientation orientation = CLUTTER_ORIENTATION_VERTICAL;
979
980   update_row_col (self, container);
981   g_array_set_size (priv->rows, 0);
982   g_array_set_size (priv->rows, self->priv->n_rows);
983
984   rows = (DimensionData *) (void *) priv->rows->data;
985   columns = (DimensionData *) (void *) priv->columns->data;
986
987   /* reset the visibility of all rows */
988   priv->visible_rows = 0;
989   for (i = 0; i < priv->n_rows; i++)
990     {
991       rows[i].expand = FALSE;
992       rows[i].visible = FALSE;
993     }
994
995   actor = CLUTTER_ACTOR (container);
996
997   /* STAGE ONE: calculate row heights for non-spanned children */
998   for (child = clutter_actor_get_first_child (actor);
999        child != NULL;
1000        child = clutter_actor_get_next_sibling (child))
1001     {
1002       ClutterTableChild *meta;
1003       DimensionData *row;
1004       gfloat c_min, c_pref;
1005
1006       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
1007         continue;
1008
1009       meta =
1010         CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager,
1011                                                                     container,
1012                                                                     child));
1013
1014       if (meta->row_span > 1)
1015         continue;
1016
1017       row = &rows[meta->row];
1018
1019       if (!row->visible)
1020         {
1021           row->visible = TRUE;
1022           priv->visible_rows += 1;
1023         }
1024
1025       clutter_actor_get_preferred_height (child, columns[meta->col].final_size,
1026                                           &c_min, &c_pref);
1027
1028       row->min_size = MAX (row->min_size, c_min);
1029       row->pref_size = MAX (row->pref_size, c_pref);
1030
1031       if (!row->expand)
1032         {
1033           row->expand = clutter_actor_needs_expand (child, orientation) ||
1034             meta->y_expand;
1035         }
1036     }
1037
1038   /* STAGE TWO: take spanning children into account */
1039   for (child = clutter_actor_get_first_child (actor);
1040        child != NULL;
1041        child = clutter_actor_get_next_sibling (child))
1042     {
1043       ClutterTableChild *meta;
1044       gfloat c_min, c_pref;
1045       gfloat min_height, pref_height;
1046       gint start_row, end_row;
1047       gint n_expand;
1048
1049       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
1050         continue;
1051
1052       meta =
1053         CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager,
1054                                                                     container,
1055                                                                     child));
1056
1057       if (meta->row_span < 2)
1058         continue;
1059
1060       start_row = meta->row;
1061       end_row = meta->row + meta->row_span - 1;
1062
1063       clutter_actor_get_preferred_height (child, columns[meta->col].final_size,
1064                                          &c_min, &c_pref);
1065
1066
1067       /* check there is enough room for this actor */
1068       min_height = 0;
1069       pref_height = 0;
1070       n_expand = 0;
1071       for (i = start_row; i <= end_row; i++)
1072         {
1073           min_height += rows[i].min_size;
1074           pref_height += rows[i].pref_size;
1075
1076           if (rows[i].expand)
1077             n_expand++;
1078
1079           if (!rows[i].visible)
1080             {
1081               rows[i].visible = TRUE;
1082               priv->visible_rows += 1;
1083             }
1084
1085           if (!rows[i].expand)
1086             {
1087               rows[i].expand = clutter_actor_needs_expand (child,
1088                                                            orientation) ||
1089                 meta->y_expand;
1090             }
1091         }
1092
1093       min_height += priv->row_spacing * (meta->row_span - 1);
1094       pref_height += priv->row_spacing * (meta->row_span - 1);
1095
1096       /* 1) If the minimum height of the rows spanned is less than the
1097        *    minimum height of the child that is spanning them, then we
1098        *    must increase the minimum height of the rows spanned.
1099        *
1100        * 2) If the preferred height of the spanned rows is more than
1101        *    the minimum height of the spanning child, then we can start
1102        *    at this size and decrease each row evenly.
1103        *
1104        * 3) If the preferred height of the rows is more than the minimum
1105        *    height of the spanned child, then we can start at the preferred
1106        *    height and expand.
1107        */
1108
1109       /* (1) */
1110       if (c_min > min_height)
1111         {
1112
1113           /* (2) */
1114           /* we can start from preferred height and decrease */
1115           if (pref_height > c_min)
1116             {
1117               for (i = start_row; i <= end_row; i++)
1118                 rows[i].final_size = rows[i].pref_size;
1119
1120               while (pref_height > c_min)
1121                 {
1122                   for (i = start_row; i <= end_row; i++)
1123                     {
1124                       if (rows[i].final_size > rows[i].min_size)
1125                         {
1126                           rows[i].final_size--;
1127                           pref_height--;
1128                         }
1129                     }
1130                 }
1131
1132               for (i = start_row; i <= end_row; i++)
1133                 rows[i].min_size = rows[i].final_size;
1134             }
1135           else
1136             {
1137               /* (3) */
1138               /* we can expand from preferred size */
1139               gfloat expand_by = c_pref - pref_height;
1140
1141               for (i = start_row; i <= end_row; i++)
1142                 {
1143                   if (n_expand)
1144                     {
1145                       if (rows[i].expand)
1146                         rows[i].min_size = rows[i].pref_size
1147                                          + expand_by / n_expand;
1148                     }
1149                   else
1150                     rows[i].min_size = rows[i].pref_size
1151                                      + expand_by / meta->row_span;
1152                 }
1153             }
1154         }
1155     }
1156
1157   /* calculate final heights */
1158   if (for_height >= 0)
1159     {
1160       gfloat min_height, pref_height;
1161       gint n_expand;
1162
1163       min_height = 0;
1164       pref_height = 0;
1165       n_expand = 0;
1166
1167       for (i = 0; i < self->priv->n_rows; i++)
1168         {
1169           pref_height += rows[i].pref_size;
1170           min_height += rows[i].min_size;
1171           if (rows[i].expand)
1172             n_expand++;
1173         }
1174
1175       pref_height += priv->row_spacing * (priv->n_rows - 1);
1176       min_height += priv->row_spacing * (priv->n_rows - 1);
1177
1178       if (for_height <= min_height)
1179         {
1180           /* erk, we can't shrink this! */
1181           for (i = 0; i < self->priv->n_rows; i++)
1182             rows[i].final_size = rows[i].min_size;
1183
1184           return;
1185         }
1186
1187       if (for_height == pref_height)
1188         {
1189           /* perfect! */
1190           for (i = 0; i < self->priv->n_rows; i++)
1191             rows[i].final_size = rows[i].pref_size;
1192
1193           return;
1194         }
1195
1196       /* for_height is between min_height and pref_height */
1197       if (for_height < pref_height && for_height > min_height)
1198         {
1199           gfloat height;
1200
1201           /* shrink rows until they reach min_height */
1202
1203           /* start with all rows at preferred size */
1204           for (i = 0; i < self->priv->n_rows; i++)
1205             rows[i].final_size = rows[i].pref_size;
1206
1207           height = pref_height;
1208
1209           while (height > for_height)
1210             {
1211               for (i = 0; i < priv->n_rows; i++)
1212                 {
1213                   if (rows[i].final_size > rows[i].min_size)
1214                     {
1215                       rows[i].final_size--;
1216                       height--;
1217                     }
1218                 }
1219             }
1220
1221           return;
1222         }
1223
1224       /* expand rows */
1225       if (for_height > pref_height)
1226         {
1227           gfloat extra_height = for_height - pref_height;
1228           gint remaining;
1229
1230           if (n_expand)
1231             remaining = (gint) extra_height % n_expand;
1232           else
1233             remaining = (gint) extra_height % self->priv->n_rows;
1234
1235           for (i = 0; i < self->priv->n_rows; i++)
1236             {
1237               if (rows[i].expand)
1238                 {
1239                   rows[i].final_size = rows[i].pref_size
1240                                      + (extra_height / n_expand);
1241                 }
1242               else
1243                 rows[i].final_size = rows[i].pref_size;
1244             }
1245
1246           /* distribute the remainder among children */
1247           i = 0;
1248           while (remaining)
1249             {
1250               rows[i].final_size++;
1251               i++;
1252               remaining--;
1253             }
1254         }
1255     }
1256
1257 }
1258
1259 static void
1260 calculate_table_dimensions (ClutterTableLayout *self,
1261                             ClutterContainer   *container,
1262                             gfloat              for_width,
1263                             gfloat              for_height)
1264 {
1265   calculate_col_widths (self, container, for_width);
1266   calculate_row_heights (self, container, for_height);
1267 }
1268
1269 static void
1270 clutter_table_layout_get_preferred_width (ClutterLayoutManager *layout,
1271                                           ClutterContainer     *container,
1272                                           gfloat                for_height,
1273                                           gfloat               *min_width_p,
1274                                           gfloat               *natural_width_p)
1275 {
1276   ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (layout);
1277   ClutterTableLayoutPrivate *priv = self->priv;
1278   gfloat total_min_width, total_pref_width;
1279   DimensionData *columns;
1280   gint i;
1281
1282   update_row_col (self, container);
1283   if (priv->n_cols < 1)
1284     {
1285       *min_width_p = 0;
1286       *natural_width_p = 0;
1287       return;
1288     }
1289
1290   calculate_table_dimensions (self, container, -1, for_height);
1291   columns = (DimensionData *) (void *) priv->columns->data;
1292
1293   total_min_width = (priv->visible_cols - 1) * (float) priv->col_spacing;
1294   total_pref_width = total_min_width;
1295
1296   for (i = 0; i < priv->n_cols; i++)
1297     {
1298       total_min_width += columns[i].min_size;
1299       total_pref_width += columns[i].pref_size;
1300     }
1301
1302   if (min_width_p)
1303     *min_width_p = total_min_width;
1304
1305   if (natural_width_p)
1306     *natural_width_p = total_pref_width;
1307 }
1308
1309 static void
1310 clutter_table_layout_get_preferred_height (ClutterLayoutManager *layout,
1311                                            ClutterContainer     *container,
1312                                            gfloat                for_width,
1313                                            gfloat               *min_height_p,
1314                                            gfloat               *natural_height_p)
1315 {
1316   ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (layout);
1317   ClutterTableLayoutPrivate *priv = self->priv;
1318   gfloat total_min_height, total_pref_height;
1319   DimensionData *rows;
1320   gint i;
1321
1322   update_row_col (self, container);
1323   if (priv->n_rows < 1)
1324     {
1325       *min_height_p = 0;
1326       *natural_height_p = 0;
1327       return;
1328     }
1329
1330   calculate_table_dimensions (self, container, for_width, -1);
1331   rows = (DimensionData *) (void *) priv->rows->data;
1332
1333   total_min_height = (priv->visible_rows - 1) * (float) priv->row_spacing;
1334   total_pref_height = total_min_height;
1335
1336   for (i = 0; i < self->priv->n_rows; i++)
1337     {
1338       total_min_height += rows[i].min_size;
1339       total_pref_height += rows[i].pref_size;
1340     }
1341
1342   if (min_height_p)
1343     *min_height_p = total_min_height;
1344
1345   if (natural_height_p)
1346     *natural_height_p = total_pref_height;
1347 }
1348
1349 static gdouble
1350 get_table_alignment_factor (ClutterTableAlignment alignment)
1351 {
1352   switch (alignment)
1353     {
1354     case CLUTTER_TABLE_ALIGNMENT_START:
1355       return 0.0;
1356
1357     case CLUTTER_TABLE_ALIGNMENT_CENTER:
1358       return 0.5;
1359
1360     case CLUTTER_TABLE_ALIGNMENT_END:
1361       return 1.0;
1362     }
1363
1364   return 0.0;
1365 }
1366
1367 static void
1368 clutter_table_layout_allocate (ClutterLayoutManager   *layout,
1369                                ClutterContainer       *container,
1370                                const ClutterActorBox  *box,
1371                                ClutterAllocationFlags  flags)
1372 {
1373   ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (layout);
1374   ClutterTableLayoutPrivate *priv = self->priv;
1375   ClutterActor *actor, *child;
1376   gint row_spacing, col_spacing;
1377   gint i;
1378   DimensionData *rows, *columns;
1379   gboolean use_animations;
1380   ClutterAnimationMode easing_mode;
1381   guint easing_duration, easing_delay;
1382
1383
1384   update_row_col (self, container);
1385   if (priv->n_cols < 1 || priv->n_rows < 1)
1386     return;
1387
1388   actor = CLUTTER_ACTOR (container);
1389
1390   if (clutter_actor_get_n_children (actor) == 0)
1391     return;
1392
1393   col_spacing = (priv->col_spacing);
1394   row_spacing = (priv->row_spacing);
1395
1396   calculate_table_dimensions (self, container,
1397                               box->x2 - box->x1,
1398                               box->y2 - box->y1);
1399
1400   rows = (DimensionData *) (void *) priv->rows->data;
1401   columns = (DimensionData *) (void *) priv->columns->data;
1402
1403   use_animations = clutter_layout_manager_get_easing_state (layout,
1404                                                             &easing_mode,
1405                                                             &easing_duration,
1406                                                             &easing_delay);
1407
1408   for (child = clutter_actor_get_first_child (actor);
1409        child != NULL;
1410        child = clutter_actor_get_next_sibling (child))
1411     {
1412       gint row, col, row_span, col_span;
1413       gint col_width, row_height;
1414       ClutterTableChild *meta;
1415       ClutterActorBox childbox;
1416       gint child_x, child_y;
1417       gdouble x_align, y_align;
1418       gboolean x_fill, y_fill;
1419
1420       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
1421         continue;
1422
1423       meta =
1424         CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (layout,
1425                                                                     container,
1426                                                                     child));
1427
1428       /* get child properties */
1429       col = meta->col;
1430       row = meta->row;
1431       row_span = meta->row_span;
1432       col_span = meta->col_span;
1433       x_align = get_table_alignment_factor (meta->x_align);
1434       y_align = get_table_alignment_factor (meta->y_align);
1435       x_fill = meta->x_fill;
1436       y_fill = meta->y_fill;
1437
1438       /* initialise the width and height */
1439       col_width = columns[col].final_size;
1440       row_height = rows[row].final_size;
1441
1442       /* Add the widths of the spanned columns:
1443        *
1444        * First check that we have a non-zero span. Then we loop over each of
1445        * the columns that we're spanning but we stop short if we go past the
1446        * number of columns in the table. This is necessary to avoid accessing
1447        * uninitialised memory. We add the spacing in here too since we only
1448        * want to add as much spacing as times we successfully span.
1449        */
1450       if (col + col_span > priv->n_cols)
1451         g_warning (G_STRLOC ": column-span exceeds number of columns");
1452       if (row + row_span > priv->n_rows)
1453         g_warning (G_STRLOC ": row-span exceeds number of rows");
1454
1455       if (col_span > 1)
1456         {
1457           for (i = col + 1; i < col + col_span && i < priv->n_cols; i++)
1458             {
1459               col_width += columns[i].final_size;
1460               col_width += col_spacing;
1461             }
1462         }
1463
1464       /* add the height of the spanned rows */
1465       if (row_span > 1)
1466         {
1467           for (i = row + 1; i < row + row_span && i < priv->n_rows; i++)
1468             {
1469               row_height += rows[i].final_size;
1470               row_height += row_spacing;
1471             }
1472         }
1473
1474       /* calculate child x */
1475       child_x = clutter_actor_box_get_x (box);
1476       for (i = 0; i < col; i++)
1477         {
1478           if (columns[i].visible)
1479             {
1480               child_x += columns[i].final_size;
1481               child_x += col_spacing;
1482             }
1483         }
1484
1485       /* calculate child y */
1486       child_y = clutter_actor_box_get_y (box);
1487       for (i = 0; i < row; i++)
1488         {
1489           if (rows[i].visible)
1490             {
1491               child_y += rows[i].final_size;
1492               child_y += row_spacing;
1493             }
1494         }
1495
1496       /* set up childbox */
1497       childbox.x1 = (float) child_x;
1498       childbox.x2 = (float) MAX (0, child_x + col_width);
1499
1500       childbox.y1 = (float) child_y;
1501       childbox.y2 = (float) MAX (0, child_y + row_height);
1502
1503       if (use_animations)
1504         {
1505           clutter_actor_save_easing_state (child);
1506           clutter_actor_set_easing_mode (child, easing_mode);
1507           clutter_actor_set_easing_duration (child, easing_duration);
1508           clutter_actor_set_easing_delay (child, easing_delay);
1509         }
1510
1511       if (clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL) ||
1512           clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL))
1513         clutter_actor_allocate (child, &childbox, flags);
1514       else
1515         clutter_actor_allocate_align_fill (child, &childbox,
1516                                            x_align, y_align,
1517                                            x_fill, y_fill,
1518                                            flags);
1519
1520       if (use_animations)
1521         clutter_actor_restore_easing_state (child);
1522     }
1523 }
1524
1525 static void
1526 clutter_table_layout_set_property (GObject      *gobject,
1527                                    guint         prop_id,
1528                                    const GValue *value,
1529                                    GParamSpec   *pspec)
1530 {
1531   ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (gobject);
1532
1533   switch (prop_id)
1534     {
1535     case PROP_COLUMN_SPACING:
1536       clutter_table_layout_set_column_spacing (self, g_value_get_uint (value));
1537       break;
1538
1539     case PROP_ROW_SPACING:
1540       clutter_table_layout_set_row_spacing (self, g_value_get_uint (value));
1541       break;
1542
1543     default:
1544       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1545       break;
1546     }
1547 }
1548
1549 static void
1550 clutter_table_layout_get_property (GObject    *gobject,
1551                                    guint       prop_id,
1552                                    GValue     *value,
1553                                    GParamSpec *pspec)
1554 {
1555   ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (gobject)->priv;
1556
1557   switch (prop_id)
1558     {
1559     case PROP_ROW_SPACING:
1560       g_value_set_uint (value, priv->row_spacing);
1561       break;
1562
1563     case PROP_COLUMN_SPACING:
1564       g_value_set_uint (value, priv->col_spacing);
1565       break;
1566
1567     default:
1568       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1569       break;
1570     }
1571 }
1572
1573 static void
1574 clutter_table_layout_finalize (GObject *gobject)
1575 {
1576   ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (gobject)->priv;
1577
1578   g_array_free (priv->columns, TRUE);
1579   g_array_free (priv->rows, TRUE);
1580
1581   G_OBJECT_CLASS (clutter_table_layout_parent_class)->finalize (gobject);
1582 }
1583
1584 static void
1585 clutter_table_layout_class_init (ClutterTableLayoutClass *klass)
1586 {
1587   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1588   ClutterLayoutManagerClass *layout_class;
1589   GParamSpec *pspec;
1590
1591   layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
1592
1593   gobject_class->set_property = clutter_table_layout_set_property;
1594   gobject_class->get_property = clutter_table_layout_get_property;
1595   gobject_class->finalize = clutter_table_layout_finalize;
1596
1597   layout_class->get_preferred_width =
1598     clutter_table_layout_get_preferred_width;
1599   layout_class->get_preferred_height =
1600     clutter_table_layout_get_preferred_height;
1601   layout_class->allocate = clutter_table_layout_allocate;
1602   layout_class->set_container = clutter_table_layout_set_container;
1603   layout_class->get_child_meta_type =
1604     clutter_table_layout_get_child_meta_type;
1605
1606   g_type_class_add_private (klass, sizeof (ClutterTableLayoutPrivate));
1607
1608   /**
1609    * ClutterTableLayout:column-spacing:
1610    *
1611    * The spacing between columns of the #ClutterTableLayout, in pixels
1612    *
1613    * Since: 1.4
1614    */
1615   pspec = g_param_spec_uint ("column-spacing",
1616                              P_("Column Spacing"),
1617                              P_("Spacing between columns"),
1618                              0, G_MAXUINT, 0,
1619                              CLUTTER_PARAM_READWRITE);
1620   g_object_class_install_property (gobject_class, PROP_COLUMN_SPACING, pspec);
1621
1622   /**
1623    * ClutterTableLayout:row-spacing:
1624    *
1625    * The spacing between rows of the #ClutterTableLayout, in pixels
1626    *
1627    * Since: 1.4
1628    */
1629   pspec = g_param_spec_uint ("row-spacing",
1630                              P_("Row Spacing"),
1631                              P_("Spacing between rows"),
1632                              0, G_MAXUINT, 0,
1633                              CLUTTER_PARAM_READWRITE);
1634   g_object_class_install_property (gobject_class, PROP_ROW_SPACING, pspec);
1635
1636   /* a leftover to be compatible to the previous implementation */
1637   pspec = g_param_spec_uint ("easing-duration",
1638                              P_("Easing Duration"),
1639                              P_("The duration of the animations"),
1640                              0, G_MAXUINT, 500,
1641                              CLUTTER_PARAM_READWRITE);
1642   g_object_class_install_property (gobject_class, PROP_EASING_DURATION, pspec);
1643 }
1644
1645 static void
1646 clutter_table_layout_init (ClutterTableLayout *layout)
1647 {
1648   ClutterTableLayoutPrivate *priv;
1649
1650   layout->priv = priv = CLUTTER_TABLE_LAYOUT_GET_PRIVATE (layout);
1651
1652   priv->row_spacing = 0;
1653   priv->col_spacing = 0;
1654
1655   priv->columns = g_array_new (FALSE, TRUE, sizeof (DimensionData));
1656   priv->rows = g_array_new (FALSE, TRUE, sizeof (DimensionData));
1657 }
1658
1659 /**
1660  * clutter_table_layout_new:
1661  *
1662  * Creates a new #ClutterTableLayout layout manager
1663  *
1664  * Return value: the newly created #ClutterTableLayout
1665  *
1666  * Since: 1.4
1667  */
1668 ClutterLayoutManager *
1669 clutter_table_layout_new (void)
1670 {
1671   return g_object_new (CLUTTER_TYPE_TABLE_LAYOUT, NULL);
1672 }
1673
1674 /**
1675  * clutter_table_layout_set_column_spacing:
1676  * @layout: a #ClutterTableLayout
1677  * @spacing: the spacing between columns of the layout, in pixels
1678  *
1679  * Sets the spacing between columns of @layout
1680  *
1681  * Since: 1.4
1682  */
1683 void
1684 clutter_table_layout_set_column_spacing (ClutterTableLayout *layout,
1685                                          guint               spacing)
1686 {
1687   ClutterTableLayoutPrivate *priv;
1688
1689   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
1690
1691   priv = layout->priv;
1692
1693   if (priv->col_spacing != spacing)
1694     {
1695       ClutterLayoutManager *manager;
1696
1697       priv->col_spacing = spacing;
1698
1699       manager = CLUTTER_LAYOUT_MANAGER (layout);
1700       clutter_layout_manager_layout_changed (manager);
1701
1702       g_object_notify (G_OBJECT (layout), "column-spacing");
1703     }
1704 }
1705
1706 /**
1707  * clutter_table_layout_get_column_spacing:
1708  * @layout: a #ClutterTableLayout
1709  *
1710  * Retrieves the spacing set using clutter_table_layout_set_column_spacing()
1711  *
1712  * Return value: the spacing between columns of the #ClutterTableLayout
1713  *
1714  * Since: 1.4
1715  */
1716 guint
1717 clutter_table_layout_get_column_spacing (ClutterTableLayout *layout)
1718 {
1719   g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), 0);
1720
1721   return layout->priv->col_spacing;
1722 }
1723
1724 /**
1725  * clutter_table_layout_set_row_spacing:
1726  * @layout: a #ClutterTableLayout
1727  * @spacing: the spacing between rows of the layout, in pixels
1728  *
1729  * Sets the spacing between rows of @layout
1730  *
1731  * Since: 1.4
1732  */
1733 void
1734 clutter_table_layout_set_row_spacing (ClutterTableLayout *layout,
1735                                       guint               spacing)
1736 {
1737   ClutterTableLayoutPrivate *priv;
1738
1739   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
1740
1741   priv = layout->priv;
1742
1743   if (priv->row_spacing != spacing)
1744     {
1745       ClutterLayoutManager *manager;
1746
1747       priv->row_spacing = spacing;
1748
1749       manager = CLUTTER_LAYOUT_MANAGER (layout);
1750       clutter_layout_manager_layout_changed (manager);
1751
1752       g_object_notify (G_OBJECT (layout), "row-spacing");
1753     }
1754 }
1755
1756 /**
1757  * clutter_table_layout_get_row_spacing:
1758  * @layout: a #ClutterTableLayout
1759  *
1760  * Retrieves the spacing set using clutter_table_layout_set_row_spacing()
1761  *
1762  * Return value: the spacing between rows of the #ClutterTableLayout
1763  *
1764  * Since: 1.4
1765  */
1766 guint
1767 clutter_table_layout_get_row_spacing (ClutterTableLayout *layout)
1768 {
1769   g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), 0);
1770
1771   return layout->priv->row_spacing;
1772 }
1773
1774 /**
1775  * clutter_table_layout_pack:
1776  * @layout: a #ClutterTableLayout
1777  * @actor: a #ClutterActor
1778  * @column: the column the @actor should be put, or -1 to append
1779  * @row: the row the @actor should be put, or -1 to append
1780  *
1781  * Packs @actor inside the #ClutterContainer associated to @layout
1782  * at the given row and column.
1783  *
1784  * Since: 1.4
1785  */
1786 void
1787 clutter_table_layout_pack (ClutterTableLayout  *layout,
1788                            ClutterActor        *actor,
1789                            gint                 column,
1790                            gint                 row)
1791 {
1792   ClutterTableLayoutPrivate *priv;
1793   ClutterLayoutManager *manager;
1794   ClutterLayoutMeta *meta;
1795
1796   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
1797   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
1798
1799   priv = layout->priv;
1800
1801   if (priv->container == NULL)
1802     {
1803       g_warning ("The layout of type '%s' must be associated to "
1804                  "a ClutterContainer before adding children",
1805                  G_OBJECT_TYPE_NAME (layout));
1806       return;
1807     }
1808
1809   update_row_col (CLUTTER_TABLE_LAYOUT (layout), priv->container);
1810
1811   clutter_container_add_actor (priv->container, actor);
1812
1813   manager = CLUTTER_LAYOUT_MANAGER (layout);
1814   meta = clutter_layout_manager_get_child_meta (manager,
1815                                                 priv->container,
1816                                                 actor);
1817   g_assert (CLUTTER_IS_TABLE_CHILD (meta));
1818
1819   if (row < 0)
1820     row = priv->n_rows + 1;
1821
1822   if (column < 0)
1823     column = priv->n_cols + 1;
1824
1825   table_child_set_position (CLUTTER_TABLE_CHILD (meta), column, row);
1826 }
1827
1828 /**
1829  * clutter_table_layout_set_span:
1830  * @layout: a #ClutterTableLayout
1831  * @actor: a #ClutterActor child of @layout
1832  * @column_span: Column span for @actor
1833  * @row_span: Row span for @actor
1834  *
1835  * Sets the row and column span for @actor
1836  * inside @layout
1837  *
1838  * Since: 1.4
1839  */
1840 void
1841 clutter_table_layout_set_span (ClutterTableLayout *layout,
1842                                ClutterActor       *actor,
1843                                gint                column_span,
1844                                gint                row_span)
1845 {
1846   ClutterTableLayoutPrivate *priv;
1847   ClutterLayoutManager *manager;
1848   ClutterLayoutMeta *meta;
1849
1850   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
1851   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
1852
1853   priv = layout->priv;
1854
1855   if (priv->container == NULL)
1856     {
1857       g_warning ("The layout of type '%s' must be associated to "
1858                  "a ClutterContainer before querying layout "
1859                  "properties",
1860                  G_OBJECT_TYPE_NAME (layout));
1861       return;
1862     }
1863
1864   manager = CLUTTER_LAYOUT_MANAGER (layout);
1865   meta = clutter_layout_manager_get_child_meta (manager,
1866                                                 priv->container,
1867                                                 actor);
1868   if (meta == NULL)
1869     {
1870       g_warning ("No layout meta found for the child of type '%s' "
1871                  "inside the layout manager of type '%s'",
1872                  G_OBJECT_TYPE_NAME (actor),
1873                  G_OBJECT_TYPE_NAME (manager));
1874       return;
1875     }
1876
1877   g_assert (CLUTTER_IS_TABLE_CHILD (meta));
1878
1879   table_child_set_span (CLUTTER_TABLE_CHILD (meta), column_span, row_span);
1880 }
1881
1882 /**
1883  * clutter_table_layout_get_span:
1884  * @layout: a #ClutterTableLayout
1885  * @actor: a #ClutterActor child of @layout
1886  * @column_span: (out): return location for the col span
1887  * @row_span: (out): return location for the row span
1888  *
1889  * Retrieves the row and column span for @actor as set using
1890  * clutter_table_layout_pack() or clutter_table_layout_set_span()
1891  *
1892  * Since: 1.4
1893  */
1894 void
1895 clutter_table_layout_get_span (ClutterTableLayout *layout,
1896                                ClutterActor       *actor,
1897                                gint               *column_span,
1898                                gint               *row_span)
1899 {
1900   ClutterTableLayoutPrivate *priv;
1901   ClutterLayoutManager *manager;
1902   ClutterLayoutMeta *meta;
1903
1904   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
1905   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
1906
1907   priv = layout->priv;
1908
1909   if (priv->container == NULL)
1910     {
1911       g_warning ("The layout of type '%s' must be associated to "
1912                  "a ClutterContainer before querying layout "
1913                  "properties",
1914                  G_OBJECT_TYPE_NAME (layout));
1915       return;
1916     }
1917
1918   manager = CLUTTER_LAYOUT_MANAGER (layout);
1919   meta = clutter_layout_manager_get_child_meta (manager,
1920                                                 priv->container,
1921                                                 actor);
1922   if (meta == NULL)
1923     {
1924       g_warning ("No layout meta found for the child of type '%s' "
1925                  "inside the layout manager of type '%s'",
1926                  G_OBJECT_TYPE_NAME (actor),
1927                  G_OBJECT_TYPE_NAME (manager));
1928       return;
1929     }
1930
1931   g_assert (CLUTTER_IS_TABLE_CHILD (meta));
1932
1933   if (column_span)
1934     *column_span = CLUTTER_TABLE_CHILD (meta)->col_span;
1935
1936   if (row_span)
1937     *row_span = CLUTTER_TABLE_CHILD (meta)->row_span;
1938 }
1939
1940 /**
1941  * clutter_table_layout_set_alignment:
1942  * @layout: a #ClutterTableLayout
1943  * @actor: a #ClutterActor child of @layout
1944  * @x_align: Horizontal alignment policy for @actor
1945  * @y_align: Vertical alignment policy for @actor
1946  *
1947  * Sets the horizontal and vertical alignment policies for @actor
1948  * inside @layout
1949  *
1950  * Since: 1.4
1951  */
1952 void
1953 clutter_table_layout_set_alignment (ClutterTableLayout    *layout,
1954                                     ClutterActor          *actor,
1955                                     ClutterTableAlignment  x_align,
1956                                     ClutterTableAlignment  y_align)
1957 {
1958   ClutterTableLayoutPrivate *priv;
1959   ClutterLayoutManager *manager;
1960   ClutterLayoutMeta *meta;
1961
1962   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
1963   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
1964
1965   priv = layout->priv;
1966
1967   if (priv->container == NULL)
1968     {
1969       g_warning ("The layout of type '%s' must be associated to "
1970                  "a ClutterContainer before querying layout "
1971                  "properties",
1972                  G_OBJECT_TYPE_NAME (layout));
1973       return;
1974     }
1975
1976   manager = CLUTTER_LAYOUT_MANAGER (layout);
1977   meta = clutter_layout_manager_get_child_meta (manager,
1978                                                 priv->container,
1979                                                 actor);
1980   if (meta == NULL)
1981     {
1982       g_warning ("No layout meta found for the child of type '%s' "
1983                  "inside the layout manager of type '%s'",
1984                  G_OBJECT_TYPE_NAME (actor),
1985                  G_OBJECT_TYPE_NAME (manager));
1986       return;
1987     }
1988
1989   g_assert (CLUTTER_IS_TABLE_CHILD (meta));
1990
1991   table_child_set_align (CLUTTER_TABLE_CHILD (meta), x_align, y_align);
1992 }
1993
1994 /**
1995  * clutter_table_layout_get_alignment:
1996  * @layout: a #ClutterTableLayout
1997  * @actor: a #ClutterActor child of @layout
1998  * @x_align: (out): return location for the horizontal alignment policy
1999  * @y_align: (out): return location for the vertical alignment policy
2000  *
2001  * Retrieves the horizontal and vertical alignment policies for @actor
2002  * as set using clutter_table_layout_pack() or
2003  * clutter_table_layout_set_alignment().
2004  *
2005  * Since: 1.4
2006  */
2007 void
2008 clutter_table_layout_get_alignment (ClutterTableLayout    *layout,
2009                                     ClutterActor          *actor,
2010                                     ClutterTableAlignment *x_align,
2011                                     ClutterTableAlignment *y_align)
2012 {
2013   ClutterTableLayoutPrivate *priv;
2014   ClutterLayoutManager *manager;
2015   ClutterLayoutMeta *meta;
2016
2017   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
2018   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
2019
2020   priv = layout->priv;
2021
2022   if (priv->container == NULL)
2023     {
2024       g_warning ("The layout of type '%s' must be associated to "
2025                  "a ClutterContainer before querying layout "
2026                  "properties",
2027                  G_OBJECT_TYPE_NAME (layout));
2028       return;
2029     }
2030
2031   manager = CLUTTER_LAYOUT_MANAGER (layout);
2032   meta = clutter_layout_manager_get_child_meta (manager,
2033                                                 priv->container,
2034                                                 actor);
2035   if (meta == NULL)
2036     {
2037       g_warning ("No layout meta found for the child of type '%s' "
2038                  "inside the layout manager of type '%s'",
2039                  G_OBJECT_TYPE_NAME (actor),
2040                  G_OBJECT_TYPE_NAME (manager));
2041       return;
2042     }
2043
2044   g_assert (CLUTTER_IS_TABLE_CHILD (meta));
2045
2046   if (x_align)
2047     *x_align = CLUTTER_TABLE_CHILD (meta)->x_align;
2048
2049   if (y_align)
2050     *y_align = CLUTTER_TABLE_CHILD (meta)->y_align;
2051 }
2052
2053 /**
2054  * clutter_table_layout_set_fill:
2055  * @layout: a #ClutterTableLayout
2056  * @actor: a #ClutterActor child of @layout
2057  * @x_fill: whether @actor should fill horizontally the allocated space
2058  * @y_fill: whether @actor should fill vertically the allocated space
2059  *
2060  * Sets the horizontal and vertical fill policies for @actor
2061  * inside @layout
2062  *
2063  * Since: 1.4
2064  */
2065 void
2066 clutter_table_layout_set_fill (ClutterTableLayout *layout,
2067                                ClutterActor       *actor,
2068                                gboolean            x_fill,
2069                                gboolean            y_fill)
2070 {
2071   ClutterTableLayoutPrivate *priv;
2072   ClutterLayoutManager *manager;
2073   ClutterLayoutMeta *meta;
2074
2075   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
2076   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
2077
2078   priv = layout->priv;
2079
2080   if (priv->container == NULL)
2081     {
2082       g_warning ("The layout of type '%s' must be associated to "
2083                  "a ClutterContainer before querying layout "
2084                  "properties",
2085                  G_OBJECT_TYPE_NAME (layout));
2086       return;
2087     }
2088
2089   manager = CLUTTER_LAYOUT_MANAGER (layout);
2090   meta = clutter_layout_manager_get_child_meta (manager,
2091                                                 priv->container,
2092                                                 actor);
2093   if (meta == NULL)
2094     {
2095       g_warning ("No layout meta found for the child of type '%s' "
2096                  "inside the layout manager of type '%s'",
2097                  G_OBJECT_TYPE_NAME (actor),
2098                  G_OBJECT_TYPE_NAME (manager));
2099       return;
2100     }
2101
2102   g_assert (CLUTTER_IS_TABLE_CHILD (meta));
2103
2104   table_child_set_fill (CLUTTER_TABLE_CHILD (meta), x_fill, y_fill);
2105 }
2106
2107 /**
2108  * clutter_table_layout_get_fill:
2109  * @layout: a #ClutterTableLayout
2110  * @actor: a #ClutterActor child of @layout
2111  * @x_fill: (out): return location for the horizontal fill policy
2112  * @y_fill: (out): return location for the vertical fill policy
2113  *
2114  * Retrieves the horizontal and vertical fill policies for @actor
2115  * as set using clutter_table_layout_pack() or clutter_table_layout_set_fill()
2116  *
2117  * Since: 1.4
2118  */
2119 void
2120 clutter_table_layout_get_fill (ClutterTableLayout *layout,
2121                                ClutterActor       *actor,
2122                                gboolean           *x_fill,
2123                                gboolean           *y_fill)
2124 {
2125   ClutterTableLayoutPrivate *priv;
2126   ClutterLayoutManager *manager;
2127   ClutterLayoutMeta *meta;
2128
2129   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
2130   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
2131
2132   priv = layout->priv;
2133
2134   if (priv->container == NULL)
2135     {
2136       g_warning ("The layout of type '%s' must be associated to "
2137                  "a ClutterContainer before querying layout "
2138                  "properties",
2139                  G_OBJECT_TYPE_NAME (layout));
2140       return;
2141     }
2142
2143   manager = CLUTTER_LAYOUT_MANAGER (layout);
2144   meta = clutter_layout_manager_get_child_meta (manager,
2145                                                 priv->container,
2146                                                 actor);
2147   if (meta == NULL)
2148     {
2149       g_warning ("No layout meta found for the child of type '%s' "
2150                  "inside the layout manager of type '%s'",
2151                  G_OBJECT_TYPE_NAME (actor),
2152                  G_OBJECT_TYPE_NAME (manager));
2153       return;
2154     }
2155
2156   g_assert (CLUTTER_IS_TABLE_CHILD (meta));
2157
2158   if (x_fill)
2159     *x_fill = CLUTTER_TABLE_CHILD (meta)->x_fill;
2160
2161   if (y_fill)
2162     *y_fill = CLUTTER_TABLE_CHILD (meta)->y_fill;
2163 }
2164
2165
2166 /**
2167  * clutter_table_layout_set_expand:
2168  * @layout: a #ClutterTableLayout
2169  * @actor: a #ClutterActor child of @layout
2170  * @x_expand: whether @actor should allocate extra space horizontally
2171  * @y_expand: whether @actor should allocate extra space vertically
2172  *
2173  * Sets the horizontal and vertical expand policies for @actor
2174  * inside @layout
2175  *
2176  * Since: 1.4
2177  */
2178 void
2179 clutter_table_layout_set_expand (ClutterTableLayout *layout,
2180                                  ClutterActor       *actor,
2181                                  gboolean            x_expand,
2182                                  gboolean            y_expand)
2183 {
2184   ClutterTableLayoutPrivate *priv;
2185   ClutterLayoutManager *manager;
2186   ClutterLayoutMeta *meta;
2187
2188   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
2189   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
2190
2191   priv = layout->priv;
2192
2193   if (priv->container == NULL)
2194     {
2195       g_warning ("The layout of type '%s' must be associated to "
2196                  "a ClutterContainer before querying layout "
2197                  "properties",
2198                  G_OBJECT_TYPE_NAME (layout));
2199       return;
2200     }
2201
2202   manager = CLUTTER_LAYOUT_MANAGER (layout);
2203   meta = clutter_layout_manager_get_child_meta (manager,
2204                                                 priv->container,
2205                                                 actor);
2206   if (meta == NULL)
2207     {
2208       g_warning ("No layout meta found for the child of type '%s' "
2209                  "inside the layout manager of type '%s'",
2210                  G_OBJECT_TYPE_NAME (actor),
2211                  G_OBJECT_TYPE_NAME (manager));
2212       return;
2213     }
2214
2215   g_assert (CLUTTER_IS_TABLE_CHILD (meta));
2216
2217   table_child_set_expand (CLUTTER_TABLE_CHILD (meta), x_expand, y_expand);
2218 }
2219
2220 /**
2221  * clutter_table_layout_get_expand:
2222  * @layout: a #ClutterTableLayout
2223  * @actor: a #ClutterActor child of @layout
2224  * @x_expand: (out): return location for the horizontal expand policy
2225  * @y_expand: (out): return location for the vertical expand policy
2226  *
2227  * Retrieves the horizontal and vertical expand policies for @actor
2228  * as set using clutter_table_layout_pack() or clutter_table_layout_set_expand()
2229  *
2230  * Since: 1.4
2231  */
2232 void
2233 clutter_table_layout_get_expand (ClutterTableLayout *layout,
2234                                  ClutterActor       *actor,
2235                                  gboolean           *x_expand,
2236                                  gboolean           *y_expand)
2237 {
2238   ClutterTableLayoutPrivate *priv;
2239   ClutterLayoutManager *manager;
2240   ClutterLayoutMeta *meta;
2241
2242   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
2243   g_return_if_fail (CLUTTER_IS_ACTOR (actor));
2244
2245   priv = layout->priv;
2246
2247   if (priv->container == NULL)
2248     {
2249       g_warning ("The layout of type '%s' must be associated to "
2250                  "a ClutterContainer before querying layout "
2251                  "properties",
2252                  G_OBJECT_TYPE_NAME (layout));
2253       return;
2254     }
2255
2256   manager = CLUTTER_LAYOUT_MANAGER (layout);
2257   meta = clutter_layout_manager_get_child_meta (manager,
2258                                                 priv->container,
2259                                                 actor);
2260   if (meta == NULL)
2261     {
2262       g_warning ("No layout meta found for the child of type '%s' "
2263                  "inside the layout manager of type '%s'",
2264                  G_OBJECT_TYPE_NAME (actor),
2265                  G_OBJECT_TYPE_NAME (manager));
2266       return;
2267     }
2268
2269   g_assert (CLUTTER_IS_TABLE_CHILD (meta));
2270
2271   if (x_expand)
2272     *x_expand = CLUTTER_TABLE_CHILD (meta)->x_expand;
2273
2274   if (y_expand)
2275     *y_expand = CLUTTER_TABLE_CHILD (meta)->y_expand;
2276 }
2277
2278 /**
2279  * clutter_table_layout_set_use_animations:
2280  * @layout: a #ClutterTableLayout
2281  * @animate: %TRUE if the @layout should use animations
2282  *
2283  * Sets whether @layout should animate changes in the layout properties
2284  *
2285  * The duration of the animations is controlled by
2286  * clutter_table_layout_set_easing_duration(); the easing mode to be used
2287  * by the animations is controlled by clutter_table_layout_set_easing_mode()
2288  *
2289  * Since: 1.4
2290  *
2291  * Deprecated: 1.12: #ClutterTableLayout will honour the
2292  *   #ClutterLayoutManager:use-animations property
2293  */
2294 void
2295 clutter_table_layout_set_use_animations (ClutterTableLayout *layout,
2296                                          gboolean            animate)
2297 {
2298   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
2299
2300   clutter_layout_manager_set_use_animations (CLUTTER_LAYOUT_MANAGER (layout),
2301                                              animate);
2302 }
2303
2304 /**
2305  * clutter_table_layout_get_use_animations:
2306  * @layout: a #ClutterTableLayout
2307  *
2308  * Retrieves whether @layout should animate changes in the layout properties
2309  *
2310  * Since clutter_table_layout_set_use_animations()
2311  *
2312  * Return value: %TRUE if the animations should be used, %FALSE otherwise
2313  *
2314  * Since: 1.4
2315  *
2316  * Deprecated: 1.12: #ClutterTable will honour the
2317  *   #ClutterLayoutManager:use-animations property
2318  */
2319 gboolean
2320 clutter_table_layout_get_use_animations (ClutterTableLayout *layout)
2321 {
2322   ClutterLayoutManager *manager;
2323
2324   g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), FALSE);
2325
2326   manager = CLUTTER_LAYOUT_MANAGER (layout);
2327
2328   return clutter_layout_manager_get_use_animations (manager);
2329 }
2330
2331 /**
2332  * clutter_table_layout_set_easing_mode:
2333  * @layout: a #ClutterTableLayout
2334  * @mode: an easing mode, either from #ClutterAnimationMode or a logical id
2335  *   from clutter_alpha_register_func()
2336  *
2337  * Sets the easing mode to be used by @layout when animating changes in layout
2338  * properties
2339  *
2340  * Use clutter_table_layout_set_use_animations() to enable and disable the
2341  * animations
2342  *
2343  * Since: 1.4
2344  *
2345  * Deprecated: 1.12: #ClutterTableLayout will honour the
2346  *   #ClutterLayoutManager:easing-mode property
2347  */
2348 void
2349 clutter_table_layout_set_easing_mode (ClutterTableLayout *layout,
2350                                       gulong              mode)
2351 {
2352   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
2353
2354   clutter_layout_manager_set_easing_mode (CLUTTER_LAYOUT_MANAGER (layout),
2355                                           mode);
2356 }
2357
2358 /**
2359  * clutter_table_layout_get_easing_mode:
2360  * @layout: a #ClutterTableLayout
2361  *
2362  * Retrieves the easing mode set using clutter_table_layout_set_easing_mode()
2363  *
2364  * Return value: an easing mode
2365  *
2366  * Since: 1.4
2367  *
2368  * Deprecated: 1.12: #ClutterTableLayout will honour the
2369  *   #ClutterLayoutManager:easing-mode property
2370  */
2371 gulong
2372 clutter_table_layout_get_easing_mode (ClutterTableLayout *layout)
2373 {
2374   ClutterLayoutManager *manager;
2375
2376   g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout),
2377                         CLUTTER_EASE_OUT_CUBIC);
2378
2379   manager = CLUTTER_LAYOUT_MANAGER (layout);
2380
2381   return clutter_layout_manager_get_easing_mode (manager);
2382 }
2383
2384 /**
2385  * clutter_table_layout_set_easing_duration:
2386  * @layout: a #ClutterTableLayout
2387  * @msecs: the duration of the animations, in milliseconds
2388  *
2389  * Sets the duration of the animations used by @layout when animating changes
2390  * in the layout properties
2391  *
2392  * Use clutter_table_layout_set_use_animations() to enable and disable the
2393  * animations
2394  *
2395  * Since: 1.4
2396  *
2397  * Deprecated: 1.12: #ClutterTableLayout will honour the
2398  *   #ClutterLayoutManager:easing-duration property
2399  */
2400 void
2401 clutter_table_layout_set_easing_duration (ClutterTableLayout *layout,
2402                                           guint               msecs)
2403 {
2404   g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
2405
2406   clutter_layout_manager_set_easing_duration (CLUTTER_LAYOUT_MANAGER (layout),
2407                                               msecs);
2408 }
2409
2410 /**
2411  * clutter_table_layout_get_easing_duration:
2412  * @layout: a #ClutterTableLayout
2413  *
2414  * Retrieves the duration set using clutter_table_layout_set_easing_duration()
2415  *
2416  * Return value: the duration of the animations, in milliseconds
2417  *
2418  * Since: 1.4
2419  *
2420  * Deprecated: 1.12
2421  */
2422 guint
2423 clutter_table_layout_get_easing_duration (ClutterTableLayout *layout)
2424 {
2425   ClutterLayoutManager *manager;
2426
2427   g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), 500);
2428
2429   manager = CLUTTER_LAYOUT_MANAGER (layout);
2430
2431   return clutter_layout_manager_get_easing_duration (manager);
2432 }
2433
2434
2435 /**
2436  * clutter_table_layout_get_row_count:
2437  * @layout: A #ClutterTableLayout
2438  *
2439  * Retrieve the current number rows in the @layout
2440  *
2441  * Returns: the number of rows
2442  *
2443  * Since: 1.4
2444  */
2445 gint
2446 clutter_table_layout_get_row_count (ClutterTableLayout *layout)
2447 {
2448   g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), -1);
2449
2450   update_row_col (layout, layout->priv->container);
2451   return CLUTTER_TABLE_LAYOUT (layout)->priv->n_rows;
2452 }
2453
2454 /**
2455  * clutter_table_layout_get_column_count:
2456  * @layout: A #ClutterTableLayout
2457  *
2458  * Retrieve the current number of columns in @layout
2459  *
2460  * Returns: the number of columns
2461  *
2462  * Since: 1.4
2463  */
2464 gint
2465 clutter_table_layout_get_column_count (ClutterTableLayout *layout)
2466 {
2467   g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), -1);
2468
2469   update_row_col (layout, layout->priv->container);
2470   return CLUTTER_TABLE_LAYOUT (layout)->priv->n_cols;
2471 }