docs: Change GridLayout's short description
[profile/ivi/clutter.git] / clutter / clutter-grid-layout.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2010 Red Hat, Inc.
7  * Copyright (C) 2012 Bastian Winkler <buz@netbuz.org>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
21  *
22  * Author:
23  *   Bastian Winkler <buz@netbuz.org>
24  *
25  * Based on GtkGrid widget by:
26  *   Matthias Clasen
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <string.h>
34 #include <math.h>
35
36 #include "clutter-grid-layout.h"
37
38 #include "clutter-actor-private.h"
39 #include "clutter-container.h"
40 #include "clutter-debug.h"
41 #include "clutter-enum-types.h"
42 #include "clutter-layout-meta.h"
43 #include "clutter-private.h"
44
45 /**
46  * SECTION:clutter-grid-layout
47  * @Short_description: A layout manager for a grid of actors
48  * @Title: ClutterGridLayout
49  * @See_also: #ClutterTableLayout, #ClutterBoxLayout
50  *
51  * #ClutterGridLayout is a layout manager which arranges its child widgets in
52  * rows and columns. It is a very similar to #ClutterTableLayout and
53  * #ClutterBoxLayout, but it consistently uses #ClutterActor's
54  * alignment and expansion flags instead of custom child properties.
55  *
56  * Children are added using clutter_grid_layout_attach(). They can span
57  * multiple rows or columns. It is also possible to add a child next to an
58  * existing child, using clutter_grid_layout_attach_next_to(). The behaviour of
59  * #ClutterGridLayout when several children occupy the same grid cell is undefined.
60  *
61  * #ClutterGridLayout can be used like a #ClutterBoxLayout by just using
62  * clutter_actor_add_child(), which will place children next to each other in
63  * the direction determined by the #ClutterGridLayout:orientation property.
64  */
65
66 #define CLUTTER_TYPE_GRID_CHILD          (clutter_grid_child_get_type ())
67 #define CLUTTER_GRID_CHILD(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GRID_CHILD, ClutterGridChild))
68 #define CLUTTER_IS_GRID_CHILD(obj)       (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GRID_CHILD))
69 #define CLUTTER_GRID_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayoutPrivate))
70
71 typedef struct _ClutterGridChild        ClutterGridChild;
72 typedef struct _ClutterLayoutMetaClass  ClutterGridChildClass;
73
74 typedef struct _ClutterGridAttach       ClutterGridAttach;
75 typedef struct _ClutterGridLine         ClutterGridLine;
76 typedef struct _ClutterGridLines        ClutterGridLines;
77 typedef struct _ClutterGridLineData     ClutterGridLineData;
78 typedef struct _ClutterGridRequest      ClutterGridRequest;
79
80
81 struct _ClutterGridAttach
82 {
83   gint pos;
84   gint span;
85 };
86
87 struct _ClutterGridChild
88 {
89   ClutterLayoutMeta parent_instance;
90
91   ClutterGridAttach attach[2];
92 };
93
94 #define CHILD_LEFT(child)    ((child)->attach[CLUTTER_ORIENTATION_HORIZONTAL].pos)
95 #define CHILD_WIDTH(child)   ((child)->attach[CLUTTER_ORIENTATION_HORIZONTAL].span)
96 #define CHILD_TOP(child)     ((child)->attach[CLUTTER_ORIENTATION_VERTICAL].pos)
97 #define CHILD_HEIGHT(child)  ((child)->attach[CLUTTER_ORIENTATION_VERTICAL].span)
98
99 /* A ClutterGridLineData struct contains row/column specific parts
100  * of the grid.
101  */
102 struct _ClutterGridLineData
103 {
104   gfloat spacing;
105   guint homogeneous : 1;
106 };
107
108 struct _ClutterGridLayoutPrivate
109 {
110   ClutterContainer *container;
111   ClutterOrientation orientation;
112
113   ClutterGridLineData linedata[2];
114 };
115
116 #define ROWS(priv)    (&(priv)->linedata[CLUTTER_ORIENTATION_HORIZONTAL])
117 #define COLUMNS(priv) (&(priv)->linedata[CLUTTER_ORIENTATION_VERTICAL])
118
119 /* A ClutterGridLine struct represents a single row or column
120  * during size requests
121  */
122 struct _ClutterGridLine
123 {
124   gfloat minimum;
125   gfloat natural;
126   gfloat position;
127   gfloat allocation;
128
129   guint need_expand : 1;
130   guint expand      : 1;
131   guint empty       : 1;
132 };
133
134 struct _ClutterGridLines
135 {
136   ClutterGridLine *lines;
137   gint min, max;
138 };
139
140 struct _ClutterGridRequest
141 {
142   ClutterGridLayout *grid;
143   ClutterGridLines lines[2];
144 };
145
146 enum
147 {
148   PROP_0,
149
150   PROP_ORIENTATION,
151   PROP_ROW_SPACING,
152   PROP_COLUMN_SPACING,
153   PROP_ROW_HOMOGENEOUS,
154   PROP_COLUMN_HOMOGENEOUS,
155
156   PROP_LAST
157 };
158 static GParamSpec *obj_props[PROP_LAST];
159
160 enum
161 {
162   PROP_CHILD_0,
163
164   PROP_CHILD_LEFT_ATTACH,
165   PROP_CHILD_TOP_ATTACH,
166   PROP_CHILD_WIDTH,
167   PROP_CHILD_HEIGHT,
168
169   PROP_CHILD_LAST
170 };
171 static GParamSpec *child_props[PROP_CHILD_LAST];
172
173 GType clutter_grid_child_get_type (void);
174
175 G_DEFINE_TYPE (ClutterGridChild, clutter_grid_child,
176                CLUTTER_TYPE_LAYOUT_META);
177 G_DEFINE_TYPE (ClutterGridLayout, clutter_grid_layout,
178                CLUTTER_TYPE_LAYOUT_MANAGER);
179
180
181 #define GET_GRID_CHILD(grid, child) \
182   (CLUTTER_GRID_CHILD(clutter_layout_manager_get_child_meta \
183    (CLUTTER_LAYOUT_MANAGER((grid)),\
184     CLUTTER_GRID_LAYOUT((grid))->priv->container,(child))))
185
186 static void
187 grid_attach (ClutterGridLayout *self,
188              ClutterActor      *actor,
189              gint               left,
190              gint               top,
191              gint               width,
192              gint               height)
193 {
194   ClutterGridChild *grid_child;
195
196   grid_child = GET_GRID_CHILD (self, actor);
197
198   CHILD_LEFT (grid_child) = left;
199   CHILD_TOP (grid_child) = top;
200   CHILD_WIDTH (grid_child) = width;
201   CHILD_HEIGHT (grid_child) = height;
202 }
203
204 /* Find the position 'touching' existing
205  * children. @orientation and @max determine
206  * from which direction to approach (horizontal
207  * + max = right, vertical + !max = top, etc).
208  * @op_pos, @op_span determine the rows/columns
209  * in which the touching has to happen.
210  */
211 static gint
212 find_attach_position (ClutterGridLayout  *self,
213                       ClutterOrientation  orientation,
214                       gint                op_pos,
215                       gint                op_span,
216                       gboolean            max)
217 {
218   ClutterGridLayoutPrivate *priv = self->priv;
219   ClutterGridChild *grid_child;
220   ClutterGridAttach *attach;
221   ClutterGridAttach *opposite;
222   ClutterActorIter iter;
223   ClutterActor *child;
224   gint pos;
225   gboolean hit;
226
227   if (max)
228     pos = -G_MAXINT;
229   else
230     pos = G_MAXINT;
231
232   hit = FALSE;
233
234   if (!priv->container)
235     return -1;
236
237   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
238   while (clutter_actor_iter_next (&iter, &child))
239     {
240       grid_child = GET_GRID_CHILD (self, child);
241
242       attach = &grid_child->attach[orientation];
243       opposite = &grid_child->attach[1 - orientation];
244
245       /* check if the ranges overlap */
246       if (opposite->pos <= op_pos + op_span && op_pos <= opposite->pos + opposite->span)
247         {
248           hit = TRUE;
249
250           if (max)
251             pos = MAX (pos, attach->pos + attach->span);
252           else
253             pos = MIN (pos, attach->pos);
254         }
255      }
256
257   if (!hit)
258     pos = 0;
259
260   return pos;
261 }
262 static void
263 grid_attach_next_to (ClutterGridLayout   *layout,
264                      ClutterActor        *child,
265                      ClutterActor        *sibling,
266                      ClutterGridPosition  side,
267                      gint                 width,
268                      gint                 height)
269 {
270   ClutterGridChild *grid_sibling;
271   gint left, top;
272
273   if (sibling)
274     {
275       grid_sibling = GET_GRID_CHILD (layout, sibling);
276
277       switch (side)
278         {
279         case CLUTTER_GRID_POSITION_LEFT:
280           left = CHILD_LEFT (grid_sibling) - width;
281           top = CHILD_TOP (grid_sibling);
282           break;
283
284         case CLUTTER_GRID_POSITION_RIGHT:
285           left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling);
286           top = CHILD_TOP (grid_sibling);
287           break;
288
289         case CLUTTER_GRID_POSITION_TOP:
290           left = CHILD_LEFT (grid_sibling);
291           top = CHILD_TOP (grid_sibling) - height;
292           break;
293
294         case CLUTTER_GRID_POSITION_BOTTOM:
295           left = CHILD_LEFT (grid_sibling);
296           top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling);
297           break;
298
299         default:
300           g_assert_not_reached ();
301         }
302     }
303   else
304     {
305       switch (side)
306         {
307         case CLUTTER_GRID_POSITION_LEFT:
308           left = find_attach_position (layout, CLUTTER_ORIENTATION_HORIZONTAL,
309                                        0, height, FALSE);
310           left -= width;
311           top = 0;
312           break;
313
314         case CLUTTER_GRID_POSITION_RIGHT:
315           left = find_attach_position (layout, CLUTTER_ORIENTATION_HORIZONTAL,
316                                        0, height, TRUE);
317           top = 0;
318           break;
319
320         case CLUTTER_GRID_POSITION_TOP:
321           left = 0;
322           top = find_attach_position (layout, CLUTTER_ORIENTATION_VERTICAL,
323                                       0, width, FALSE);
324           top -= height;
325           break;
326
327         case CLUTTER_GRID_POSITION_BOTTOM:
328           left = 0;
329           top = find_attach_position (layout, CLUTTER_ORIENTATION_VERTICAL,
330                                       0, width, TRUE);
331           break;
332
333         default:
334           g_assert_not_reached ();
335         }
336     }
337
338   grid_attach (layout, child, left, top, width, height);
339 }
340
341 static void
342 clutter_grid_request_update_child_attach (ClutterGridRequest *request,
343                                           ClutterActor       *actor)
344 {
345   ClutterGridLayoutPrivate *priv = request->grid->priv;
346   ClutterGridChild *grid_child;
347
348   grid_child = GET_GRID_CHILD (request->grid, actor);
349
350   if (CHILD_LEFT (grid_child) == -1 || CHILD_TOP (grid_child) == -1)
351     {
352       ClutterGridPosition side;
353       ClutterActor *sibling;
354
355       if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL)
356         {
357           ClutterTextDirection td;
358           gboolean rtl;
359           ClutterActor *container = CLUTTER_ACTOR (priv->container);
360
361           td = clutter_actor_get_text_direction (container);
362           rtl = (td == CLUTTER_TEXT_DIRECTION_RTL) ? TRUE : FALSE;
363           side = rtl ? CLUTTER_GRID_POSITION_RIGHT : CLUTTER_GRID_POSITION_LEFT;
364         }
365       else
366         {
367           /* XXX: maybe we should also add a :pack-start property to modify
368            * this */
369           side = CLUTTER_GRID_POSITION_BOTTOM;
370         }
371
372       sibling = clutter_actor_get_previous_sibling (actor);
373       grid_attach_next_to (request->grid, actor, sibling, side,
374                            CHILD_WIDTH (grid_child),
375                            CHILD_HEIGHT (grid_child));
376     }
377 }
378
379 static void
380 clutter_grid_request_update_attach (ClutterGridRequest *request)
381 {
382   ClutterGridLayoutPrivate *priv = request->grid->priv;
383   ClutterActorIter iter;
384   ClutterActor *child;
385
386   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
387   while (clutter_actor_iter_next (&iter, &child))
388     clutter_grid_request_update_child_attach (request, child);
389 }
390
391 /* Calculates the min and max numbers for both orientations.
392  */
393 static void
394 clutter_grid_request_count_lines (ClutterGridRequest *request)
395 {
396   ClutterGridLayoutPrivate *priv = request->grid->priv;
397   ClutterGridChild *grid_child;
398   ClutterGridAttach *attach;
399   ClutterActorIter iter;
400   ClutterActor *child;
401   gint min[2];
402   gint max[2];
403
404   min[0] = min[1] = G_MAXINT;
405   max[0] = max[1] = G_MININT;
406
407   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
408   while (clutter_actor_iter_next (&iter, &child))
409     {
410       grid_child = GET_GRID_CHILD (request->grid, child);
411       attach = grid_child->attach;
412
413       min[0] = MIN (min[0], attach[0].pos);
414       max[0] = MAX (max[0], attach[0].pos + attach[0].span);
415       min[1] = MIN (min[1], attach[1].pos);
416       max[1] = MAX (max[1], attach[1].pos + attach[1].span);
417     }
418
419   request->lines[0].min = min[0];
420   request->lines[0].max = max[0];
421   request->lines[1].min = min[1];
422   request->lines[1].max = max[1];
423 }
424
425 /* Sets line sizes to 0 and marks lines as expand
426  * if they have a non-spanning expanding child.
427  */
428 static void
429 clutter_grid_request_init (ClutterGridRequest *request,
430                            ClutterOrientation  orientation)
431 {
432   ClutterGridLayoutPrivate *priv = request->grid->priv;
433   ClutterGridChild *grid_child;
434   ClutterGridAttach *attach;
435   ClutterGridLines *lines;
436   ClutterActorIter iter;
437   ClutterActor *child;
438   gint i;
439
440   lines = &request->lines[orientation];
441
442   for (i = 0; i < lines->max - lines->min; i++)
443     {
444       lines->lines[i].minimum = 0;
445       lines->lines[i].natural = 0;
446       lines->lines[i].expand = FALSE;
447     }
448
449   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
450   while (clutter_actor_iter_next (&iter, &child))
451     {
452       grid_child = GET_GRID_CHILD (request->grid, child);
453       attach = &grid_child->attach[orientation];
454       if (attach->span == 1 && clutter_actor_needs_expand (child, orientation))
455         lines->lines[attach->pos - lines->min].expand = TRUE;
456     }
457 }
458
459 /* Sums allocations for lines spanned by child and their spacing.
460  */
461 static gfloat
462 compute_allocation_for_child (ClutterGridRequest *request,
463                               ClutterActor       *child,
464                               ClutterOrientation  orientation)
465 {
466   ClutterGridLayoutPrivate *priv = request->grid->priv;
467   ClutterGridChild *grid_child;
468   ClutterGridLineData *linedata;
469   ClutterGridLines *lines;
470   ClutterGridLine *line;
471   ClutterGridAttach *attach;
472   gfloat size;
473   gint i;
474
475   grid_child = GET_GRID_CHILD (request->grid, child);
476   linedata = &priv->linedata[orientation];
477   lines = &request->lines[orientation];
478   attach = &grid_child->attach[orientation];
479
480   size = (attach->span - 1) * linedata->spacing;
481   for (i = 0; i < attach->span; i++)
482     {
483       line = &lines->lines[attach->pos - lines->min + i];
484       size += line->allocation;
485     }
486
487   return size;
488 }
489
490 static void
491 compute_request_for_child (ClutterGridRequest *request,
492                            ClutterActor       *child,
493                            ClutterOrientation  orientation,
494                            gboolean            contextual,
495                            gfloat             *minimum,
496                            gfloat             *natural)
497 {
498   if (contextual)
499     {
500       gfloat size;
501
502       size = compute_allocation_for_child (request, child, 1 - orientation);
503       if (orientation == CLUTTER_ORIENTATION_HORIZONTAL)
504         clutter_actor_get_preferred_width (child, size, minimum, natural);
505       else
506         clutter_actor_get_preferred_height (child, size, minimum, natural);
507     }
508   else
509     {
510       if (orientation == CLUTTER_ORIENTATION_VERTICAL)
511         clutter_actor_get_preferred_width (child, -1, minimum, natural);
512       else
513         clutter_actor_get_preferred_height (child, -1, minimum, natural);
514     }
515 }
516
517 /* Sets requisition to max. of non-spanning children.
518  * If contextual is TRUE, requires allocations of
519  * lines in the opposite orientation to be set.
520  */
521 static void
522 clutter_grid_request_non_spanning (ClutterGridRequest *request,
523                                    ClutterOrientation  orientation,
524                                    gboolean            contextual)
525 {
526   ClutterGridLayoutPrivate *priv = request->grid->priv;
527   ClutterGridChild *grid_child;
528   ClutterGridAttach *attach;
529   ClutterGridLines *lines;
530   ClutterGridLine *line;
531   ClutterActorIter iter;
532   ClutterActor *child;
533   gfloat minimum;
534   gfloat natural;
535
536   lines = &request->lines[orientation];
537
538   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
539   while (clutter_actor_iter_next (&iter, &child))
540     {
541       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
542         continue;
543
544       grid_child = GET_GRID_CHILD (request->grid, child);
545
546       attach = &grid_child->attach[orientation];
547       if (attach->span != 1)
548         continue;
549
550       compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
551
552       line = &lines->lines[attach->pos - lines->min];
553       line->minimum = MAX (line->minimum, minimum);
554       line->natural = MAX (line->natural, natural);
555     }
556 }
557
558 /* Enforce homogeneous sizes.
559  */
560 static void
561 clutter_grid_request_homogeneous (ClutterGridRequest *request,
562                                   ClutterOrientation  orientation)
563 {
564   ClutterGridLayoutPrivate *priv = request->grid->priv;
565   ClutterGridLineData *linedata;
566   ClutterGridLines *lines;
567   gfloat minimum, natural;
568   gint i;
569
570   linedata = &priv->linedata[orientation];
571   lines = &request->lines[orientation];
572
573   if (!linedata->homogeneous)
574     return;
575
576   minimum = 0.0f;
577   natural = 0.0f;
578
579   for (i = 0; i < lines->max - lines->min; i++)
580     {
581       minimum = MAX (minimum, lines->lines[i].minimum);
582       natural = MAX (natural, lines->lines[i].natural);
583     }
584
585   for (i = 0; i < lines->max - lines->min; i++)
586     {
587       lines->lines[i].minimum = minimum;
588       lines->lines[i].natural = natural;
589     }
590 }
591
592 /* Deals with spanning children.
593  * Requires expand fields of lines to be set for
594  * non-spanning children.
595  */
596 static void
597 clutter_grid_request_spanning (ClutterGridRequest *request,
598                                ClutterOrientation  orientation,
599                                gboolean            contextual)
600 {
601   ClutterGridLayoutPrivate *priv = request->grid->priv;
602   ClutterGridChild *grid_child;
603   ClutterActor *child;
604   ClutterActorIter iter;
605   ClutterGridAttach *attach;
606   ClutterGridLineData *linedata;
607   ClutterGridLines *lines;
608   ClutterGridLine *line;
609   gfloat minimum;
610   gfloat natural;
611   gint span_minimum;
612   gint span_natural;
613   gint span_expand;
614   gboolean force_expand;
615   gint extra;
616   gint expand;
617   gint line_extra;
618   gint i;
619
620   linedata = &priv->linedata[orientation];
621   lines = &request->lines[orientation];
622
623   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
624   while (clutter_actor_iter_next (&iter, &child))
625     {
626       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
627         continue;
628
629       grid_child = GET_GRID_CHILD (request->grid, child);
630
631       attach = &grid_child->attach[orientation];
632       if (attach->span == 1)
633         continue;
634
635       compute_request_for_child (request, child, orientation, contextual,
636                                  &minimum, &natural);
637
638       span_minimum = (attach->span - 1) * linedata->spacing;
639       span_natural = (attach->span - 1) * linedata->spacing;
640       span_expand = 0;
641       force_expand = FALSE;
642       for (i = 0; i < attach->span; i++)
643         {
644           line = &lines->lines[attach->pos - lines->min + i];
645           span_minimum += line->minimum;
646           span_natural += line->natural;
647           if (line->expand)
648             span_expand += 1;
649         }
650       if (span_expand == 0)
651         {
652           span_expand = attach->span;
653           force_expand = TRUE;
654         }
655
656       /* If we need to request more space for this child to fill
657        * its requisition, then divide up the needed space amongst the
658        * lines it spans, favoring expandable lines if any.
659        *
660        * When doing homogeneous allocation though, try to keep the
661        * line allocations even, since we're going to force them to
662        * be the same anyway, and we don't want to introduce unnecessary
663        * extra space.
664        */
665       if (span_minimum < minimum)
666         {
667           if (linedata->homogeneous)
668             {
669               gint total, m;
670
671               total = minimum - (attach->span - 1) * linedata->spacing;
672               m = total / attach->span + (total % attach->span ? 1 : 0);
673               for (i = 0; i < attach->span; i++)
674                 {
675                   line = &lines->lines[attach->pos - lines->min + i];
676                   line->minimum = MAX(line->minimum, m);
677                 }
678             }
679           else
680             {
681               extra = minimum - span_minimum;
682               expand = span_expand;
683               for (i = 0; i < attach->span; i++)
684                 {
685                   line = &lines->lines[attach->pos - lines->min + i];
686                   if (force_expand || line->expand)
687                     {
688                       line_extra = extra / expand;
689                       line->minimum += line_extra;
690                       extra -= line_extra;
691                       expand -= 1;
692                     }
693                 }
694             }
695         }
696
697       if (span_natural < natural)
698         {
699           if (linedata->homogeneous)
700             {
701               gint total, n;
702
703               total = natural - (attach->span - 1) * linedata->spacing;
704               n = total / attach->span + (total % attach->span ? 1 : 0);
705               for (i = 0; i < attach->span; i++)
706                 {
707                   line = &lines->lines[attach->pos - lines->min + i];
708                   line->natural = MAX(line->natural, n);
709                 }
710             }
711           else
712             {
713               extra = natural - span_natural;
714               expand = span_expand;
715               for (i = 0; i < attach->span; i++)
716                 {
717                   line = &lines->lines[attach->pos - lines->min + i];
718                   if (force_expand || line->expand)
719                     {
720                       line_extra = extra / expand;
721                       line->natural += line_extra;
722                       extra -= line_extra;
723                       expand -= 1;
724                     }
725                 }
726             }
727         }
728     }
729 }
730
731 /* Marks empty and expanding lines and counts them.
732  */
733 static void
734 clutter_grid_request_compute_expand (ClutterGridRequest *request,
735                                      ClutterOrientation  orientation,
736                                      gint               *nonempty_lines,
737                                      gint               *expand_lines)
738 {
739   ClutterGridLayoutPrivate *priv = request->grid->priv;
740   ClutterGridChild *grid_child;
741   ClutterGridAttach *attach;
742   ClutterActorIter iter;
743   ClutterActor *child;
744   gint i;
745   ClutterGridLines *lines;
746   ClutterGridLine *line;
747   gboolean has_expand;
748   gint expand;
749   gint empty;
750
751   lines = &request->lines[orientation];
752
753   for (i = 0; i < lines->max - lines->min; i++)
754     {
755       lines->lines[i].need_expand = FALSE;
756       lines->lines[i].expand = FALSE;
757       lines->lines[i].empty = TRUE;
758     }
759
760   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
761   while (clutter_actor_iter_next (&iter, &child))
762     {
763       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
764         continue;
765
766       grid_child = GET_GRID_CHILD (request->grid, child);
767
768       attach = &grid_child->attach[orientation];
769       if (attach->span != 1)
770         continue;
771
772       line = &lines->lines[attach->pos - lines->min];
773       line->empty = FALSE;
774       if (clutter_actor_needs_expand (child, orientation))
775         line->expand = TRUE;
776     }
777
778
779   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
780   while (clutter_actor_iter_next (&iter, &child))
781     {
782       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
783         continue;
784
785       grid_child = GET_GRID_CHILD (request->grid, child);
786
787       attach = &grid_child->attach[orientation];
788       if (attach->span == 1)
789         continue;
790
791       has_expand = FALSE;
792       for (i = 0; i < attach->span; i++)
793         {
794           line = &lines->lines[attach->pos - lines->min + i];
795           line->empty = FALSE;
796           if (line->expand)
797             has_expand = TRUE;
798         }
799
800       if (!has_expand && clutter_actor_needs_expand (child, orientation))
801         {
802           for (i = 0; i < attach->span; i++)
803             {
804               line = &lines->lines[attach->pos - lines->min + i];
805               line->need_expand = TRUE;
806             }
807         }
808     }
809
810   empty = 0;
811   expand = 0;
812   for (i = 0; i < lines->max - lines->min; i++)
813     {
814       line = &lines->lines[i];
815
816       if (line->need_expand)
817         line->expand = TRUE;
818
819       if (line->empty)
820         empty += 1;
821
822       if (line->expand)
823         expand += 1;
824     }
825
826   if (nonempty_lines)
827     *nonempty_lines = lines->max - lines->min - empty;
828
829   if (expand_lines)
830     *expand_lines = expand;
831 }
832
833 /* Sums the minimum and natural fields of lines and their spacing.
834  */
835 static void
836 clutter_grid_request_sum (ClutterGridRequest *request,
837                           ClutterOrientation  orientation,
838                           gfloat             *minimum,
839                           gfloat             *natural)
840 {
841   ClutterGridLayoutPrivate *priv = request->grid->priv;
842   ClutterGridLineData *linedata;
843   ClutterGridLines *lines;
844   gint i;
845   gfloat min, nat;
846   gint nonempty;
847
848   clutter_grid_request_compute_expand (request, orientation, &nonempty, NULL);
849
850   linedata = &priv->linedata[orientation];
851   lines = &request->lines[orientation];
852
853   min = 0;
854   nat = 0;
855   if (nonempty > 0)
856     {
857       min = (nonempty - 1) * linedata->spacing;
858       nat = (nonempty - 1) * linedata->spacing;
859     }
860
861   for (i = 0; i < lines->max - lines->min; i++)
862     {
863       min += lines->lines[i].minimum;
864       nat += lines->lines[i].natural;
865     }
866
867   if (minimum)
868     *minimum = min;
869
870   if (natural)
871     *natural = nat;
872 }
873
874 /* Computes minimum and natural fields of lines.
875  * When contextual is TRUE, requires allocation of
876  * lines in the opposite orientation to be set.
877  */
878 static void
879 clutter_grid_request_run (ClutterGridRequest *request,
880                           ClutterOrientation  orientation,
881                           gboolean            contextual)
882 {
883   clutter_grid_request_init (request, orientation);
884   clutter_grid_request_non_spanning (request, orientation, contextual);
885   clutter_grid_request_homogeneous (request, orientation);
886   clutter_grid_request_spanning (request, orientation, contextual);
887   clutter_grid_request_homogeneous (request, orientation);
888 }
889
890 typedef struct _RequestedSize
891 {
892   gpointer data;
893
894   gfloat minimum_size;
895   gfloat natural_size;
896 } RequestedSize;
897
898
899 /* Pulled from gtksizerequest.c from Gtk+ */
900 static gint
901 compare_gap (gconstpointer p1,
902              gconstpointer p2,
903              gpointer      data)
904 {
905   RequestedSize *sizes = data;
906   const guint *c1 = p1;
907   const guint *c2 = p2;
908
909   const gint d1 = MAX (sizes[*c1].natural_size -
910                        sizes[*c1].minimum_size,
911                        0);
912   const gint d2 = MAX (sizes[*c2].natural_size -
913                        sizes[*c2].minimum_size,
914                        0);
915
916   gint delta = (d2 - d1);
917
918   if (0 == delta)
919     delta = (*c2 - *c1);
920
921   return delta;
922 }
923
924 /*
925  * distribute_natural_allocation:
926  * @extra_space: Extra space to redistribute among children after subtracting
927  *   minimum sizes and any child padding from the overall allocation
928  * @n_requested_sizes: Number of requests to fit into the allocation
929  * @sizes: An array of structs with a client pointer and a minimum/natural size
930  *   in the orientation of the allocation.
931  *
932  * Distributes @extra_space to child @sizes by bringing smaller
933  * children up to natural size first.
934  *
935  * The remaining space will be added to the @minimum_size member of the
936  * RequestedSize struct. If all sizes reach their natural size then
937  * the remaining space is returned.
938  *
939  * Returns: The remainder of @extra_space after redistributing space
940  * to @sizes.
941  *
942  * Pulled from gtksizerequest.c from Gtk+
943  */
944 static gint
945 distribute_natural_allocation (gint           extra_space,
946                                guint          n_requested_sizes,
947                                RequestedSize *sizes)
948 {
949   guint *spreading;
950   gint   i;
951
952   g_return_val_if_fail (extra_space >= 0, 0);
953
954   spreading = g_newa (guint, n_requested_sizes);
955
956   for (i = 0; i < n_requested_sizes; i++)
957     spreading[i] = i;
958
959   /* Distribute the container's extra space c_gap. We want to assign
960    * this space such that the sum of extra space assigned to children
961    * (c^i_gap) is equal to c_cap. The case that there's not enough
962    * space for all children to take their natural size needs some
963    * attention. The goals we want to achieve are:
964    *
965    *   a) Maximize number of children taking their natural size.
966    *   b) The allocated size of children should be a continuous
967    *   function of c_gap.  That is, increasing the container size by
968    *   one pixel should never make drastic changes in the distribution.
969    *   c) If child i takes its natural size and child j doesn't,
970    *   child j should have received at least as much gap as child i.
971    *
972    * The following code distributes the additional space by following
973    * these rules.
974    */
975
976   /* Sort descending by gap and position. */
977   g_qsort_with_data (spreading,
978                      n_requested_sizes, sizeof (guint),
979                      compare_gap, sizes);
980
981   /* Distribute available space.
982    * This master piece of a loop was conceived by Behdad Esfahbod.
983    */
984   for (i = n_requested_sizes - 1; extra_space > 0 && i >= 0; --i)
985     {
986       /* Divide remaining space by number of remaining children.
987        * Sort order and reducing remaining space by assigned space
988        * ensures that space is distributed equally.
989        */
990       gint glue = (extra_space + i) / (i + 1);
991       gint gap = sizes[(spreading[i])].natural_size
992                - sizes[(spreading[i])].minimum_size;
993
994       gint extra = MIN (glue, gap);
995
996       sizes[spreading[i]].minimum_size += extra;
997
998       extra_space -= extra;
999     }
1000
1001   return extra_space;
1002 }
1003
1004 /* Requires that the minimum and natural fields of lines
1005  * have been set, computes the allocation field of lines
1006  * by distributing total_size among lines.
1007  */
1008 static void
1009 clutter_grid_request_allocate (ClutterGridRequest *request,
1010                                ClutterOrientation  orientation,
1011                                gfloat              total_size)
1012 {
1013   ClutterGridLayoutPrivate *priv = request->grid->priv;
1014   ClutterGridLineData *linedata;
1015   ClutterGridLines *lines;
1016   ClutterGridLine *line;
1017   gint nonempty;
1018   gint expand;
1019   gint i, j;
1020   RequestedSize *sizes;
1021   gint extra;
1022   gint rest;
1023   gint size;
1024
1025   clutter_grid_request_compute_expand (request, orientation, &nonempty, &expand);
1026
1027   if (nonempty == 0)
1028     return;
1029
1030   linedata = &priv->linedata[orientation];
1031   lines = &request->lines[orientation];
1032
1033   size = total_size - (nonempty - 1) * linedata->spacing;
1034
1035   if (linedata->homogeneous)
1036     {
1037       extra = size / nonempty;
1038       rest = size % nonempty;
1039
1040       for (i = 0; i < lines->max - lines->min; i++)
1041         {
1042           line = &lines->lines[i];
1043           if (line->empty)
1044             continue;
1045
1046           line->allocation = extra;
1047           if (rest > 0)
1048             {
1049               line->allocation += 1;
1050               rest -= 1;
1051             }
1052         }
1053     }
1054   else
1055     {
1056       sizes = g_newa (RequestedSize, nonempty);
1057
1058       j = 0;
1059       for (i = 0; i < lines->max - lines->min; i++)
1060         {
1061           line = &lines->lines[i];
1062           if (line->empty)
1063             continue;
1064
1065           size -= line->minimum;
1066
1067           sizes[j].minimum_size = line->minimum;
1068           sizes[j].natural_size = line->natural;
1069           sizes[j].data = line;
1070           j++;
1071         }
1072
1073       size = distribute_natural_allocation (MAX (0, size), nonempty, sizes);
1074
1075       if (expand > 0)
1076         {
1077           extra = size / expand;
1078           rest = size % expand;
1079         }
1080       else
1081         {
1082           extra = 0;
1083           rest = 0;
1084         }
1085
1086       j = 0;
1087       for (i = 0; i < lines->max - lines->min; i++)
1088         {
1089           line = &lines->lines[i];
1090           if (line->empty)
1091             continue;
1092
1093           g_assert (line == sizes[j].data);
1094
1095           line->allocation = sizes[j].minimum_size;
1096           if (line->expand)
1097             {
1098               line->allocation += extra;
1099               if (rest > 0)
1100                 {
1101                   line->allocation += 1;
1102                   rest -= 1;
1103                 }
1104             }
1105
1106           j++;
1107         }
1108     }
1109 }
1110
1111 /* Computes the position fields from allocation and spacing.
1112  */
1113 static void
1114 clutter_grid_request_position (ClutterGridRequest *request,
1115                                ClutterOrientation  orientation)
1116 {
1117   ClutterGridLayoutPrivate *priv = request->grid->priv;
1118   ClutterGridLineData *linedata;
1119   ClutterGridLines *lines;
1120   ClutterGridLine *line;
1121   gfloat position;
1122   gint i;
1123
1124   linedata = &priv->linedata[orientation];
1125   lines = &request->lines[orientation];
1126
1127   position = 0.f;
1128   for (i = 0; i < lines->max - lines->min; i++)
1129     {
1130       line = &lines->lines[i];
1131       if (!line->empty)
1132         {
1133           line->position = position;
1134           position += line->allocation + linedata->spacing;
1135         }
1136     }
1137 }
1138
1139 static void
1140 clutter_grid_child_set_property (GObject      *gobject,
1141                                  guint         prop_id,
1142                                  const GValue *value,
1143                                  GParamSpec   *pspec)
1144 {
1145   ClutterGridChild *grid_child = CLUTTER_GRID_CHILD (gobject);
1146   ClutterLayoutManager *manager;
1147
1148   manager = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (gobject));
1149
1150   switch (prop_id)
1151     {
1152     case PROP_CHILD_LEFT_ATTACH:
1153       CHILD_LEFT (grid_child) = g_value_get_int (value);
1154       clutter_layout_manager_layout_changed (manager);
1155       break;
1156
1157     case PROP_CHILD_TOP_ATTACH:
1158       CHILD_TOP (grid_child) = g_value_get_int (value);
1159       clutter_layout_manager_layout_changed (manager);
1160       break;
1161
1162     case PROP_CHILD_WIDTH:
1163       CHILD_WIDTH (grid_child) = g_value_get_int (value);
1164       clutter_layout_manager_layout_changed (manager);
1165       break;
1166
1167     case PROP_CHILD_HEIGHT:
1168       CHILD_HEIGHT (grid_child) = g_value_get_int (value);
1169       clutter_layout_manager_layout_changed (manager);
1170       break;
1171
1172     default:
1173       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1174       break;
1175     }
1176 }
1177
1178 static void
1179 clutter_grid_child_get_property (GObject    *gobject,
1180                                  guint       prop_id,
1181                                  GValue     *value,
1182                                  GParamSpec *pspec)
1183 {
1184   ClutterGridChild *grid_child = CLUTTER_GRID_CHILD (gobject);
1185
1186   switch (prop_id)
1187     {
1188     case PROP_CHILD_LEFT_ATTACH:
1189       g_value_set_int (value, CHILD_LEFT (grid_child));
1190       break;
1191
1192     case PROP_CHILD_TOP_ATTACH:
1193       g_value_set_int (value, CHILD_TOP (grid_child));
1194       break;
1195
1196     case PROP_CHILD_WIDTH:
1197       g_value_set_int (value, CHILD_WIDTH (grid_child));
1198       break;
1199
1200     case PROP_CHILD_HEIGHT:
1201       g_value_set_int (value, CHILD_HEIGHT (grid_child));
1202       break;
1203
1204     default:
1205       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1206       break;
1207     }
1208 }
1209
1210 static void
1211 clutter_grid_child_class_init (ClutterGridChildClass *klass)
1212 {
1213   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1214
1215   gobject_class->set_property = clutter_grid_child_set_property;
1216   gobject_class->get_property = clutter_grid_child_get_property;
1217
1218   child_props[PROP_CHILD_LEFT_ATTACH] =
1219     g_param_spec_int ("left-attach",
1220                       P_("Left attachment"),
1221                       P_("The column number to attach the left side of the "
1222                          "child to"),
1223                       -G_MAXINT, G_MAXINT, 0,
1224                       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1225
1226   child_props[PROP_CHILD_TOP_ATTACH] =
1227     g_param_spec_int ("top-attach",
1228                       P_("Top attachment"),
1229                       P_("The row number to attach the top side of a child "
1230                          "widget to"),
1231                       -G_MAXINT, G_MAXINT, 0,
1232                       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1233
1234   child_props[PROP_CHILD_WIDTH] =
1235     g_param_spec_int ("width",
1236                       P_("Width"),
1237                       P_("The number of columns that a child spans"),
1238                       -G_MAXINT, G_MAXINT, 1,
1239                       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1240
1241   child_props[PROP_CHILD_HEIGHT] =
1242     g_param_spec_int ("height",
1243                       P_("Height"),
1244                       P_("The number of rows that a child spans"),
1245                       -G_MAXINT, G_MAXINT, 1,
1246                       G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1247
1248   g_object_class_install_properties (gobject_class, PROP_CHILD_LAST,
1249                                      child_props);
1250 }
1251
1252 static void
1253 clutter_grid_child_init (ClutterGridChild *self)
1254 {
1255   CHILD_LEFT (self) = -1;
1256   CHILD_TOP (self) = -1;
1257   CHILD_WIDTH (self) = 1;
1258   CHILD_HEIGHT (self) = 1;
1259 }
1260
1261 static void
1262 clutter_grid_layout_set_container (ClutterLayoutManager *self,
1263                                    ClutterContainer     *container)
1264 {
1265   ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv;
1266   ClutterLayoutManagerClass *parent_class;
1267
1268   priv->container = container;
1269
1270   if (priv->container != NULL)
1271     {
1272       ClutterRequestMode request_mode;
1273
1274       /* we need to change the :request-mode of the container
1275        * to match the orientation
1276        */
1277       request_mode = priv->orientation == CLUTTER_ORIENTATION_VERTICAL
1278                    ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH
1279                    : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT;
1280       clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container),
1281                                       request_mode);
1282     }
1283
1284   parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_grid_layout_parent_class);
1285   parent_class->set_container (self, container);
1286 }
1287
1288 static void
1289 clutter_grid_layout_get_preferred_width (ClutterLayoutManager *self,
1290                                          ClutterContainer     *container,
1291                                          gfloat                for_height,
1292                                          gfloat               *min_width_p,
1293                                          gfloat               *nat_width_p)
1294 {
1295   ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv;
1296   ClutterGridRequest request;
1297   ClutterGridLines *lines;
1298
1299   if (min_width_p)
1300     *min_width_p = 0.0f;
1301   if (nat_width_p)
1302     *nat_width_p = 0.0f;
1303
1304   request.grid = CLUTTER_GRID_LAYOUT (self);
1305   clutter_grid_request_update_attach (&request);
1306   clutter_grid_request_count_lines (&request);
1307   lines = &request.lines[priv->orientation];
1308   lines->lines = g_newa (ClutterGridLine, lines->max - lines->min);
1309   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine));
1310
1311   clutter_grid_request_run (&request, priv->orientation, FALSE);
1312   clutter_grid_request_sum (&request, priv->orientation,
1313                             min_width_p, nat_width_p);
1314 }
1315
1316 static void
1317 clutter_grid_layout_get_preferred_height (ClutterLayoutManager *self,
1318                                           ClutterContainer     *container,
1319                                           gfloat                for_width,
1320                                           gfloat               *min_height_p,
1321                                           gfloat               *nat_height_p)
1322 {
1323   ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv;
1324   ClutterGridRequest request;
1325   ClutterGridLines *lines;
1326
1327   if (min_height_p)
1328     *min_height_p = 0.0f;
1329   if (nat_height_p)
1330     *nat_height_p = 0.0f;
1331
1332   request.grid = CLUTTER_GRID_LAYOUT (self);
1333   clutter_grid_request_update_attach (&request);
1334   clutter_grid_request_count_lines (&request);
1335   lines = &request.lines[priv->orientation];
1336   lines->lines = g_newa (ClutterGridLine, lines->max - lines->min);
1337   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine));
1338
1339   clutter_grid_request_run (&request, priv->orientation, FALSE);
1340   clutter_grid_request_sum (&request, priv->orientation,
1341                             min_height_p, nat_height_p);
1342 }
1343
1344 static void
1345 allocate_child (ClutterGridRequest *request,
1346                 ClutterOrientation  orientation,
1347                 ClutterGridChild   *child,
1348                 gfloat             *position,
1349                 gfloat             *size)
1350 {
1351   ClutterGridLayoutPrivate *priv = request->grid->priv;
1352   ClutterGridLineData *linedata;
1353   ClutterGridLines *lines;
1354   ClutterGridLine *line;
1355   ClutterGridAttach *attach;
1356   gint i;
1357
1358   linedata = &priv->linedata[orientation];
1359   lines = &request->lines[orientation];
1360   attach = &child->attach[orientation];
1361
1362   *position = lines->lines[attach->pos - lines->min].position;
1363
1364   *size = (attach->span - 1) * linedata->spacing;
1365   for (i = 0; i < attach->span; i++)
1366     {
1367       line = &lines->lines[attach->pos - lines->min + i];
1368       *size += line->allocation;
1369     }
1370 }
1371
1372 #define GET_SIZE(allocation, orientation) \
1373   (orientation == CLUTTER_ORIENTATION_HORIZONTAL \
1374    ? clutter_actor_box_get_width ((allocation)) \
1375    : clutter_actor_box_get_height ((allocation)))
1376
1377 static void
1378 clutter_grid_layout_allocate (ClutterLayoutManager   *layout,
1379                               ClutterContainer       *container,
1380                               const ClutterActorBox  *allocation,
1381                               ClutterAllocationFlags  flags)
1382 {
1383   ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (layout);
1384   ClutterGridLayoutPrivate *priv = self->priv;
1385   ClutterGridRequest request;
1386   ClutterGridLines *lines;
1387   ClutterActorIter iter;
1388   ClutterActor *child;
1389   gboolean use_animations;
1390   ClutterAnimationMode mode;
1391   guint duration, delay;
1392
1393   request.grid = self;
1394
1395   clutter_grid_request_update_attach (&request);
1396   clutter_grid_request_count_lines (&request);
1397   lines = &request.lines[0];
1398   lines->lines = g_newa (ClutterGridLine, lines->max - lines->min);
1399   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine));
1400   lines = &request.lines[1];
1401   lines->lines = g_newa (ClutterGridLine, lines->max - lines->min);
1402   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine));
1403
1404   clutter_grid_request_run (&request, 1 - priv->orientation, FALSE);
1405   clutter_grid_request_allocate (&request, 1 - priv->orientation, GET_SIZE (allocation, 1 - priv->orientation));
1406   clutter_grid_request_run (&request, priv->orientation, TRUE);
1407   clutter_grid_request_allocate (&request, priv->orientation, GET_SIZE (allocation, priv->orientation));
1408
1409   clutter_grid_request_position (&request, 0);
1410   clutter_grid_request_position (&request, 1);
1411
1412   use_animations = clutter_layout_manager_get_easing_state (layout,
1413                                                             &mode,
1414                                                             &duration,
1415                                                             &delay);
1416
1417   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
1418   while (clutter_actor_iter_next (&iter, &child))
1419     {
1420       ClutterActorBox child_allocation;
1421       gfloat x, y, width, height;
1422       ClutterGridChild *grid_child;
1423
1424       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
1425         continue;
1426
1427       grid_child = GET_GRID_CHILD (self, child);
1428       allocate_child (&request, CLUTTER_ORIENTATION_HORIZONTAL, grid_child,
1429                       &x, &width);
1430       allocate_child (&request, CLUTTER_ORIENTATION_VERTICAL, grid_child,
1431                       &y, &height);
1432       x += allocation->x1;
1433       y += allocation->y1;
1434       clutter_actor_box_set_origin (&child_allocation, x, y);
1435       clutter_actor_box_set_size (&child_allocation, width, height);
1436
1437       CLUTTER_NOTE (LAYOUT, "Allocation for %s { %.2f, %.2f - %.2f x %.2f }",
1438                     _clutter_actor_get_debug_name (child),
1439                     x, y, width, height);
1440
1441       if (use_animations)
1442         {
1443           clutter_actor_save_easing_state (child);
1444           clutter_actor_set_easing_mode (child, mode);
1445           clutter_actor_set_easing_duration (child, duration);
1446           clutter_actor_set_easing_delay (child, delay);
1447         }
1448
1449       clutter_actor_allocate (child, &child_allocation, flags);
1450
1451       if (use_animations)
1452         clutter_actor_restore_easing_state (child);
1453     }
1454 }
1455
1456 static GType
1457 clutter_grid_layout_get_child_meta_type (ClutterLayoutManager *self)
1458 {
1459   return CLUTTER_TYPE_GRID_CHILD;
1460 }
1461
1462 static void
1463 clutter_grid_layout_set_property (GObject      *gobject,
1464                                   guint         prop_id,
1465                                   const GValue *value,
1466                                   GParamSpec   *pspec)
1467 {
1468   ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (gobject);
1469
1470   switch (prop_id)
1471     {
1472     case PROP_ORIENTATION:
1473       clutter_grid_layout_set_orientation (self, g_value_get_enum (value));
1474       break;
1475
1476     case PROP_ROW_SPACING:
1477       clutter_grid_layout_set_row_spacing (self, g_value_get_uint (value));
1478       break;
1479
1480     case PROP_COLUMN_SPACING:
1481       clutter_grid_layout_set_column_spacing (self, g_value_get_uint (value));
1482       break;
1483
1484     case PROP_ROW_HOMOGENEOUS:
1485       clutter_grid_layout_set_row_homogeneous (self,
1486                                                g_value_get_boolean (value));
1487       break;
1488
1489     case PROP_COLUMN_HOMOGENEOUS:
1490       clutter_grid_layout_set_column_homogeneous (self,
1491                                                   g_value_get_boolean (value));
1492       break;
1493
1494     default:
1495       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1496       break;
1497     }
1498 }
1499
1500 static void
1501 clutter_grid_layout_get_property (GObject    *gobject,
1502                                   guint       prop_id,
1503                                   GValue     *value,
1504                                   GParamSpec *pspec)
1505 {
1506   ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (gobject)->priv;
1507
1508   switch (prop_id)
1509     {
1510     case PROP_ORIENTATION:
1511       g_value_set_enum (value, priv->orientation);
1512       break;
1513
1514     case PROP_ROW_SPACING:
1515       g_value_set_uint (value, COLUMNS (priv)->spacing);
1516       break;
1517
1518     case PROP_COLUMN_SPACING:
1519       g_value_set_uint (value, ROWS (priv)->spacing);
1520       break;
1521
1522     case PROP_ROW_HOMOGENEOUS:
1523       g_value_set_boolean (value, COLUMNS (priv)->homogeneous);
1524       break;
1525
1526     case PROP_COLUMN_HOMOGENEOUS:
1527       g_value_set_boolean (value, ROWS (priv)->homogeneous);
1528       break;
1529
1530     default:
1531       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1532       break;
1533     }
1534 }
1535
1536 static void
1537 clutter_grid_layout_class_init (ClutterGridLayoutClass *klass)
1538 {
1539   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1540   ClutterLayoutManagerClass *layout_class;
1541
1542   layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
1543
1544   g_type_class_add_private (klass, sizeof (ClutterGridLayoutPrivate));
1545
1546   object_class->set_property = clutter_grid_layout_set_property;
1547   object_class->get_property = clutter_grid_layout_get_property;
1548
1549   layout_class->set_container = clutter_grid_layout_set_container;
1550   layout_class->get_preferred_width = clutter_grid_layout_get_preferred_width;
1551   layout_class->get_preferred_height = clutter_grid_layout_get_preferred_height;
1552   layout_class->allocate = clutter_grid_layout_allocate;
1553   layout_class->get_child_meta_type = clutter_grid_layout_get_child_meta_type;
1554
1555   /**
1556    * ClutterGridLayout:orientation:
1557    *
1558    * The orientation of the layout, either horizontal or vertical
1559    *
1560    * Since: 1.12
1561    */
1562   obj_props[PROP_ORIENTATION] =
1563     g_param_spec_enum ("orientation",
1564                        P_("Orientation"),
1565                        P_("The orientation of the layout"),
1566                        CLUTTER_TYPE_ORIENTATION,
1567                        CLUTTER_ORIENTATION_HORIZONTAL,
1568                        G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1569
1570   /**
1571    * ClutterGridLayout:row-spacing:
1572    *
1573    * The amount of space in pixels between two consecutive rows
1574    *
1575    * Since: 1.12
1576    */
1577   obj_props[PROP_ROW_SPACING] =
1578     g_param_spec_uint ("row-spacing",
1579                        P_("Row spacing"),
1580                        P_("The amount of space between two consecutive rows"),
1581                        0, G_MAXUINT, 0,
1582                        G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1583
1584   /**
1585    * ClutterGridLayout:column-spacing:
1586    *
1587    * The amount of space in pixels between two consecutive columns
1588    *
1589    * Since: 1.12
1590    */
1591   obj_props[PROP_COLUMN_SPACING] =
1592     g_param_spec_uint ("column-spacing",
1593                        P_("Column spacing"),
1594                        P_("The amount of space between two consecutive "
1595                           "columns"),
1596                        0, G_MAXUINT, 0,
1597                        G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1598
1599   /**
1600    * ClutterGridLayout:row-homogeneous:
1601    *
1602    * Whether all rows of the layout should have the same height
1603    *
1604    * Since: 1.12
1605    */
1606   obj_props[PROP_ROW_HOMOGENEOUS] =
1607     g_param_spec_boolean ("row-homogeneous",
1608                           P_("Row Homogeneous"),
1609                           P_("If TRUE, the rows are all the same height"),
1610                           FALSE,
1611                           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1612
1613   /**
1614    * ClutterGridLayout:column-homogeneous:
1615    *
1616    * Whether all columns of the layout should have the same width
1617    *
1618    * Since: 1.12
1619    */
1620   obj_props[PROP_COLUMN_HOMOGENEOUS] =
1621     g_param_spec_boolean ("column-homogeneous",
1622                           P_("Column Homogeneous"),
1623                           P_("If TRUE, the columns are all the same width"),
1624                           FALSE,
1625                           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
1626
1627   g_object_class_install_properties (object_class, PROP_LAST, obj_props);
1628 }
1629
1630 static void
1631 clutter_grid_layout_init (ClutterGridLayout *self)
1632 {
1633   ClutterGridLayoutPrivate *priv;
1634
1635   self->priv = priv = CLUTTER_GRID_LAYOUT_GET_PRIVATE (self);
1636
1637   priv->orientation = CLUTTER_ORIENTATION_HORIZONTAL;
1638
1639   priv->linedata[0].spacing = 0;
1640   priv->linedata[1].spacing = 0;
1641
1642   priv->linedata[0].homogeneous = FALSE;
1643   priv->linedata[1].homogeneous = FALSE;
1644 }
1645
1646 /**
1647  * clutter_grid_layout_new:
1648  *
1649  * Creates a new #ClutterGridLayout
1650  *
1651  * Return value: the new #ClutterGridLayout
1652  */
1653 ClutterLayoutManager *
1654 clutter_grid_layout_new (void)
1655 {
1656   return g_object_new (CLUTTER_TYPE_GRID_LAYOUT, NULL);
1657 }
1658
1659 /**
1660  * clutter_grid_layout_attach:
1661  * @layout: a #ClutterGridLayout
1662  * @child: the #ClutterActor to add
1663  * @left: the column number to attach the left side of @child to
1664  * @top: the row number to attach the top side of @child to
1665  * @width: the number of columns that @child will span
1666  * @height: the number of rows that @child will span
1667  *
1668  * Adds a widget to the grid.
1669  *
1670  * The position of @child is determined by @left and @top. The
1671  * number of 'cells' that @child will occupy is determined by
1672  * @width and @height.
1673  *
1674  * Since: 1.12
1675  */
1676 void
1677 clutter_grid_layout_attach (ClutterGridLayout *layout,
1678                             ClutterActor      *child,
1679                             gint               left,
1680                             gint               top,
1681                             gint               width,
1682                             gint               height)
1683 {
1684   ClutterGridLayoutPrivate *priv;
1685
1686   g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
1687
1688   priv = layout->priv;
1689
1690   if (!priv->container)
1691     return;
1692
1693   grid_attach (layout, child, left, top, width, height);
1694   clutter_actor_add_child (CLUTTER_ACTOR (priv->container), child);
1695 }
1696
1697 /**
1698  * clutter_grid_layout_attach_next_to:
1699  * @layout: a #ClutterGridLayout
1700  * @child: the actor to add
1701  * @sibling: (allow-none): the child of @layout that @child will be placed
1702  *     next to, or %NULL to place @child at the beginning or end
1703  * @side: the side of @sibling that @child is positioned next to
1704  * @width: the number of columns that @child will span
1705  * @height: the number of rows that @child will span
1706  *
1707  * Adds a actor to the grid.
1708  *
1709  * The actor is placed next to @sibling, on the side determined by
1710  * @side. When @sibling is %NULL, the actor is placed in row (for
1711  * left or right placement) or column 0 (for top or bottom placement),
1712  * at the end indicated by @side.
1713  *
1714  * Attaching widgets labeled [1], [2], [3] with @sibling == %NULL and
1715  * @side == %CLUTTER_GRID_POSITION_LEFT yields a layout of [3][2][1].
1716  *
1717  * Since: 1.12
1718  */
1719 void
1720 clutter_grid_layout_attach_next_to (ClutterGridLayout   *layout,
1721                                     ClutterActor        *child,
1722                                     ClutterActor        *sibling,
1723                                     ClutterGridPosition  side,
1724                                     gint                 width,
1725                                     gint                 height)
1726 {
1727   ClutterGridLayoutPrivate *priv;
1728
1729   g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
1730   g_return_if_fail (CLUTTER_IS_ACTOR (child));
1731   g_return_if_fail (clutter_actor_get_parent (child) == NULL);
1732   g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
1733   g_return_if_fail (width > 0);
1734   g_return_if_fail (height > 0);
1735
1736   priv = layout->priv;
1737
1738   if (!priv->container)
1739     return;
1740
1741   grid_attach_next_to (layout, child, sibling, side, width, height);
1742   clutter_actor_add_child (CLUTTER_ACTOR (priv->container), child);
1743 }
1744
1745 /**
1746  * clutter_grid_layout_set_orientation:
1747  * @layout: a #ClutterGridLayout
1748  * @orientation: the orientation of the #ClutterGridLayout
1749  *
1750  * Sets the orientation of the @layout
1751  *
1752  * Since: 1.12
1753  */
1754 void
1755 clutter_grid_layout_set_orientation (ClutterGridLayout *layout,
1756                                      ClutterOrientation orientation)
1757 {
1758   ClutterGridLayoutPrivate *priv;
1759
1760   g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
1761
1762   priv = layout->priv;
1763
1764   if (priv->orientation != orientation)
1765     {
1766       priv->orientation = orientation;
1767
1768       clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
1769       g_object_notify_by_pspec (G_OBJECT (layout), obj_props[PROP_ORIENTATION]);
1770     }
1771 }
1772
1773 /**
1774  * clutter_grid_layout_get_child_at:
1775  * @layout: a #ClutterGridLayout
1776  * @left: the left edge of the cell
1777  * @top: the top edge of the cell
1778  *
1779  * Gets the child of @layout whose area covers the grid
1780  * cell whose upper left corner is at @left, @top.
1781  *
1782  * Returns: (transfer none): the child at the given position, or %NULL
1783  *
1784  * Since: 1.12
1785  */
1786 ClutterActor *
1787 clutter_grid_layout_get_child_at (ClutterGridLayout *layout,
1788                                   gint               left,
1789                                   gint               top)
1790 {
1791   ClutterGridLayoutPrivate *priv;
1792   ClutterGridChild *grid_child;
1793   ClutterActorIter iter;
1794   ClutterActor *child;
1795
1796   g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), NULL);
1797
1798   priv = layout->priv;
1799
1800   if (!priv->container)
1801     return NULL;
1802
1803   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
1804   while (clutter_actor_iter_next (&iter, &child))
1805     {
1806       grid_child = GET_GRID_CHILD (layout, child);
1807
1808       if (CHILD_LEFT (grid_child) <= left &&
1809           CHILD_LEFT (grid_child) + CHILD_WIDTH (grid_child) > left &&
1810           CHILD_TOP (grid_child) <= top &&
1811           CHILD_TOP (grid_child) + CHILD_HEIGHT (grid_child) > top)
1812         return child;
1813     }
1814
1815   return NULL;
1816 }
1817
1818 /**
1819  * clutter_grid_layout_insert_row:
1820  * @layout: a #ClutterGridLayout
1821  * @position: the position to insert the row at
1822  *
1823  * Inserts a row at the specified position.
1824  *
1825  * Children which are attached at or below this position
1826  * are moved one row down. Children which span across this
1827  * position are grown to span the new row.
1828  *
1829  * Since: 1.12
1830  */
1831 void
1832 clutter_grid_layout_insert_row (ClutterGridLayout *layout,
1833                                 gint               position)
1834 {
1835   ClutterGridLayoutPrivate *priv;
1836   ClutterGridChild *grid_child;
1837   ClutterActorIter iter;
1838   ClutterActor *child;
1839   gint top, height;
1840
1841   g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
1842
1843   priv = layout->priv;
1844
1845   if (!priv->container)
1846     return;
1847
1848   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
1849   while (clutter_actor_iter_next (&iter, &child))
1850     {
1851       grid_child = GET_GRID_CHILD (layout, child);
1852
1853       top = CHILD_TOP (grid_child);
1854       height = CHILD_HEIGHT (grid_child);
1855
1856       if (top >= position)
1857         {
1858           CHILD_TOP (grid_child) = top + 1;
1859           g_object_notify_by_pspec (G_OBJECT (grid_child),
1860                                     child_props[PROP_CHILD_TOP_ATTACH]);
1861         }
1862       else if (top + height > position)
1863         {
1864           CHILD_HEIGHT (grid_child) = height + 1;
1865           g_object_notify_by_pspec (G_OBJECT (grid_child),
1866                                     child_props[PROP_CHILD_HEIGHT]);
1867         }
1868     }
1869   clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
1870 }
1871
1872 /**
1873  * clutter_grid_layout_insert_column:
1874  * @layout: a #ClutterGridLayout
1875  * @position: the position to insert the column at
1876  *
1877  * Inserts a column at the specified position.
1878  *
1879  * Children which are attached at or to the right of this position
1880  * are moved one column to the right. Children which span across this
1881  * position are grown to span the new column.
1882  *
1883  * Since: 1.12
1884  */
1885 void
1886 clutter_grid_layout_insert_column (ClutterGridLayout *layout,
1887                                    gint               position)
1888 {
1889   ClutterGridLayoutPrivate *priv;
1890   ClutterGridChild *grid_child;
1891   ClutterActorIter iter;
1892   ClutterActor *child;
1893   gint left, width;
1894
1895   g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
1896
1897   priv = layout->priv;
1898
1899   if (!priv->container)
1900     return;
1901
1902   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container));
1903   while (clutter_actor_iter_next (&iter, &child))
1904     {
1905       grid_child = GET_GRID_CHILD (layout, child);
1906
1907       left = CHILD_LEFT (grid_child);
1908       width = CHILD_WIDTH (grid_child);
1909
1910       if (left >= position)
1911         {
1912           CHILD_LEFT (grid_child) = left + 1;
1913           g_object_notify_by_pspec (G_OBJECT (grid_child),
1914                                     child_props[PROP_CHILD_LEFT_ATTACH]);
1915         }
1916       else if (left + width > position)
1917         {
1918           CHILD_WIDTH (grid_child) = width + 1;
1919           g_object_notify_by_pspec (G_OBJECT (grid_child),
1920                                     child_props[PROP_CHILD_WIDTH]);
1921         }
1922     }
1923   clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
1924 }
1925
1926 /**
1927  * clutter_grid_layout_insert_next_to:
1928  * @layout: a #ClutterGridLayout
1929  * @sibling: the child of @layout that the new row or column will be
1930  *     placed next to
1931  * @side: the side of @sibling that @child is positioned next to
1932  *
1933  * Inserts a row or column at the specified position.
1934  *
1935  * The new row or column is placed next to @sibling, on the side
1936  * determined by @side. If @side is %CLUTTER_GRID_POSITION_LEFT or
1937  * %CLUTTER_GRID_POSITION_BOTTOM, a row is inserted. If @side is
1938  * %CLUTTER_GRID_POSITION_LEFT of %CLUTTER_GRID_POSITION_RIGHT,
1939  * a column is inserted.
1940  *
1941  * Since: 1.12
1942  */
1943 void
1944 clutter_grid_layout_insert_next_to (ClutterGridLayout   *layout,
1945                                     ClutterActor        *sibling,
1946                                     ClutterGridPosition  side)
1947 {
1948   ClutterGridChild *grid_child;
1949
1950   g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
1951   g_return_if_fail (CLUTTER_IS_ACTOR (sibling));
1952
1953   grid_child = GET_GRID_CHILD (layout, sibling);
1954
1955   switch (side)
1956     {
1957     case CLUTTER_GRID_POSITION_LEFT:
1958       clutter_grid_layout_insert_column (layout, CHILD_LEFT (grid_child));
1959       break;
1960
1961     case CLUTTER_GRID_POSITION_RIGHT:
1962       clutter_grid_layout_insert_column (layout, CHILD_LEFT (grid_child) +
1963                                          CHILD_WIDTH (grid_child));
1964       break;
1965
1966     case CLUTTER_GRID_POSITION_TOP:
1967       clutter_grid_layout_insert_row (layout, CHILD_TOP (grid_child));
1968       break;
1969
1970     case CLUTTER_GRID_POSITION_BOTTOM:
1971       clutter_grid_layout_insert_row (layout, CHILD_TOP (grid_child) +
1972                                       CHILD_HEIGHT (grid_child));
1973       break;
1974
1975     default:
1976       g_assert_not_reached ();
1977     }
1978 }
1979
1980 /**
1981  * clutter_grid_layout_get_orientation:
1982  * @layout: a #ClutterGridLayout
1983  *
1984  * Retrieves the orientation of the @layout.
1985  *
1986  * Return value: the orientation of the layout
1987  *
1988  * Since: 1.12
1989  */
1990 ClutterOrientation
1991 clutter_grid_layout_get_orientation (ClutterGridLayout *layout)
1992 {
1993   g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout),
1994                         CLUTTER_ORIENTATION_HORIZONTAL);
1995
1996   return layout->priv->orientation;
1997 }
1998
1999 /**
2000  * clutter_grid_layout_set_row_spacing:
2001  * @layout: a #ClutterGridLayout
2002  * @spacing: the spacing between rows of the layout, in pixels
2003  *
2004  * Sets the spacing between rows of @layout
2005  *
2006  * Since: 1.12
2007  */
2008 void
2009 clutter_grid_layout_set_row_spacing (ClutterGridLayout *layout,
2010                                      guint              spacing)
2011 {
2012   ClutterGridLayoutPrivate *priv;
2013
2014   g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
2015
2016   priv = layout->priv;
2017
2018   if (COLUMNS (priv)->spacing != spacing)
2019     {
2020       COLUMNS (priv)->spacing = spacing;
2021
2022       clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
2023       g_object_notify_by_pspec (G_OBJECT (layout),
2024                                 obj_props[PROP_ROW_SPACING]);
2025     }
2026 }
2027
2028 /**
2029  * clutter_grid_layout_get_row_spacing:
2030  * @layout: a #ClutterGridLayout
2031  *
2032  * Retrieves the spacing set using clutter_grid_layout_set_row_spacing()
2033  *
2034  * Return value: the spacing between rows of @layout
2035  *
2036  * Since: 1.12
2037  */
2038 guint
2039 clutter_grid_layout_get_row_spacing (ClutterGridLayout *layout)
2040 {
2041   ClutterGridLayoutPrivate *priv;
2042
2043   g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), 0);
2044
2045   priv = layout->priv;
2046
2047   return COLUMNS (priv)->spacing;
2048 }
2049
2050 /**
2051  * clutter_grid_layout_set_column_spacing:
2052  * @layout: a #ClutterGridLayout
2053  * @spacing: the spacing between columns of the layout, in pixels
2054  *
2055  * Sets the spacing between columns of @layout
2056  *
2057  * Since: 1.12
2058  */
2059 void
2060 clutter_grid_layout_set_column_spacing (ClutterGridLayout *layout,
2061                                         guint spacing)
2062 {
2063   ClutterGridLayoutPrivate *priv;
2064
2065   g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
2066
2067   priv = layout->priv;
2068
2069   if (ROWS (priv)->spacing != spacing)
2070     {
2071       ROWS (priv)->spacing = spacing;
2072
2073       clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
2074       g_object_notify_by_pspec (G_OBJECT (layout),
2075                                 obj_props[PROP_COLUMN_SPACING]);
2076     }
2077 }
2078
2079 /**
2080  * clutter_grid_layout_get_column_spacing:
2081  * @layout: a #ClutterGridLayout
2082  *
2083  * Retrieves the spacing set using clutter_grid_layout_set_column_spacing()
2084  *
2085  * Return value: the spacing between coluns of @layout
2086  *
2087  * Since: 1.12
2088  */
2089 guint
2090 clutter_grid_layout_get_column_spacing (ClutterGridLayout *layout)
2091 {
2092   ClutterGridLayoutPrivate *priv;
2093
2094   g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), 0);
2095
2096   priv = layout->priv;
2097
2098   return ROWS (priv)->spacing;
2099 }
2100
2101 /**
2102  * clutter_grid_layout_set_column_homogeneous:
2103  * @layout: a #ClutterGridLayout
2104  * @homogeneous: %TRUE to make columns homogeneous
2105  *
2106  * Sets whether all columns of @layout will have the same width.
2107  *
2108  * Since: 1.12
2109  */
2110 void
2111 clutter_grid_layout_set_column_homogeneous (ClutterGridLayout *layout,
2112                                             gboolean           homogeneous)
2113 {
2114   ClutterGridLayoutPrivate *priv;
2115
2116   g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
2117
2118   priv = layout->priv;
2119
2120   if (ROWS (priv)->homogeneous != homogeneous)
2121     {
2122       ROWS (priv)->homogeneous = homogeneous;
2123
2124       clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
2125       g_object_notify_by_pspec (G_OBJECT (layout),
2126                                 obj_props[PROP_COLUMN_HOMOGENEOUS]);
2127     }
2128 }
2129
2130 /**
2131  * clutter_grid_layout_get_column_homogeneous:
2132  * @layout: a #ClutterGridLayout
2133  *
2134  * Returns whether all columns of @layout have the same width.
2135  *
2136  * Returns: whether all columns of @layout have the same width.
2137  */
2138 gboolean
2139 clutter_grid_layout_get_column_homogeneous (ClutterGridLayout *layout)
2140 {
2141   ClutterGridLayoutPrivate *priv;
2142
2143   g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), FALSE);
2144
2145   priv = layout->priv;
2146
2147   return ROWS (priv)->homogeneous;
2148 }
2149
2150 /**
2151  * clutter_grid_layout_set_row_homogeneous:
2152  * @layout: a #ClutterGridLayout
2153  * @homogeneous: %TRUE to make rows homogeneous
2154  *
2155  * Sets whether all rows of @layout will have the same height.
2156  *
2157  * Since: 1.12
2158  */
2159 void
2160 clutter_grid_layout_set_row_homogeneous (ClutterGridLayout *layout,
2161                                          gboolean           homogeneous)
2162 {
2163   ClutterGridLayoutPrivate *priv;
2164
2165   g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout));
2166
2167   priv = layout->priv;
2168
2169   if (COLUMNS (priv)->homogeneous != homogeneous)
2170     {
2171       COLUMNS (priv)->homogeneous = homogeneous;
2172
2173       clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout));
2174       g_object_notify_by_pspec (G_OBJECT (layout),
2175                                 obj_props[PROP_ROW_HOMOGENEOUS]);
2176     }
2177 }
2178
2179 /**
2180  * clutter_grid_layout_get_row_homogeneous:
2181  * @layout: a #ClutterGridLayout
2182  *
2183  * Returns whether all rows of @layout have the same height.
2184  *
2185  * Returns: whether all rows of @layout have the same height.
2186  *
2187  * Since: 1.12
2188  */
2189 gboolean
2190 clutter_grid_layout_get_row_homogeneous (ClutterGridLayout *layout)
2191 {
2192   ClutterGridLayoutPrivate *priv;
2193
2194   g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), FALSE);
2195
2196   priv = layout->priv;
2197
2198   return COLUMNS (priv)->homogeneous;
2199 }