]> Pileus Git - ~andy/gtk/blob - gtk/gtkgrid.c
GtkGrid: Fix a size allocation problem
[~andy/gtk] / gtk / gtkgrid.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Red Hat, Inc.
3  * Author: Matthias Clasen
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include <string.h>
24
25 #include "gtkgrid.h"
26
27 #include "gtkorientableprivate.h"
28 #include "gtksizerequest.h"
29 #include "gtkprivate.h"
30 #include "gtkintl.h"
31
32
33 /**
34  * SECTION:gtkgrid
35  * @Short_description: Pack widgets in a rows and columns
36  * @Title: GtkGrid
37  * @See_also: #GtkTable, #GtkHBox, #GtkVBox
38  *
39  * GtkGrid is a container which arranges its child widgets in
40  * rows and columns. It is a very similar to #GtkTable and #GtkBox,
41  * but it consistently uses #GtkWidget's #GtkWidget:margin and #GtkWidget:expand
42  * properties instead of custom child properties, and it fully supports
43  * <link linkend="geometry-management">height-for-width geometry management</link>.
44  *
45  * Children are added using gtk_grid_attach(). They can span multiple
46  * rows or columns. It is also possible to add a child next to an
47  * existing child, using gtk_grid_attach_next_to().
48  *
49  * GtkGrid can be used like a #GtkBox by just using gtk_container_add(),
50  * which will place children next to each other in the direction determined
51  * by the #GtkOrientable:orientation property.
52  */
53
54 typedef struct _GtkGridChild GtkGridChild;
55 typedef struct _GtkGridChildAttach GtkGridChildAttach;
56 typedef struct _GtkGridLine GtkGridLine;
57 typedef struct _GtkGridLines GtkGridLines;
58 typedef struct _GtkGridLineData GtkGridLineData;
59 typedef struct _GtkGridRequest GtkGridRequest;
60
61 struct _GtkGridChildAttach
62 {
63   gint pos;
64   gint span;
65 };
66
67 struct _GtkGridChild
68 {
69   GtkWidget *widget;
70   GtkGridChildAttach attach[2];
71 };
72
73 #define CHILD_LEFT(child)    ((child)->attach[GTK_ORIENTATION_HORIZONTAL].pos)
74 #define CHILD_WIDTH(child)   ((child)->attach[GTK_ORIENTATION_HORIZONTAL].span)
75 #define CHILD_TOP(child)     ((child)->attach[GTK_ORIENTATION_VERTICAL].pos)
76 #define CHILD_HEIGHT(child)  ((child)->attach[GTK_ORIENTATION_VERTICAL].span)
77
78 /* A GtkGridLineData struct contains row/column specific parts
79  * of the grid.
80  */
81 struct _GtkGridLineData
82 {
83   gint16 spacing;
84   guint homogeneous : 1;
85 };
86
87 struct _GtkGridPrivate
88 {
89   GList *children;
90
91   GtkOrientation orientation;
92
93   GtkGridLineData linedata[2];
94 };
95
96 #define ROWS(priv)    (&(priv)->linedata[GTK_ORIENTATION_HORIZONTAL])
97 #define COLUMNS(priv) (&(priv)->linedata[GTK_ORIENTATION_VERTICAL])
98
99 /* A GtkGridLine struct represents a single row or column
100  * during size requests
101  */
102 struct _GtkGridLine
103 {
104   gint minimum;
105   gint natural;
106   gint position;
107   gint allocation;
108
109   guint need_expand : 1;
110   guint expand      : 1;
111   guint empty       : 1;
112 };
113
114 struct _GtkGridLines
115 {
116   GtkGridLine *lines;
117   gint min, max;
118 };
119
120 struct _GtkGridRequest
121 {
122   GtkGrid *grid;
123   GtkGridLines lines[2];
124 };
125
126
127 enum
128 {
129   PROP_0,
130   PROP_ORIENTATION,
131   PROP_ROW_SPACING,
132   PROP_COLUMN_SPACING,
133   PROP_ROW_HOMOGENEOUS,
134   PROP_COLUMN_HOMOGENEOUS
135 };
136
137 enum
138 {
139   CHILD_PROP_0,
140   CHILD_PROP_LEFT_ATTACH,
141   CHILD_PROP_TOP_ATTACH,
142   CHILD_PROP_WIDTH,
143   CHILD_PROP_HEIGHT
144 };
145
146 G_DEFINE_TYPE_WITH_CODE (GtkGrid, gtk_grid, GTK_TYPE_CONTAINER,
147                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
148
149
150 static void
151 gtk_grid_get_property (GObject    *object,
152                        guint       prop_id,
153                        GValue     *value,
154                        GParamSpec *pspec)
155 {
156   GtkGrid *grid = GTK_GRID (object);
157   GtkGridPrivate *priv = grid->priv;
158
159   switch (prop_id)
160     {
161     case PROP_ORIENTATION:
162       g_value_set_enum (value, priv->orientation);
163       break;
164
165     case PROP_ROW_SPACING:
166       g_value_set_int (value, ROWS (priv)->spacing);
167       break;
168
169     case PROP_COLUMN_SPACING:
170       g_value_set_int (value, COLUMNS (priv)->spacing);
171       break;
172
173     case PROP_ROW_HOMOGENEOUS:
174       g_value_set_boolean (value, COLUMNS (priv)->homogeneous);
175       break;
176
177     case PROP_COLUMN_HOMOGENEOUS:
178       g_value_set_boolean (value, ROWS (priv)->homogeneous);
179       break;
180
181     default:
182       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
183       break;
184     }
185 }
186
187 static void
188 gtk_grid_set_orientation (GtkGrid        *grid,
189                           GtkOrientation  orientation)
190 {
191   GtkGridPrivate *priv = grid->priv;
192
193   if (priv->orientation != orientation)
194     {
195       priv->orientation = orientation;
196       _gtk_orientable_set_style_classes (GTK_ORIENTABLE (grid));
197
198       g_object_notify (G_OBJECT (grid), "orientation");
199     }
200 }
201
202 static void
203 gtk_grid_set_property (GObject      *object,
204                        guint         prop_id,
205                        const GValue *value,
206                        GParamSpec   *pspec)
207 {
208   GtkGrid *grid = GTK_GRID (object);
209
210   switch (prop_id)
211     {
212     case PROP_ORIENTATION:
213       gtk_grid_set_orientation (grid, g_value_get_enum (value));
214       break;
215
216     case PROP_ROW_SPACING:
217       gtk_grid_set_row_spacing (grid, g_value_get_int (value));
218       break;
219
220     case PROP_COLUMN_SPACING:
221       gtk_grid_set_column_spacing (grid, g_value_get_int (value));
222       break;
223
224     case PROP_ROW_HOMOGENEOUS:
225       gtk_grid_set_row_homogeneous (grid, g_value_get_boolean (value));
226       break;
227
228     case PROP_COLUMN_HOMOGENEOUS:
229       gtk_grid_set_column_homogeneous (grid, g_value_get_boolean (value));
230       break;
231
232     default:
233       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
234       break;
235     }
236 }
237
238 static GtkGridChild *
239 find_grid_child (GtkGrid   *grid,
240                  GtkWidget *widget)
241 {
242   GtkGridPrivate *priv = grid->priv;
243   GtkGridChild *child;
244   GList *list;
245
246   for (list = priv->children; list; list = list->next)
247     {
248       child = list->data;
249
250       if (child->widget == widget)
251         return child;
252     }
253
254   return NULL;
255 }
256
257 static void
258 gtk_grid_get_child_property (GtkContainer *container,
259                              GtkWidget    *child,
260                              guint         property_id,
261                              GValue       *value,
262                              GParamSpec   *pspec)
263 {
264   GtkGrid *grid = GTK_GRID (container);
265   GtkGridChild *grid_child;
266
267   grid_child = find_grid_child (grid, child);
268
269   if (grid_child == NULL)
270     {
271       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
272       return;
273     }
274
275   switch (property_id)
276     {
277     case CHILD_PROP_LEFT_ATTACH:
278       g_value_set_int (value, CHILD_LEFT (grid_child));
279       break;
280
281     case CHILD_PROP_TOP_ATTACH:
282       g_value_set_int (value, CHILD_TOP (grid_child));
283       break;
284
285     case CHILD_PROP_WIDTH:
286       g_value_set_int (value, CHILD_WIDTH (grid_child));
287       break;
288
289     case CHILD_PROP_HEIGHT:
290       g_value_set_int (value, CHILD_HEIGHT (grid_child));
291       break;
292
293     default:
294       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
295       break;
296     }
297 }
298
299 static void
300 gtk_grid_set_child_property (GtkContainer *container,
301                              GtkWidget    *child,
302                              guint         property_id,
303                              const GValue *value,
304                              GParamSpec   *pspec)
305 {
306   GtkGrid *grid = GTK_GRID (container);
307   GtkGridChild *grid_child;
308
309   grid_child = find_grid_child (grid, child);
310
311   if (grid_child == NULL)
312     {
313       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
314       return;
315     }
316
317   switch (property_id)
318     {
319     case CHILD_PROP_LEFT_ATTACH:
320       CHILD_LEFT (grid_child) = g_value_get_int (value);
321       break;
322
323     case CHILD_PROP_TOP_ATTACH:
324       CHILD_TOP (grid_child) = g_value_get_int (value);
325       break;
326
327    case CHILD_PROP_WIDTH:
328       CHILD_WIDTH (grid_child) = g_value_get_int (value);
329       break;
330
331     case CHILD_PROP_HEIGHT:
332       CHILD_HEIGHT (grid_child) = g_value_get_int (value);
333       break;
334
335     default:
336       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
337       break;
338     }
339
340   if (gtk_widget_get_visible (child) &&
341       gtk_widget_get_visible (GTK_WIDGET (grid)))
342     gtk_widget_queue_resize (child);
343 }
344
345 static void
346 gtk_grid_init (GtkGrid *grid)
347 {
348   GtkGridPrivate *priv;
349
350   grid->priv = G_TYPE_INSTANCE_GET_PRIVATE (grid, GTK_TYPE_GRID, GtkGridPrivate);
351   priv = grid->priv;
352
353   gtk_widget_set_has_window (GTK_WIDGET (grid), FALSE);
354   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (grid), FALSE);
355
356   priv->children = NULL;
357   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
358
359   priv->linedata[0].spacing = 0;
360   priv->linedata[1].spacing = 0;
361
362   priv->linedata[0].homogeneous = FALSE;
363   priv->linedata[1].homogeneous = FALSE;
364 }
365
366 static void grid_attach (GtkGrid   *grid,
367                          GtkWidget *child,
368                          gint       left,
369                          gint       top,
370                          gint       width,
371                          gint       height);
372
373 static void
374 gtk_grid_add (GtkContainer *container,
375               GtkWidget    *child)
376 {
377   GtkGrid *grid = GTK_GRID (container);
378   GtkGridPrivate *priv = grid->priv;
379   GtkGridChild *grid_child;
380   GtkGridChildAttach *attach;
381   GtkGridChildAttach *opposite;
382   GList *list;
383   gint pos;
384
385   pos = 0;
386   for (list = priv->children; list; list = list->next)
387     {
388       grid_child = list->data;
389
390       attach = &grid_child->attach[priv->orientation];
391       opposite = &grid_child->attach[1 - priv->orientation];
392
393       if (opposite->pos <= 0 && opposite->pos + opposite->span > 0)
394         pos = MAX (pos, attach->pos + attach->span);
395      }
396
397   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
398     grid_attach (grid, child, pos, 0, 1, 1);
399   else
400     grid_attach (grid, child, 0, pos, 1, 1);
401 }
402
403 static void
404 gtk_grid_remove (GtkContainer *container,
405                  GtkWidget    *child)
406 {
407   GtkGrid *grid = GTK_GRID (container);
408   GtkGridPrivate *priv = grid->priv;
409   GtkGridChild *grid_child;
410   GList *list;
411
412   for (list = priv->children; list; list = list->next)
413     {
414       grid_child = list->data;
415
416       if (grid_child->widget == child)
417         {
418           gboolean was_visible = gtk_widget_get_visible (child);
419
420           gtk_widget_unparent (child);
421
422           priv->children = g_list_remove (priv->children, grid_child);
423
424           g_slice_free (GtkGridChild, grid_child);
425
426           if (was_visible && gtk_widget_get_visible (GTK_WIDGET (grid)))
427             gtk_widget_queue_resize (GTK_WIDGET (grid));
428
429           break;
430         }
431     }
432 }
433
434 static void
435 gtk_grid_forall (GtkContainer *container,
436                  gboolean      include_internals,
437                  GtkCallback   callback,
438                  gpointer      callback_data)
439 {
440   GtkGrid *grid = GTK_GRID (container);
441   GtkGridPrivate *priv = grid->priv;
442   GtkGridChild *child;
443   GList *list;
444
445   list = priv->children;
446   while (list)
447     {
448       child = list->data;
449       list  = list->next;
450
451       (* callback) (child->widget, callback_data);
452     }
453 }
454
455 static GType
456 gtk_grid_child_type (GtkContainer *container)
457 {
458   return GTK_TYPE_WIDGET;
459 }
460
461 /* Calculates the min and max numbers for both orientations.
462  */
463 static void
464 gtk_grid_request_count_lines (GtkGridRequest *request)
465 {
466   GtkGridPrivate *priv = request->grid->priv;
467   GtkGridChild *child;
468   GtkGridChildAttach *attach;
469   GList *list;
470   gint min[2];
471   gint max[2];
472
473   min[0] = min[1] = G_MAXINT;
474   max[0] = max[1] = G_MININT;
475
476   for (list = priv->children; list; list = list->next)
477     {
478       child = list->data;
479       attach = child->attach;
480
481       min[0] = MIN (min[0], attach[0].pos);
482       max[0] = MAX (max[0], attach[0].pos + attach[0].span);
483       min[1] = MIN (min[1], attach[1].pos);
484       max[1] = MAX (max[1], attach[1].pos + attach[1].span);
485     }
486
487   request->lines[0].min = min[0];
488   request->lines[0].max = max[0];
489   request->lines[1].min = min[1];
490   request->lines[1].max = max[1];
491 }
492
493 /* Sets line sizes to 0 and marks lines as expand
494  * if they have a non-spanning expanding child.
495  */
496 static void
497 gtk_grid_request_init (GtkGridRequest *request,
498                        GtkOrientation  orientation)
499 {
500   GtkGridPrivate *priv = request->grid->priv;
501   GtkGridChild *child;
502   GtkGridChildAttach *attach;
503   GtkGridLines *lines;
504   GList *list;
505   gint i;
506
507   lines = &request->lines[orientation];
508
509   for (i = 0; i < lines->max - lines->min; i++)
510     {
511       lines->lines[i].minimum = 0;
512       lines->lines[i].natural = 0;
513       lines->lines[i].expand = FALSE;
514     }
515
516   for (list = priv->children; list; list = list->next)
517     {
518       child = list->data;
519
520       attach = &child->attach[orientation];
521       if (attach->span == 1 && gtk_widget_compute_expand (child->widget, orientation))
522         lines->lines[attach->pos - lines->min].expand = TRUE;
523     }
524 }
525
526 /* Sums allocations for lines spanned by child and their spacing.
527  */
528 static gint
529 compute_allocation_for_child (GtkGridRequest *request,
530                               GtkGridChild   *child,
531                               GtkOrientation  orientation)
532 {
533   GtkGridPrivate *priv = request->grid->priv;
534   GtkGridLineData *linedata;
535   GtkGridLines *lines;
536   GtkGridLine *line;
537   GtkGridChildAttach *attach;
538   gint size;
539   gint i;
540
541   linedata = &priv->linedata[orientation];
542   lines = &request->lines[orientation];
543   attach = &child->attach[orientation];
544
545   size = (attach->span - 1) * linedata->spacing;
546   for (i = 0; i < attach->span; i++)
547     {
548       line = &lines->lines[attach->pos - lines->min + i];
549       size += line->allocation;
550     }
551
552   return size;
553 }
554
555 static void
556 compute_request_for_child (GtkGridRequest *request,
557                            GtkGridChild   *child,
558                            GtkOrientation  orientation,
559                            gboolean        contextual,
560                            gint           *minimum,
561                            gint           *natural)
562 {
563   if (contextual)
564     {
565       gint size;
566
567       size = compute_allocation_for_child (request, child, 1 - orientation);
568       if (orientation == GTK_ORIENTATION_HORIZONTAL)
569         gtk_widget_get_preferred_width_for_height (child->widget,
570                                                    size,
571                                                    minimum, natural);
572       else
573         gtk_widget_get_preferred_height_for_width (child->widget,
574                                                    size,
575                                                    minimum, natural);
576     }
577   else
578     {
579       if (orientation == GTK_ORIENTATION_HORIZONTAL)
580         gtk_widget_get_preferred_width (child->widget, minimum, natural);
581       else
582         gtk_widget_get_preferred_height (child->widget, minimum, natural);
583     }
584 }
585
586 /* Sets requisition to max. of non-spanning children.
587  * If contextual is TRUE, requires allocations of
588  * lines in the opposite orientation to be set.
589  */
590 static void
591 gtk_grid_request_non_spanning (GtkGridRequest *request,
592                                GtkOrientation  orientation,
593                                gboolean        contextual)
594 {
595   GtkGridPrivate *priv = request->grid->priv;
596   GtkGridChild *child;
597   GtkGridChildAttach *attach;
598   GtkGridLines *lines;
599   GtkGridLine *line;
600   GList *list;
601   gint minimum;
602   gint natural;
603
604   lines = &request->lines[orientation];
605
606   for (list = priv->children; list; list = list->next)
607     {
608       child = list->data;
609
610       if (!gtk_widget_get_visible (child->widget))
611         continue;
612
613       attach = &child->attach[orientation];
614       if (attach->span != 1)
615         continue;
616
617       compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
618
619       line = &lines->lines[attach->pos - lines->min];
620       line->minimum = MAX (line->minimum, minimum);
621       line->natural = MAX (line->natural, natural);
622     }
623 }
624
625 /* Enforce homogeneous sizes.
626  */
627 static void
628 gtk_grid_request_homogeneous (GtkGridRequest *request,
629                               GtkOrientation  orientation)
630 {
631   GtkGridPrivate *priv = request->grid->priv;
632   GtkGridLineData *linedata;
633   GtkGridLines *lines;
634   gint minimum, natural;
635   gint i;
636
637   linedata = &priv->linedata[orientation];
638   lines = &request->lines[orientation];
639
640   if (!linedata->homogeneous)
641     return;
642
643   minimum = 0;
644   natural = 0;
645
646   for (i = 0; i < lines->max - lines->min; i++)
647     {
648       minimum = MAX (minimum, lines->lines[i].minimum);
649       natural = MAX (natural, lines->lines[i].natural);
650     }
651
652   for (i = 0; i < lines->max - lines->min; i++)
653     {
654       lines->lines[i].minimum = minimum;
655       lines->lines[i].natural = natural;
656     }
657 }
658
659 /* Deals with spanning children.
660  * Requires expand fields of lines to be set for
661  * non-spanning children.
662  */
663 static void
664 gtk_grid_request_spanning (GtkGridRequest *request,
665                            GtkOrientation  orientation,
666                            gboolean        contextual)
667 {
668   GtkGridPrivate *priv = request->grid->priv;
669   GList *list;
670   GtkGridChild *child;
671   GtkGridChildAttach *attach;
672   GtkGridLineData *linedata;
673   GtkGridLines *lines;
674   GtkGridLine *line;
675   gint minimum;
676   gint natural;
677   gint span_minimum;
678   gint span_natural;
679   gint span_expand;
680   gboolean force_expand;
681   gint extra;
682   gint expand;
683   gint line_extra;
684   gint i;
685
686   linedata = &priv->linedata[orientation];
687   lines = &request->lines[orientation];
688
689   for (list = priv->children; list; list = list->next)
690     {
691       child = list->data;
692
693       if (!gtk_widget_get_visible (child->widget))
694         continue;
695
696       attach = &child->attach[orientation];
697       if (attach->span == 1)
698         continue;
699
700       compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
701
702       span_minimum = (attach->span - 1) * linedata->spacing;
703       span_natural = (attach->span - 1) * linedata->spacing;
704       span_expand = 0;
705       force_expand = FALSE;
706       for (i = 0; i < attach->span; i++)
707         {
708           line = &lines->lines[attach->pos - lines->min + i];
709           span_minimum += line->minimum;
710           span_natural += line->natural;
711           if (line->expand)
712             span_expand += 1;
713         }
714       if (span_expand == 0)
715         {
716           span_expand = attach->span;
717           force_expand = TRUE;
718         }
719
720       /* If we need to request more space for this child to fill
721        * its requisition, then divide up the needed space amongst the
722        * lines it spans, favoring expandable lines if any.
723        */
724       if (span_minimum < minimum)
725         {
726           extra = minimum - span_minimum;
727           expand = span_expand;
728           for (i = 0; i < attach->span; i++)
729             {
730               line = &lines->lines[attach->pos - lines->min + i];
731               if (force_expand || line->expand)
732                 {
733                   line_extra = extra / expand;
734                   line->minimum += line_extra;
735                   extra -= line_extra;
736                   expand -= 1;
737                 }
738             }
739         }
740
741       if (span_natural < natural)
742         {
743           extra = natural - span_natural;
744           expand = span_expand;
745           for (i = 0; i < attach->span; i++)
746             {
747               line = &lines->lines[attach->pos - lines->min + i];
748               if (force_expand || line->expand)
749                 {
750                   line_extra = extra / expand;
751                   line->natural += line_extra;
752                   extra -= line_extra;
753                   expand -= 1;
754                 }
755             }
756         }
757     }
758 }
759
760 /* Marks empty and expanding lines and counts them.
761  */
762 static void
763 gtk_grid_request_compute_expand (GtkGridRequest *request,
764                                  GtkOrientation  orientation,
765                                  gint           *nonempty_lines,
766                                  gint           *expand_lines)
767 {
768   GtkGridPrivate *priv = request->grid->priv;
769   GtkGridChild *child;
770   GtkGridChildAttach *attach;
771   GList *list;
772   gint i;
773   GtkGridLines *lines;
774   GtkGridLine *line;
775   gboolean has_expand;
776   gint expand;
777   gint empty;
778
779   lines = &request->lines[orientation];
780
781   for (i = 0; i < lines->max - lines->min; i++)
782     {
783       lines->lines[i].need_expand = FALSE;
784       lines->lines[i].expand = FALSE;
785       lines->lines[i].empty = TRUE;
786     }
787
788   for (list = priv->children; list; list = list->next)
789     {
790       child = list->data;
791
792       if (!gtk_widget_get_visible (child->widget))
793         continue;
794
795       attach = &child->attach[orientation];
796       if (attach->span != 1)
797         continue;
798
799       line = &lines->lines[attach->pos - lines->min];
800       line->empty = FALSE;
801       if (gtk_widget_compute_expand (child->widget, orientation))
802         line->expand = TRUE;
803     }
804
805   for (list = priv->children; list; list = list->next)
806     {
807       child = list->data;
808
809       if (!gtk_widget_get_visible (child->widget))
810         continue;
811
812       attach = &child->attach[orientation];
813       if (attach->span == 1)
814         continue;
815
816       has_expand = FALSE;
817       for (i = 0; i < attach->span; i++)
818         {
819           line = &lines->lines[attach->pos - lines->min + i];
820           line->empty = FALSE;
821           if (line->expand)
822             has_expand = TRUE;
823         }
824
825       if (!has_expand && gtk_widget_compute_expand (child->widget, orientation))
826         {
827           for (i = 0; i < attach->span; i++)
828             {
829               line = &lines->lines[attach->pos - lines->min + i];
830               line->need_expand = TRUE;
831             }
832         }
833     }
834
835   empty = 0;
836   expand = 0;
837   for (i = 0; i < lines->max - lines->min; i++)
838     {
839       line = &lines->lines[i];
840
841       if (line->need_expand)
842         line->expand = TRUE;
843
844       if (line->empty)
845         empty += 1;
846
847       if (line->expand)
848         expand += 1;
849     }
850
851   if (nonempty_lines)
852     *nonempty_lines = lines->max - lines->min - empty;
853
854   if (expand_lines)
855     *expand_lines = expand;
856 }
857
858 /* Sums the minimum and natural fields of lines and their spacing.
859  */
860 static void
861 gtk_grid_request_sum (GtkGridRequest *request,
862                       GtkOrientation  orientation,
863                       gint           *minimum,
864                       gint           *natural)
865 {
866   GtkGridPrivate *priv = request->grid->priv;
867   GtkGridLineData *linedata;
868   GtkGridLines *lines;
869   gint i;
870   gint min, nat;
871   gint nonempty;
872
873   gtk_grid_request_compute_expand (request, orientation, &nonempty, NULL);
874
875   linedata = &priv->linedata[orientation];
876   lines = &request->lines[orientation];
877
878   min = (nonempty - 1) * linedata->spacing;
879   nat = (nonempty - 1) * linedata->spacing;
880
881   for (i = 0; i < lines->max - lines->min; i++)
882     {
883       min += lines->lines[i].minimum;
884       nat += lines->lines[i].natural;
885     }
886
887   if (minimum)
888     *minimum = min;
889
890   if (natural)
891     *natural = nat;
892 }
893
894 /* Computes minimum and natural fields of lines.
895  * When contextual is TRUE, requires allocation of
896  * lines in the opposite orientation to be set.
897  */
898 static void
899 gtk_grid_request_run (GtkGridRequest *request,
900                       GtkOrientation  orientation,
901                       gboolean        contextual)
902 {
903   gtk_grid_request_init (request, orientation);
904   gtk_grid_request_non_spanning (request, orientation, contextual);
905   gtk_grid_request_homogeneous (request, orientation);
906   gtk_grid_request_spanning (request, orientation, contextual);
907   gtk_grid_request_homogeneous (request, orientation);
908 }
909
910 /* Requires that the minimum and natural fields of lines
911  * have been set, computes the allocation field of lines
912  * by distributing total_size among lines.
913  */
914 static void
915 gtk_grid_request_allocate (GtkGridRequest *request,
916                            GtkOrientation  orientation,
917                            gint            total_size)
918 {
919   GtkGridPrivate *priv = request->grid->priv;
920   GtkGridLineData *linedata;
921   GtkGridLines *lines;
922   GtkGridLine *line;
923   gint nonempty;
924   gint expand;
925   gint i, j;
926   GtkRequestedSize *sizes;
927   gint extra;
928   gint rest;
929   gint size;
930
931   gtk_grid_request_compute_expand (request, orientation, &nonempty, &expand);
932
933   linedata = &priv->linedata[orientation];
934   lines = &request->lines[orientation];
935
936   size = total_size - (nonempty - 1) * linedata->spacing;
937
938   if (linedata->homogeneous)
939     {
940       extra = size / nonempty;
941       rest = size % nonempty;
942
943       for (i = 0; i < lines->max - lines->min; i++)
944         {
945           line = &lines->lines[i];
946           if (line->empty)
947             continue;
948
949           line->allocation = extra;
950           if (rest > 0)
951             {
952               line->allocation += 1;
953               rest -= 1;
954             }
955         }
956     }
957   else
958     {
959       sizes = g_newa (GtkRequestedSize, nonempty);
960
961       j = 0;
962       for (i = 0; i < lines->max - lines->min; i++)
963         {
964           line = &lines->lines[i];
965           if (line->empty)
966             continue;
967
968           size -= line->minimum;
969
970           sizes[j].minimum_size = line->minimum;
971           sizes[j].natural_size = line->natural;
972           sizes[j].data = line;
973           j++;
974         }
975
976       size = gtk_distribute_natural_allocation (MAX (0, size), nonempty, sizes);
977
978       if (expand > 0)
979         {
980           extra = size / expand;
981           rest = size % expand;
982         }
983       else
984         {
985           extra = 0;
986           rest = 0;
987         }
988
989       j = 0;
990       for (i = 0; i < lines->max - lines->min; i++)
991         {
992           line = &lines->lines[i];
993           if (line->empty)
994             continue;
995
996           g_assert (line == sizes[j].data);
997
998           line->allocation = sizes[j].minimum_size;
999           if (line->expand)
1000             {
1001               line->allocation += extra;
1002               if (rest > 0)
1003                 {
1004                   line->allocation += 1;
1005                   rest -= 1;
1006                 }
1007             }
1008
1009           j++;
1010         }
1011     }
1012 }
1013
1014 /* Computes the position fields from allocation and spacing.
1015  */
1016 static void
1017 gtk_grid_request_position (GtkGridRequest *request,
1018                            GtkOrientation  orientation)
1019 {
1020   GtkGridPrivate *priv = request->grid->priv;
1021   GtkGridLineData *linedata;
1022   GtkGridLines *lines;
1023   GtkGridLine *line;
1024   gint position;
1025   gint i;
1026
1027   linedata = &priv->linedata[orientation];
1028   lines = &request->lines[orientation];
1029
1030   position = 0;
1031   for (i = 0; i < lines->max - lines->min; i++)
1032     {
1033       line = &lines->lines[i];
1034       if (!line->empty)
1035         {
1036           line->position = position;
1037           position += line->allocation + linedata->spacing;
1038         }
1039     }
1040 }
1041
1042 static void
1043 gtk_grid_get_size (GtkGrid        *grid,
1044                    GtkOrientation  orientation,
1045                    gint           *minimum,
1046                    gint           *natural)
1047 {
1048   GtkGridRequest request;
1049   GtkGridLines *lines;
1050
1051   if (minimum)
1052     *minimum = 0;
1053
1054   if (natural)
1055     *natural = 0;
1056
1057   if (grid->priv->children == NULL)
1058     return;
1059
1060   request.grid = grid;
1061   gtk_grid_request_count_lines (&request);
1062   lines = &request.lines[orientation];
1063   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1064   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1065
1066   gtk_grid_request_run (&request, orientation, FALSE);
1067   gtk_grid_request_sum (&request, orientation, minimum, natural);
1068 }
1069
1070 static void
1071 gtk_grid_get_size_for_size (GtkGrid        *grid,
1072                             GtkOrientation  orientation,
1073                             gint            size,
1074                             gint           *minimum,
1075                             gint           *natural)
1076 {
1077   GtkGridRequest request;
1078   GtkGridLines *lines;
1079   gint min_size;
1080
1081   if (minimum)
1082     *minimum = 0;
1083
1084   if (natural)
1085     *natural = 0;
1086
1087   if (grid->priv->children == NULL)
1088     return;
1089
1090   request.grid = grid;
1091   gtk_grid_request_count_lines (&request);
1092   lines = &request.lines[0];
1093   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1094   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1095   lines = &request.lines[1];
1096   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1097   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1098
1099   gtk_grid_request_run (&request, 1 - orientation, FALSE);
1100   gtk_grid_request_sum (&request, 1 - orientation, &min_size, NULL);
1101   gtk_grid_request_allocate (&request, 1 - orientation, MAX (size, min_size));
1102
1103   gtk_grid_request_run (&request, orientation, TRUE);
1104   gtk_grid_request_sum (&request, orientation, minimum, natural);
1105 }
1106
1107 static void
1108 gtk_grid_get_preferred_width (GtkWidget *widget,
1109                               gint      *minimum,
1110                               gint      *natural)
1111 {
1112   GtkGrid *grid = GTK_GRID (widget);
1113
1114   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1115     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, 0, minimum, natural);
1116   else
1117     gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
1118 }
1119
1120 static void
1121 gtk_grid_get_preferred_height (GtkWidget *widget,
1122                                gint      *minimum,
1123                                gint      *natural)
1124 {
1125   GtkGrid *grid = GTK_GRID (widget);
1126
1127   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1128     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, 0, minimum, natural);
1129   else
1130     gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
1131 }
1132
1133 static void
1134 gtk_grid_get_preferred_width_for_height (GtkWidget *widget,
1135                                          gint       height,
1136                                          gint      *minimum,
1137                                          gint      *natural)
1138 {
1139   GtkGrid *grid = GTK_GRID (widget);
1140
1141   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1142     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
1143   else
1144     gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
1145 }
1146
1147 static void
1148 gtk_grid_get_preferred_height_for_width (GtkWidget *widget,
1149                                          gint       width,
1150                                          gint      *minimum,
1151                                          gint      *natural)
1152 {
1153   GtkGrid *grid = GTK_GRID (widget);
1154
1155   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1156     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
1157   else
1158     gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
1159 }
1160
1161 static void
1162 allocate_child (GtkGridRequest *request,
1163                 GtkOrientation  orientation,
1164                 GtkGridChild   *child,
1165                 gint           *position,
1166                 gint           *size)
1167 {
1168   GtkGridPrivate *priv = request->grid->priv;
1169   GtkGridLineData *linedata;
1170   GtkGridLines *lines;
1171   GtkGridLine *line;
1172   GtkGridChildAttach *attach;
1173   gint i;
1174
1175   linedata = &priv->linedata[orientation];
1176   lines = &request->lines[orientation];
1177   attach = &child->attach[orientation];
1178
1179   *position = lines->lines[attach->pos - lines->min].position;
1180
1181   *size = (attach->span - 1) * linedata->spacing;
1182   for (i = 0; i < attach->span; i++)
1183     {
1184       line = &lines->lines[attach->pos - lines->min + i];
1185       *size += line->allocation;
1186     }
1187 }
1188
1189 static void
1190 gtk_grid_request_allocate_children (GtkGridRequest *request)
1191 {
1192   GtkGridPrivate *priv = request->grid->priv;
1193   GList *list;
1194   GtkGridChild *child;
1195   GtkAllocation allocation;
1196   GtkAllocation child_allocation;
1197   gint x, y, width, height;
1198
1199   gtk_widget_get_allocation (GTK_WIDGET (request->grid), &allocation);
1200
1201   for (list = priv->children; list; list = list->next)
1202     {
1203       child = list->data;
1204
1205       if (!gtk_widget_get_visible (child->widget))
1206         continue;
1207
1208       allocate_child (request, GTK_ORIENTATION_HORIZONTAL, child, &x, &width);
1209       allocate_child (request, GTK_ORIENTATION_VERTICAL, child, &y, &height);
1210
1211       child_allocation.x = allocation.x + x;
1212       child_allocation.y = allocation.y + y;
1213       child_allocation.width = MAX (1, width);
1214       child_allocation.height = MAX (1, height);
1215
1216       if (gtk_widget_get_direction (GTK_WIDGET (request->grid)) == GTK_TEXT_DIR_RTL)
1217         child_allocation.x = allocation.x + allocation.width
1218                              - (child_allocation.x - allocation.x) - child_allocation.width;
1219
1220       gtk_widget_size_allocate (child->widget, &child_allocation);
1221     }
1222 }
1223
1224 #define GET_SIZE(allocation, orientation) (orientation == GTK_ORIENTATION_HORIZONTAL ? allocation->width : allocation->height)
1225
1226 static void
1227 gtk_grid_size_allocate (GtkWidget     *widget,
1228                         GtkAllocation *allocation)
1229 {
1230   GtkGrid *grid = GTK_GRID (widget);
1231   GtkGridPrivate *priv = grid->priv;
1232   GtkGridRequest request;
1233   GtkGridLines *lines;
1234   GtkOrientation orientation;
1235
1236   if (priv->children == NULL)
1237     {
1238       gtk_widget_set_allocation (widget, allocation);
1239       return;
1240     }
1241
1242   request.grid = grid;
1243
1244   gtk_grid_request_count_lines (&request);
1245   lines = &request.lines[0];
1246   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1247   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1248   lines = &request.lines[1];
1249   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1250   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1251
1252   gtk_widget_set_allocation (widget, allocation);
1253
1254   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1255     orientation = GTK_ORIENTATION_HORIZONTAL;
1256   else
1257     orientation = GTK_ORIENTATION_VERTICAL;
1258
1259   gtk_grid_request_run (&request, 1 - orientation, FALSE);
1260   gtk_grid_request_allocate (&request, 1 - orientation, GET_SIZE (allocation, 1 - orientation));
1261   gtk_grid_request_run (&request, orientation, TRUE);
1262   gtk_grid_request_allocate (&request, orientation, GET_SIZE (allocation, orientation));
1263
1264   gtk_grid_request_position (&request, 0);
1265   gtk_grid_request_position (&request, 1);
1266
1267   gtk_grid_request_allocate_children (&request);
1268 }
1269
1270 static void
1271 gtk_grid_class_init (GtkGridClass *class)
1272 {
1273   GObjectClass *object_class = G_OBJECT_CLASS (class);
1274   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1275   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
1276
1277   object_class->get_property = gtk_grid_get_property;
1278   object_class->set_property = gtk_grid_set_property;
1279
1280   widget_class->size_allocate = gtk_grid_size_allocate;
1281   widget_class->get_preferred_width = gtk_grid_get_preferred_width;
1282   widget_class->get_preferred_height = gtk_grid_get_preferred_height;
1283   widget_class->get_preferred_width_for_height = gtk_grid_get_preferred_width_for_height;
1284   widget_class->get_preferred_height_for_width = gtk_grid_get_preferred_height_for_width;
1285
1286   container_class->add = gtk_grid_add;
1287   container_class->remove = gtk_grid_remove;
1288   container_class->forall = gtk_grid_forall;
1289   container_class->child_type = gtk_grid_child_type;
1290   container_class->set_child_property = gtk_grid_set_child_property;
1291   container_class->get_child_property = gtk_grid_get_child_property;
1292   gtk_container_class_handle_border_width (container_class);
1293
1294   g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
1295
1296   g_object_class_install_property (object_class, PROP_ROW_SPACING,
1297     g_param_spec_int ("row-spacing",
1298                       P_("Row spacing"),
1299                       P_("The amount of space between two consecutive rows"),
1300                       0, G_MAXINT16, 0,
1301                       GTK_PARAM_READWRITE));
1302
1303   g_object_class_install_property (object_class, PROP_COLUMN_SPACING,
1304     g_param_spec_int ("column-spacing",
1305                       P_("Column spacing"),
1306                       P_("The amount of space between two consecutive columns"),
1307                       0, G_MAXINT16, 0,
1308                       GTK_PARAM_READWRITE));
1309
1310   g_object_class_install_property (object_class, PROP_ROW_HOMOGENEOUS,
1311     g_param_spec_boolean ("row-homogeneous",
1312                           P_("Row Homogeneous"),
1313                           P_("If TRUE, the rows are all the same height"),
1314                           FALSE,
1315                           GTK_PARAM_READWRITE));
1316
1317   g_object_class_install_property (object_class, PROP_COLUMN_HOMOGENEOUS,
1318     g_param_spec_boolean ("column-homogeneous",
1319                           P_("Column Homogeneous"),
1320                           P_("If TRUE, the columns are all the same width"),
1321                           FALSE,
1322                           GTK_PARAM_READWRITE));
1323
1324   gtk_container_class_install_child_property (container_class, CHILD_PROP_LEFT_ATTACH,
1325     g_param_spec_int ("left-attach",
1326                       P_("Left attachment"),
1327                       P_("The column number to attach the left side of the child to"),
1328                       G_MININT, G_MAXINT, 0,
1329                       GTK_PARAM_READWRITE));
1330
1331   gtk_container_class_install_child_property (container_class, CHILD_PROP_TOP_ATTACH,
1332     g_param_spec_int ("top-attach",
1333                       P_("Top attachment"),
1334                       P_("The row number to attach the top side of a child widget to"),
1335                       G_MININT, G_MAXINT, 0,
1336                       GTK_PARAM_READWRITE));
1337
1338   gtk_container_class_install_child_property (container_class, CHILD_PROP_WIDTH,
1339     g_param_spec_int ("width",
1340                       P_("Width"),
1341                       P_("The number of columns that a child spans"),
1342                       1, G_MAXINT, 1,
1343                       GTK_PARAM_READWRITE));
1344
1345   gtk_container_class_install_child_property (container_class, CHILD_PROP_HEIGHT,
1346     g_param_spec_int ("height",
1347                       P_("Height"),
1348                       P_("The number of rows that a child spans"),
1349                       1, G_MAXINT, 1,
1350                       GTK_PARAM_READWRITE));
1351
1352   g_type_class_add_private (class, sizeof (GtkGridPrivate));
1353 }
1354
1355 /**
1356  * gtk_grid_new:
1357  *
1358  * Creates a new grid widget.
1359  *
1360  * Returns: the new #GtkGrid
1361  */
1362 GtkWidget *
1363 gtk_grid_new (void)
1364 {
1365   return g_object_new (GTK_TYPE_GRID, NULL);
1366 }
1367
1368 static void
1369 grid_attach (GtkGrid   *grid,
1370              GtkWidget *widget,
1371              gint       left,
1372              gint       top,
1373              gint       width,
1374              gint       height)
1375 {
1376   GtkGridPrivate *priv = grid->priv;
1377   GtkGridChild *child;
1378
1379   child = g_slice_new (GtkGridChild);
1380   child->widget = widget;
1381   CHILD_LEFT (child) = left;
1382   CHILD_TOP (child) = top;
1383   CHILD_WIDTH (child) = width;
1384   CHILD_HEIGHT (child) = height;
1385
1386   priv->children = g_list_prepend (priv->children, child);
1387
1388   gtk_widget_set_parent (widget, GTK_WIDGET (grid));
1389 }
1390
1391 /**
1392  * gtk_grid_attach:
1393  * @grid: a #GtkGrid
1394  * @child: the widget to add
1395  * @left: the column number to attach the left side of @child to
1396  * @top: the row number to attach the top side of @child to
1397  * @width: the number of columns that @child will span
1398  * @height: the number of rows that @child will span
1399  *
1400  * Adds a widget to the grid.
1401  *
1402  * The position of @child is determined by @left and @top. The
1403  * number of 'cells' that @child will occupy is determined by
1404  * @width and @height.
1405  */
1406 void
1407 gtk_grid_attach (GtkGrid   *grid,
1408                  GtkWidget *child,
1409                  gint       left,
1410                  gint       top,
1411                  gint       width,
1412                  gint       height)
1413 {
1414   g_return_if_fail (GTK_IS_GRID (grid));
1415   g_return_if_fail (GTK_IS_WIDGET (child));
1416   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
1417   g_return_if_fail (width > 0);
1418   g_return_if_fail (height > 0);
1419
1420   grid_attach (grid, child, left, top, width, height);
1421 }
1422
1423 /**
1424  * gtk_grid_attach_next_to:
1425  * @grid: a #GtkGrid
1426  * @child: the widget to add
1427  * @sibling: the child of @grid that @child will be placed next to
1428  * @side: the side of @sibling that @child is positioned next to
1429  * @width: the number of columns that @child will span
1430  * @height: the number of rows that @child will span
1431  *
1432  * Adds a widget to the grid.
1433  *
1434  * The widget is placed next to @sibling, on the side determined by
1435  * @side.
1436  */
1437 void
1438 gtk_grid_attach_next_to (GtkGrid         *grid,
1439                          GtkWidget       *child,
1440                          GtkWidget       *sibling,
1441                          GtkPositionType  side,
1442                          gint             width,
1443                          gint             height)
1444 {
1445   GtkGridChild *grid_sibling;
1446   gint left, top;
1447
1448   g_return_if_fail (GTK_IS_GRID (grid));
1449   g_return_if_fail (GTK_IS_WIDGET (child));
1450   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
1451   g_return_if_fail (gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
1452   g_return_if_fail (width > 0);
1453   g_return_if_fail (height > 0);
1454
1455   grid_sibling = find_grid_child (grid, sibling);
1456
1457   switch (side)
1458     {
1459     case GTK_POS_LEFT:
1460       left = CHILD_LEFT (grid_sibling) - width;
1461       top = CHILD_TOP (grid_sibling);
1462       break;
1463     case GTK_POS_RIGHT:
1464       left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling);
1465       top = CHILD_TOP (grid_sibling);
1466       break;
1467     case GTK_POS_TOP:
1468       left = CHILD_LEFT (grid_sibling);
1469       top = CHILD_TOP (grid_sibling) - height;
1470       break;
1471     case GTK_POS_BOTTOM:
1472       left = CHILD_LEFT (grid_sibling);
1473       top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling);
1474       break;
1475     default:
1476       g_assert_not_reached ();
1477     }
1478
1479   grid_attach (grid, child, left, top, width, height);
1480 }
1481
1482 /**
1483  * gtk_grid_set_row_homogeneous:
1484  * @grid: a #GtkGrid
1485  * @homogeneous: %TRUE to make rows homogeneous
1486  *
1487  * Sets whether all rows of @grid will have the same height.
1488  */
1489 void
1490 gtk_grid_set_row_homogeneous (GtkGrid  *grid,
1491                               gboolean  homogeneous)
1492 {
1493   GtkGridPrivate *priv;
1494   g_return_if_fail (GTK_IS_GRID (grid));
1495
1496   priv = grid->priv;
1497
1498   /* Yes, homogeneous rows means all the columns have the same size */
1499   if (COLUMNS (priv)->homogeneous != homogeneous)
1500     {
1501       COLUMNS (priv)->homogeneous = homogeneous;
1502
1503       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1504         gtk_widget_queue_resize (GTK_WIDGET (grid));
1505
1506       g_object_notify (G_OBJECT (grid), "row-homogeneous");
1507     }
1508 }
1509
1510 /**
1511  * gtk_grid_get_row_homogeneous:
1512  * @grid: a #GtkGrid
1513  *
1514  * Returns whether all rows of @grid have the same height.
1515  *
1516  * Returns: whether all rows of @grid have the same height.
1517  */
1518 gboolean
1519 gtk_grid_get_row_homogeneous (GtkGrid *grid)
1520 {
1521   GtkGridPrivate *priv;
1522   g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
1523
1524   priv = grid->priv;
1525
1526   return COLUMNS (priv)->homogeneous;
1527 }
1528
1529 /**
1530  * gtk_grid_set_column_homogeneous:
1531  * @grid: a #GtkGrid
1532  * @homogeneous: %TRUE to make columns homogeneous
1533  *
1534  * Sets whether all columns of @grid will have the same width.
1535  */
1536 void
1537 gtk_grid_set_column_homogeneous (GtkGrid  *grid,
1538                                  gboolean  homogeneous)
1539 {
1540   GtkGridPrivate *priv;
1541   g_return_if_fail (GTK_IS_GRID (grid));
1542
1543   priv = grid->priv;
1544
1545   /* Yes, homogeneous columns means all the rows have the same size */
1546   if (ROWS (priv)->homogeneous != homogeneous)
1547     {
1548       ROWS (priv)->homogeneous = homogeneous;
1549
1550       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1551         gtk_widget_queue_resize (GTK_WIDGET (grid));
1552
1553       g_object_notify (G_OBJECT (grid), "column-homogeneous");
1554     }
1555 }
1556
1557 /**
1558  * gtk_grid_get_column_homogeneous:
1559  * @grid: a #GtkGrid
1560  *
1561  * Returns whether all columns of @grid have the same width.
1562  *
1563  * Returns: whether all columns of @grid have the same width.
1564  */
1565 gboolean
1566 gtk_grid_get_column_homogeneous (GtkGrid *grid)
1567 {
1568   GtkGridPrivate *priv;
1569   g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
1570
1571   priv = grid->priv;
1572
1573   return ROWS (priv)->homogeneous;
1574 }
1575
1576 /**
1577  * gtk_grid_set_row_spacing:
1578  * @grid: a #GtkGrid
1579  * @spacing: the amount of space to insert between rows
1580  *
1581  * Sets the amount of space between rows of @grid.
1582  */
1583 void
1584 gtk_grid_set_row_spacing (GtkGrid *grid,
1585                           guint    spacing)
1586 {
1587   GtkGridPrivate *priv;
1588   g_return_if_fail (GTK_IS_GRID (grid));
1589   g_return_if_fail (spacing <= G_MAXINT16);
1590
1591   priv = grid->priv;
1592
1593   if (ROWS (priv)->spacing != spacing)
1594     {
1595       ROWS (priv)->spacing = spacing;
1596
1597       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1598         gtk_widget_queue_resize (GTK_WIDGET (grid));
1599
1600       g_object_notify (G_OBJECT (grid), "row-spacing");
1601     }
1602 }
1603
1604 /**
1605  * gtk_grid_get_row_spacing:
1606  * @grid: a #GtkGrid
1607  *
1608  * Returns the amount of space between the rows of @grid.
1609  *
1610  * Returns: the row spacing of @grid
1611  */
1612 guint
1613 gtk_grid_get_row_spacing (GtkGrid *grid)
1614 {
1615   GtkGridPrivate *priv;
1616   g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1617
1618   priv = grid->priv;
1619
1620   return ROWS (priv)->spacing;
1621 }
1622
1623 /**
1624  * gtk_grid_set_column_spacing:
1625  * @grid: a #GtkGrid
1626  * @spacing: the amount of space to insert between columns
1627  *
1628  * Sets the amount of space between columns of @grid.
1629  */
1630 void
1631 gtk_grid_set_column_spacing (GtkGrid *grid,
1632                              guint    spacing)
1633 {
1634   GtkGridPrivate *priv;
1635   g_return_if_fail (GTK_IS_GRID (grid));
1636   g_return_if_fail (spacing <= G_MAXINT16);
1637
1638   priv = grid->priv;
1639
1640   if (COLUMNS (priv)->spacing != spacing)
1641     {
1642       COLUMNS (priv)->spacing = spacing;
1643
1644       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1645         gtk_widget_queue_resize (GTK_WIDGET (grid));
1646
1647       g_object_notify (G_OBJECT (grid), "column-spacing");
1648     }
1649 }
1650
1651 /**
1652  * gtk_grid_get_column_spacing:
1653  * @grid: a #GtkGrid
1654  *
1655  * Returns the amount of space between the columns of @grid.
1656  *
1657  * Returns: the column spacing of @grid
1658  */
1659 guint
1660 gtk_grid_get_column_spacing (GtkGrid *grid)
1661 {
1662   GtkGridPrivate *priv;
1663
1664   g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1665
1666   priv = grid->priv;
1667
1668   return COLUMNS (priv)->spacing;
1669 }