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