]> Pileus Git - ~andy/gtk/blob - gtk/gtkgrid.c
Change FSF Address
[~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, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "config.h"
20
21 #include <string.h>
22
23 #include "gtkgrid.h"
24
25 #include "gtkorientableprivate.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, COLUMNS (priv)->spacing);
165       break;
166
167     case PROP_COLUMN_SPACING:
168       g_value_set_int (value, ROWS (priv)->spacing);
169       break;
170
171     case PROP_ROW_HOMOGENEOUS:
172       g_value_set_boolean (value, COLUMNS (priv)->homogeneous);
173       break;
174
175     case PROP_COLUMN_HOMOGENEOUS:
176       g_value_set_boolean (value, ROWS (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
191   if (priv->orientation != orientation)
192     {
193       priv->orientation = orientation;
194       _gtk_orientable_set_style_classes (GTK_ORIENTABLE (grid));
195
196       g_object_notify (G_OBJECT (grid), "orientation");
197     }
198 }
199
200 static void
201 gtk_grid_set_property (GObject      *object,
202                        guint         prop_id,
203                        const GValue *value,
204                        GParamSpec   *pspec)
205 {
206   GtkGrid *grid = GTK_GRID (object);
207
208   switch (prop_id)
209     {
210     case PROP_ORIENTATION:
211       gtk_grid_set_orientation (grid, g_value_get_enum (value));
212       break;
213
214     case PROP_ROW_SPACING:
215       gtk_grid_set_row_spacing (grid, g_value_get_int (value));
216       break;
217
218     case PROP_COLUMN_SPACING:
219       gtk_grid_set_column_spacing (grid, g_value_get_int (value));
220       break;
221
222     case PROP_ROW_HOMOGENEOUS:
223       gtk_grid_set_row_homogeneous (grid, g_value_get_boolean (value));
224       break;
225
226     case PROP_COLUMN_HOMOGENEOUS:
227       gtk_grid_set_column_homogeneous (grid, g_value_get_boolean (value));
228       break;
229
230     default:
231       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
232       break;
233     }
234 }
235
236 static GtkGridChild *
237 find_grid_child (GtkGrid   *grid,
238                  GtkWidget *widget)
239 {
240   GtkGridPrivate *priv = grid->priv;
241   GtkGridChild *child;
242   GList *list;
243
244   for (list = priv->children; list; list = list->next)
245     {
246       child = list->data;
247
248       if (child->widget == widget)
249         return child;
250     }
251
252   return NULL;
253 }
254
255 static void
256 gtk_grid_get_child_property (GtkContainer *container,
257                              GtkWidget    *child,
258                              guint         property_id,
259                              GValue       *value,
260                              GParamSpec   *pspec)
261 {
262   GtkGrid *grid = GTK_GRID (container);
263   GtkGridChild *grid_child;
264
265   grid_child = find_grid_child (grid, child);
266
267   if (grid_child == NULL)
268     {
269       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
270       return;
271     }
272
273   switch (property_id)
274     {
275     case CHILD_PROP_LEFT_ATTACH:
276       g_value_set_int (value, CHILD_LEFT (grid_child));
277       break;
278
279     case CHILD_PROP_TOP_ATTACH:
280       g_value_set_int (value, CHILD_TOP (grid_child));
281       break;
282
283     case CHILD_PROP_WIDTH:
284       g_value_set_int (value, CHILD_WIDTH (grid_child));
285       break;
286
287     case CHILD_PROP_HEIGHT:
288       g_value_set_int (value, CHILD_HEIGHT (grid_child));
289       break;
290
291     default:
292       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
293       break;
294     }
295 }
296
297 static void
298 gtk_grid_set_child_property (GtkContainer *container,
299                              GtkWidget    *child,
300                              guint         property_id,
301                              const GValue *value,
302                              GParamSpec   *pspec)
303 {
304   GtkGrid *grid = GTK_GRID (container);
305   GtkGridChild *grid_child;
306
307   grid_child = find_grid_child (grid, child);
308
309   if (grid_child == NULL)
310     {
311       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
312       return;
313     }
314
315   switch (property_id)
316     {
317     case CHILD_PROP_LEFT_ATTACH:
318       CHILD_LEFT (grid_child) = g_value_get_int (value);
319       break;
320
321     case CHILD_PROP_TOP_ATTACH:
322       CHILD_TOP (grid_child) = g_value_get_int (value);
323       break;
324
325    case CHILD_PROP_WIDTH:
326       CHILD_WIDTH (grid_child) = g_value_get_int (value);
327       break;
328
329     case CHILD_PROP_HEIGHT:
330       CHILD_HEIGHT (grid_child) = g_value_get_int (value);
331       break;
332
333     default:
334       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
335       break;
336     }
337
338   if (gtk_widget_get_visible (child) &&
339       gtk_widget_get_visible (GTK_WIDGET (grid)))
340     gtk_widget_queue_resize (child);
341 }
342
343 static void
344 gtk_grid_init (GtkGrid *grid)
345 {
346   GtkGridPrivate *priv;
347
348   grid->priv = G_TYPE_INSTANCE_GET_PRIVATE (grid, GTK_TYPE_GRID, GtkGridPrivate);
349   priv = grid->priv;
350
351   gtk_widget_set_has_window (GTK_WIDGET (grid), FALSE);
352   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (grid), FALSE);
353
354   priv->children = NULL;
355   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
356
357   priv->linedata[0].spacing = 0;
358   priv->linedata[1].spacing = 0;
359
360   priv->linedata[0].homogeneous = FALSE;
361   priv->linedata[1].homogeneous = FALSE;
362 }
363
364 static void
365 grid_attach (GtkGrid   *grid,
366              GtkWidget *widget,
367              gint       left,
368              gint       top,
369              gint       width,
370              gint       height)
371 {
372   GtkGridPrivate *priv = grid->priv;
373   GtkGridChild *child;
374
375   child = g_slice_new (GtkGridChild);
376   child->widget = widget;
377   CHILD_LEFT (child) = left;
378   CHILD_TOP (child) = top;
379   CHILD_WIDTH (child) = width;
380   CHILD_HEIGHT (child) = height;
381
382   priv->children = g_list_prepend (priv->children, child);
383
384   gtk_widget_set_parent (widget, GTK_WIDGET (grid));
385 }
386
387 /* Find the position 'touching' existing
388  * children. @orientation and @max determine
389  * from which direction to approach (horizontal
390  * + max = right, vertical + !max = top, etc).
391  * @op_pos, @op_span determine the rows/columns
392  * in which the touching has to happen.
393  */
394 static gint
395 find_attach_position (GtkGrid         *grid,
396                       GtkOrientation   orientation,
397                       gint             op_pos,
398                       gint             op_span,
399                       gboolean         max)
400 {
401   GtkGridPrivate *priv = grid->priv;
402   GtkGridChild *grid_child;
403   GtkGridChildAttach *attach;
404   GtkGridChildAttach *opposite;
405   GList *list;
406   gint pos;
407   gboolean hit;
408
409   if (max)
410     pos = -G_MAXINT;
411   else
412     pos = G_MAXINT;
413
414   hit = FALSE;
415
416   for (list = priv->children; list; list = list->next)
417     {
418       grid_child = list->data;
419
420       attach = &grid_child->attach[orientation];
421       opposite = &grid_child->attach[1 - orientation];
422
423       /* check if the ranges overlap */
424       if (opposite->pos <= op_pos + op_span && op_pos <= opposite->pos + opposite->span)
425         {
426           hit = TRUE;
427
428           if (max)
429             pos = MAX (pos, attach->pos + attach->span);
430           else
431             pos = MIN (pos, attach->pos);
432         }
433      }
434
435   if (!hit)
436     pos = 0;
437
438   return pos;
439 }
440
441 static void
442 gtk_grid_add (GtkContainer *container,
443               GtkWidget    *child)
444 {
445   GtkGrid *grid = GTK_GRID (container);
446   GtkGridPrivate *priv = grid->priv;
447   gint pos[2] = { 0, 0 };
448
449   pos[priv->orientation] = find_attach_position (grid, priv->orientation, 0, 1, TRUE);
450   grid_attach (grid, child, pos[0], pos[1], 1, 1);
451 }
452
453 static void
454 gtk_grid_remove (GtkContainer *container,
455                  GtkWidget    *child)
456 {
457   GtkGrid *grid = GTK_GRID (container);
458   GtkGridPrivate *priv = grid->priv;
459   GtkGridChild *grid_child;
460   GList *list;
461
462   for (list = priv->children; list; list = list->next)
463     {
464       grid_child = list->data;
465
466       if (grid_child->widget == child)
467         {
468           gboolean was_visible = gtk_widget_get_visible (child);
469
470           gtk_widget_unparent (child);
471
472           priv->children = g_list_remove (priv->children, grid_child);
473
474           g_slice_free (GtkGridChild, grid_child);
475
476           if (was_visible && gtk_widget_get_visible (GTK_WIDGET (grid)))
477             gtk_widget_queue_resize (GTK_WIDGET (grid));
478
479           break;
480         }
481     }
482 }
483
484 static void
485 gtk_grid_forall (GtkContainer *container,
486                  gboolean      include_internals,
487                  GtkCallback   callback,
488                  gpointer      callback_data)
489 {
490   GtkGrid *grid = GTK_GRID (container);
491   GtkGridPrivate *priv = grid->priv;
492   GtkGridChild *child;
493   GList *list;
494
495   list = priv->children;
496   while (list)
497     {
498       child = list->data;
499       list  = list->next;
500
501       (* callback) (child->widget, callback_data);
502     }
503 }
504
505 static GType
506 gtk_grid_child_type (GtkContainer *container)
507 {
508   return GTK_TYPE_WIDGET;
509 }
510
511 /* Calculates the min and max numbers for both orientations.
512  */
513 static void
514 gtk_grid_request_count_lines (GtkGridRequest *request)
515 {
516   GtkGridPrivate *priv = request->grid->priv;
517   GtkGridChild *child;
518   GtkGridChildAttach *attach;
519   GList *list;
520   gint min[2];
521   gint max[2];
522
523   min[0] = min[1] = G_MAXINT;
524   max[0] = max[1] = G_MININT;
525
526   for (list = priv->children; list; list = list->next)
527     {
528       child = list->data;
529       attach = child->attach;
530
531       min[0] = MIN (min[0], attach[0].pos);
532       max[0] = MAX (max[0], attach[0].pos + attach[0].span);
533       min[1] = MIN (min[1], attach[1].pos);
534       max[1] = MAX (max[1], attach[1].pos + attach[1].span);
535     }
536
537   request->lines[0].min = min[0];
538   request->lines[0].max = max[0];
539   request->lines[1].min = min[1];
540   request->lines[1].max = max[1];
541 }
542
543 /* Sets line sizes to 0 and marks lines as expand
544  * if they have a non-spanning expanding child.
545  */
546 static void
547 gtk_grid_request_init (GtkGridRequest *request,
548                        GtkOrientation  orientation)
549 {
550   GtkGridPrivate *priv = request->grid->priv;
551   GtkGridChild *child;
552   GtkGridChildAttach *attach;
553   GtkGridLines *lines;
554   GList *list;
555   gint i;
556
557   lines = &request->lines[orientation];
558
559   for (i = 0; i < lines->max - lines->min; i++)
560     {
561       lines->lines[i].minimum = 0;
562       lines->lines[i].natural = 0;
563       lines->lines[i].expand = FALSE;
564     }
565
566   for (list = priv->children; list; list = list->next)
567     {
568       child = list->data;
569
570       attach = &child->attach[orientation];
571       if (attach->span == 1 && gtk_widget_compute_expand (child->widget, orientation))
572         lines->lines[attach->pos - lines->min].expand = TRUE;
573     }
574 }
575
576 /* Sums allocations for lines spanned by child and their spacing.
577  */
578 static gint
579 compute_allocation_for_child (GtkGridRequest *request,
580                               GtkGridChild   *child,
581                               GtkOrientation  orientation)
582 {
583   GtkGridPrivate *priv = request->grid->priv;
584   GtkGridLineData *linedata;
585   GtkGridLines *lines;
586   GtkGridLine *line;
587   GtkGridChildAttach *attach;
588   gint size;
589   gint i;
590
591   linedata = &priv->linedata[orientation];
592   lines = &request->lines[orientation];
593   attach = &child->attach[orientation];
594
595   size = (attach->span - 1) * linedata->spacing;
596   for (i = 0; i < attach->span; i++)
597     {
598       line = &lines->lines[attach->pos - lines->min + i];
599       size += line->allocation;
600     }
601
602   return size;
603 }
604
605 static void
606 compute_request_for_child (GtkGridRequest *request,
607                            GtkGridChild   *child,
608                            GtkOrientation  orientation,
609                            gboolean        contextual,
610                            gint           *minimum,
611                            gint           *natural)
612 {
613   if (contextual)
614     {
615       gint size;
616
617       size = compute_allocation_for_child (request, child, 1 - orientation);
618       if (orientation == GTK_ORIENTATION_HORIZONTAL)
619         gtk_widget_get_preferred_width_for_height (child->widget,
620                                                    size,
621                                                    minimum, natural);
622       else
623         gtk_widget_get_preferred_height_for_width (child->widget,
624                                                    size,
625                                                    minimum, natural);
626     }
627   else
628     {
629       if (orientation == GTK_ORIENTATION_HORIZONTAL)
630         gtk_widget_get_preferred_width (child->widget, minimum, natural);
631       else
632         gtk_widget_get_preferred_height (child->widget, minimum, natural);
633     }
634 }
635
636 /* Sets requisition to max. of non-spanning children.
637  * If contextual is TRUE, requires allocations of
638  * lines in the opposite orientation to be set.
639  */
640 static void
641 gtk_grid_request_non_spanning (GtkGridRequest *request,
642                                GtkOrientation  orientation,
643                                gboolean        contextual)
644 {
645   GtkGridPrivate *priv = request->grid->priv;
646   GtkGridChild *child;
647   GtkGridChildAttach *attach;
648   GtkGridLines *lines;
649   GtkGridLine *line;
650   GList *list;
651   gint minimum;
652   gint natural;
653
654   lines = &request->lines[orientation];
655
656   for (list = priv->children; list; list = list->next)
657     {
658       child = list->data;
659
660       if (!gtk_widget_get_visible (child->widget))
661         continue;
662
663       attach = &child->attach[orientation];
664       if (attach->span != 1)
665         continue;
666
667       compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
668
669       line = &lines->lines[attach->pos - lines->min];
670       line->minimum = MAX (line->minimum, minimum);
671       line->natural = MAX (line->natural, natural);
672     }
673 }
674
675 /* Enforce homogeneous sizes.
676  */
677 static void
678 gtk_grid_request_homogeneous (GtkGridRequest *request,
679                               GtkOrientation  orientation)
680 {
681   GtkGridPrivate *priv = request->grid->priv;
682   GtkGridLineData *linedata;
683   GtkGridLines *lines;
684   gint minimum, natural;
685   gint i;
686
687   linedata = &priv->linedata[orientation];
688   lines = &request->lines[orientation];
689
690   if (!linedata->homogeneous)
691     return;
692
693   minimum = 0;
694   natural = 0;
695
696   for (i = 0; i < lines->max - lines->min; i++)
697     {
698       minimum = MAX (minimum, lines->lines[i].minimum);
699       natural = MAX (natural, lines->lines[i].natural);
700     }
701
702   for (i = 0; i < lines->max - lines->min; i++)
703     {
704       lines->lines[i].minimum = minimum;
705       lines->lines[i].natural = natural;
706     }
707 }
708
709 /* Deals with spanning children.
710  * Requires expand fields of lines to be set for
711  * non-spanning children.
712  */
713 static void
714 gtk_grid_request_spanning (GtkGridRequest *request,
715                            GtkOrientation  orientation,
716                            gboolean        contextual)
717 {
718   GtkGridPrivate *priv = request->grid->priv;
719   GList *list;
720   GtkGridChild *child;
721   GtkGridChildAttach *attach;
722   GtkGridLineData *linedata;
723   GtkGridLines *lines;
724   GtkGridLine *line;
725   gint minimum;
726   gint natural;
727   gint span_minimum;
728   gint span_natural;
729   gint span_expand;
730   gboolean force_expand;
731   gint extra;
732   gint expand;
733   gint line_extra;
734   gint i;
735
736   linedata = &priv->linedata[orientation];
737   lines = &request->lines[orientation];
738
739   for (list = priv->children; list; list = list->next)
740     {
741       child = list->data;
742
743       if (!gtk_widget_get_visible (child->widget))
744         continue;
745
746       attach = &child->attach[orientation];
747       if (attach->span == 1)
748         continue;
749
750       compute_request_for_child (request, child, orientation, contextual, &minimum, &natural);
751
752       span_minimum = (attach->span - 1) * linedata->spacing;
753       span_natural = (attach->span - 1) * linedata->spacing;
754       span_expand = 0;
755       force_expand = FALSE;
756       for (i = 0; i < attach->span; i++)
757         {
758           line = &lines->lines[attach->pos - lines->min + i];
759           span_minimum += line->minimum;
760           span_natural += line->natural;
761           if (line->expand)
762             span_expand += 1;
763         }
764       if (span_expand == 0)
765         {
766           span_expand = attach->span;
767           force_expand = TRUE;
768         }
769
770       /* If we need to request more space for this child to fill
771        * its requisition, then divide up the needed space amongst the
772        * lines it spans, favoring expandable lines if any.
773        */
774       if (span_minimum < minimum)
775         {
776           extra = minimum - span_minimum;
777           expand = span_expand;
778           for (i = 0; i < attach->span; i++)
779             {
780               line = &lines->lines[attach->pos - lines->min + i];
781               if (force_expand || line->expand)
782                 {
783                   line_extra = extra / expand;
784                   line->minimum += line_extra;
785                   extra -= line_extra;
786                   expand -= 1;
787                 }
788             }
789         }
790
791       if (span_natural < natural)
792         {
793           extra = natural - span_natural;
794           expand = span_expand;
795           for (i = 0; i < attach->span; i++)
796             {
797               line = &lines->lines[attach->pos - lines->min + i];
798               if (force_expand || line->expand)
799                 {
800                   line_extra = extra / expand;
801                   line->natural += line_extra;
802                   extra -= line_extra;
803                   expand -= 1;
804                 }
805             }
806         }
807     }
808 }
809
810 /* Marks empty and expanding lines and counts them.
811  */
812 static void
813 gtk_grid_request_compute_expand (GtkGridRequest *request,
814                                  GtkOrientation  orientation,
815                                  gint           *nonempty_lines,
816                                  gint           *expand_lines)
817 {
818   GtkGridPrivate *priv = request->grid->priv;
819   GtkGridChild *child;
820   GtkGridChildAttach *attach;
821   GList *list;
822   gint i;
823   GtkGridLines *lines;
824   GtkGridLine *line;
825   gboolean has_expand;
826   gint expand;
827   gint empty;
828
829   lines = &request->lines[orientation];
830
831   for (i = 0; i < lines->max - lines->min; i++)
832     {
833       lines->lines[i].need_expand = FALSE;
834       lines->lines[i].expand = FALSE;
835       lines->lines[i].empty = TRUE;
836     }
837
838   for (list = priv->children; list; list = list->next)
839     {
840       child = list->data;
841
842       if (!gtk_widget_get_visible (child->widget))
843         continue;
844
845       attach = &child->attach[orientation];
846       if (attach->span != 1)
847         continue;
848
849       line = &lines->lines[attach->pos - lines->min];
850       line->empty = FALSE;
851       if (gtk_widget_compute_expand (child->widget, orientation))
852         line->expand = TRUE;
853     }
854
855   for (list = priv->children; list; list = list->next)
856     {
857       child = list->data;
858
859       if (!gtk_widget_get_visible (child->widget))
860         continue;
861
862       attach = &child->attach[orientation];
863       if (attach->span == 1)
864         continue;
865
866       has_expand = FALSE;
867       for (i = 0; i < attach->span; i++)
868         {
869           line = &lines->lines[attach->pos - lines->min + i];
870           line->empty = FALSE;
871           if (line->expand)
872             has_expand = TRUE;
873         }
874
875       if (!has_expand && gtk_widget_compute_expand (child->widget, orientation))
876         {
877           for (i = 0; i < attach->span; i++)
878             {
879               line = &lines->lines[attach->pos - lines->min + i];
880               line->need_expand = TRUE;
881             }
882         }
883     }
884
885   empty = 0;
886   expand = 0;
887   for (i = 0; i < lines->max - lines->min; i++)
888     {
889       line = &lines->lines[i];
890
891       if (line->need_expand)
892         line->expand = TRUE;
893
894       if (line->empty)
895         empty += 1;
896
897       if (line->expand)
898         expand += 1;
899     }
900
901   if (nonempty_lines)
902     *nonempty_lines = lines->max - lines->min - empty;
903
904   if (expand_lines)
905     *expand_lines = expand;
906 }
907
908 /* Sums the minimum and natural fields of lines and their spacing.
909  */
910 static void
911 gtk_grid_request_sum (GtkGridRequest *request,
912                       GtkOrientation  orientation,
913                       gint           *minimum,
914                       gint           *natural)
915 {
916   GtkGridPrivate *priv = request->grid->priv;
917   GtkGridLineData *linedata;
918   GtkGridLines *lines;
919   gint i;
920   gint min, nat;
921   gint nonempty;
922
923   gtk_grid_request_compute_expand (request, orientation, &nonempty, NULL);
924
925   linedata = &priv->linedata[orientation];
926   lines = &request->lines[orientation];
927
928   min = 0;
929   nat = 0;
930   if (nonempty > 0)
931     {
932       min = (nonempty - 1) * linedata->spacing;
933       nat = (nonempty - 1) * linedata->spacing;
934     }
935
936   for (i = 0; i < lines->max - lines->min; i++)
937     {
938       min += lines->lines[i].minimum;
939       nat += lines->lines[i].natural;
940     }
941
942   if (minimum)
943     *minimum = min;
944
945   if (natural)
946     *natural = nat;
947 }
948
949 /* Computes minimum and natural fields of lines.
950  * When contextual is TRUE, requires allocation of
951  * lines in the opposite orientation to be set.
952  */
953 static void
954 gtk_grid_request_run (GtkGridRequest *request,
955                       GtkOrientation  orientation,
956                       gboolean        contextual)
957 {
958   gtk_grid_request_init (request, orientation);
959   gtk_grid_request_non_spanning (request, orientation, contextual);
960   gtk_grid_request_homogeneous (request, orientation);
961   gtk_grid_request_spanning (request, orientation, contextual);
962   gtk_grid_request_homogeneous (request, orientation);
963 }
964
965 /* Requires that the minimum and natural fields of lines
966  * have been set, computes the allocation field of lines
967  * by distributing total_size among lines.
968  */
969 static void
970 gtk_grid_request_allocate (GtkGridRequest *request,
971                            GtkOrientation  orientation,
972                            gint            total_size)
973 {
974   GtkGridPrivate *priv = request->grid->priv;
975   GtkGridLineData *linedata;
976   GtkGridLines *lines;
977   GtkGridLine *line;
978   gint nonempty;
979   gint expand;
980   gint i, j;
981   GtkRequestedSize *sizes;
982   gint extra;
983   gint rest;
984   gint size;
985
986   gtk_grid_request_compute_expand (request, orientation, &nonempty, &expand);
987
988   linedata = &priv->linedata[orientation];
989   lines = &request->lines[orientation];
990
991   size = total_size - (nonempty - 1) * linedata->spacing;
992
993   if (linedata->homogeneous)
994     {
995       extra = size / nonempty;
996       rest = size % nonempty;
997
998       for (i = 0; i < lines->max - lines->min; i++)
999         {
1000           line = &lines->lines[i];
1001           if (line->empty)
1002             continue;
1003
1004           line->allocation = extra;
1005           if (rest > 0)
1006             {
1007               line->allocation += 1;
1008               rest -= 1;
1009             }
1010         }
1011     }
1012   else
1013     {
1014       sizes = g_newa (GtkRequestedSize, nonempty);
1015
1016       j = 0;
1017       for (i = 0; i < lines->max - lines->min; i++)
1018         {
1019           line = &lines->lines[i];
1020           if (line->empty)
1021             continue;
1022
1023           size -= line->minimum;
1024
1025           sizes[j].minimum_size = line->minimum;
1026           sizes[j].natural_size = line->natural;
1027           sizes[j].data = line;
1028           j++;
1029         }
1030
1031       size = gtk_distribute_natural_allocation (MAX (0, size), nonempty, sizes);
1032
1033       if (expand > 0)
1034         {
1035           extra = size / expand;
1036           rest = size % expand;
1037         }
1038       else
1039         {
1040           extra = 0;
1041           rest = 0;
1042         }
1043
1044       j = 0;
1045       for (i = 0; i < lines->max - lines->min; i++)
1046         {
1047           line = &lines->lines[i];
1048           if (line->empty)
1049             continue;
1050
1051           g_assert (line == sizes[j].data);
1052
1053           line->allocation = sizes[j].minimum_size;
1054           if (line->expand)
1055             {
1056               line->allocation += extra;
1057               if (rest > 0)
1058                 {
1059                   line->allocation += 1;
1060                   rest -= 1;
1061                 }
1062             }
1063
1064           j++;
1065         }
1066     }
1067 }
1068
1069 /* Computes the position fields from allocation and spacing.
1070  */
1071 static void
1072 gtk_grid_request_position (GtkGridRequest *request,
1073                            GtkOrientation  orientation)
1074 {
1075   GtkGridPrivate *priv = request->grid->priv;
1076   GtkGridLineData *linedata;
1077   GtkGridLines *lines;
1078   GtkGridLine *line;
1079   gint position;
1080   gint i;
1081
1082   linedata = &priv->linedata[orientation];
1083   lines = &request->lines[orientation];
1084
1085   position = 0;
1086   for (i = 0; i < lines->max - lines->min; i++)
1087     {
1088       line = &lines->lines[i];
1089       if (!line->empty)
1090         {
1091           line->position = position;
1092           position += line->allocation + linedata->spacing;
1093         }
1094     }
1095 }
1096
1097 static void
1098 gtk_grid_get_size (GtkGrid        *grid,
1099                    GtkOrientation  orientation,
1100                    gint           *minimum,
1101                    gint           *natural)
1102 {
1103   GtkGridRequest request;
1104   GtkGridLines *lines;
1105
1106   if (minimum)
1107     *minimum = 0;
1108
1109   if (natural)
1110     *natural = 0;
1111
1112   if (grid->priv->children == NULL)
1113     return;
1114
1115   request.grid = grid;
1116   gtk_grid_request_count_lines (&request);
1117   lines = &request.lines[orientation];
1118   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1119   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1120
1121   gtk_grid_request_run (&request, orientation, FALSE);
1122   gtk_grid_request_sum (&request, orientation, minimum, natural);
1123 }
1124
1125 static void
1126 gtk_grid_get_size_for_size (GtkGrid        *grid,
1127                             GtkOrientation  orientation,
1128                             gint            size,
1129                             gint           *minimum,
1130                             gint           *natural)
1131 {
1132   GtkGridRequest request;
1133   GtkGridLines *lines;
1134   gint min_size;
1135
1136   if (minimum)
1137     *minimum = 0;
1138
1139   if (natural)
1140     *natural = 0;
1141
1142   if (grid->priv->children == NULL)
1143     return;
1144
1145   request.grid = grid;
1146   gtk_grid_request_count_lines (&request);
1147   lines = &request.lines[0];
1148   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1149   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1150   lines = &request.lines[1];
1151   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1152   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1153
1154   gtk_grid_request_run (&request, 1 - orientation, FALSE);
1155   gtk_grid_request_sum (&request, 1 - orientation, &min_size, NULL);
1156   gtk_grid_request_allocate (&request, 1 - orientation, MAX (size, min_size));
1157
1158   gtk_grid_request_run (&request, orientation, TRUE);
1159   gtk_grid_request_sum (&request, orientation, minimum, natural);
1160 }
1161
1162 static void
1163 gtk_grid_get_preferred_width (GtkWidget *widget,
1164                               gint      *minimum,
1165                               gint      *natural)
1166 {
1167   GtkGrid *grid = GTK_GRID (widget);
1168
1169   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1170     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, 0, minimum, natural);
1171   else
1172     gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
1173 }
1174
1175 static void
1176 gtk_grid_get_preferred_height (GtkWidget *widget,
1177                                gint      *minimum,
1178                                gint      *natural)
1179 {
1180   GtkGrid *grid = GTK_GRID (widget);
1181
1182   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1183     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, 0, minimum, natural);
1184   else
1185     gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
1186 }
1187
1188 static void
1189 gtk_grid_get_preferred_width_for_height (GtkWidget *widget,
1190                                          gint       height,
1191                                          gint      *minimum,
1192                                          gint      *natural)
1193 {
1194   GtkGrid *grid = GTK_GRID (widget);
1195
1196   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1197     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
1198   else
1199     gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
1200 }
1201
1202 static void
1203 gtk_grid_get_preferred_height_for_width (GtkWidget *widget,
1204                                          gint       width,
1205                                          gint      *minimum,
1206                                          gint      *natural)
1207 {
1208   GtkGrid *grid = GTK_GRID (widget);
1209
1210   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1211     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
1212   else
1213     gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
1214 }
1215
1216 static void
1217 allocate_child (GtkGridRequest *request,
1218                 GtkOrientation  orientation,
1219                 GtkGridChild   *child,
1220                 gint           *position,
1221                 gint           *size)
1222 {
1223   GtkGridPrivate *priv = request->grid->priv;
1224   GtkGridLineData *linedata;
1225   GtkGridLines *lines;
1226   GtkGridLine *line;
1227   GtkGridChildAttach *attach;
1228   gint i;
1229
1230   linedata = &priv->linedata[orientation];
1231   lines = &request->lines[orientation];
1232   attach = &child->attach[orientation];
1233
1234   *position = lines->lines[attach->pos - lines->min].position;
1235
1236   *size = (attach->span - 1) * linedata->spacing;
1237   for (i = 0; i < attach->span; i++)
1238     {
1239       line = &lines->lines[attach->pos - lines->min + i];
1240       *size += line->allocation;
1241     }
1242 }
1243
1244 static void
1245 gtk_grid_request_allocate_children (GtkGridRequest *request)
1246 {
1247   GtkGridPrivate *priv = request->grid->priv;
1248   GList *list;
1249   GtkGridChild *child;
1250   GtkAllocation allocation;
1251   GtkAllocation child_allocation;
1252   gint x, y, width, height;
1253
1254   gtk_widget_get_allocation (GTK_WIDGET (request->grid), &allocation);
1255
1256   for (list = priv->children; list; list = list->next)
1257     {
1258       child = list->data;
1259
1260       if (!gtk_widget_get_visible (child->widget))
1261         continue;
1262
1263       allocate_child (request, GTK_ORIENTATION_HORIZONTAL, child, &x, &width);
1264       allocate_child (request, GTK_ORIENTATION_VERTICAL, child, &y, &height);
1265
1266       child_allocation.x = allocation.x + x;
1267       child_allocation.y = allocation.y + y;
1268       child_allocation.width = MAX (1, width);
1269       child_allocation.height = MAX (1, height);
1270
1271       if (gtk_widget_get_direction (GTK_WIDGET (request->grid)) == GTK_TEXT_DIR_RTL)
1272         child_allocation.x = allocation.x + allocation.width
1273                              - (child_allocation.x - allocation.x) - child_allocation.width;
1274
1275       gtk_widget_size_allocate (child->widget, &child_allocation);
1276     }
1277 }
1278
1279 #define GET_SIZE(allocation, orientation) (orientation == GTK_ORIENTATION_HORIZONTAL ? allocation->width : allocation->height)
1280
1281 static void
1282 gtk_grid_size_allocate (GtkWidget     *widget,
1283                         GtkAllocation *allocation)
1284 {
1285   GtkGrid *grid = GTK_GRID (widget);
1286   GtkGridPrivate *priv = grid->priv;
1287   GtkGridRequest request;
1288   GtkGridLines *lines;
1289   GtkOrientation orientation;
1290
1291   if (priv->children == NULL)
1292     {
1293       gtk_widget_set_allocation (widget, allocation);
1294       return;
1295     }
1296
1297   request.grid = grid;
1298
1299   gtk_grid_request_count_lines (&request);
1300   lines = &request.lines[0];
1301   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1302   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1303   lines = &request.lines[1];
1304   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1305   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1306
1307   gtk_widget_set_allocation (widget, allocation);
1308
1309   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1310     orientation = GTK_ORIENTATION_HORIZONTAL;
1311   else
1312     orientation = GTK_ORIENTATION_VERTICAL;
1313
1314   gtk_grid_request_run (&request, 1 - orientation, FALSE);
1315   gtk_grid_request_allocate (&request, 1 - orientation, GET_SIZE (allocation, 1 - orientation));
1316   gtk_grid_request_run (&request, orientation, TRUE);
1317   gtk_grid_request_allocate (&request, orientation, GET_SIZE (allocation, orientation));
1318
1319   gtk_grid_request_position (&request, 0);
1320   gtk_grid_request_position (&request, 1);
1321
1322   gtk_grid_request_allocate_children (&request);
1323 }
1324
1325 static void
1326 gtk_grid_class_init (GtkGridClass *class)
1327 {
1328   GObjectClass *object_class = G_OBJECT_CLASS (class);
1329   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1330   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
1331
1332   object_class->get_property = gtk_grid_get_property;
1333   object_class->set_property = gtk_grid_set_property;
1334
1335   widget_class->size_allocate = gtk_grid_size_allocate;
1336   widget_class->get_preferred_width = gtk_grid_get_preferred_width;
1337   widget_class->get_preferred_height = gtk_grid_get_preferred_height;
1338   widget_class->get_preferred_width_for_height = gtk_grid_get_preferred_width_for_height;
1339   widget_class->get_preferred_height_for_width = gtk_grid_get_preferred_height_for_width;
1340
1341   container_class->add = gtk_grid_add;
1342   container_class->remove = gtk_grid_remove;
1343   container_class->forall = gtk_grid_forall;
1344   container_class->child_type = gtk_grid_child_type;
1345   container_class->set_child_property = gtk_grid_set_child_property;
1346   container_class->get_child_property = gtk_grid_get_child_property;
1347   gtk_container_class_handle_border_width (container_class);
1348
1349   g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
1350
1351   g_object_class_install_property (object_class, PROP_ROW_SPACING,
1352     g_param_spec_int ("row-spacing",
1353                       P_("Row spacing"),
1354                       P_("The amount of space between two consecutive rows"),
1355                       0, G_MAXINT16, 0,
1356                       GTK_PARAM_READWRITE));
1357
1358   g_object_class_install_property (object_class, PROP_COLUMN_SPACING,
1359     g_param_spec_int ("column-spacing",
1360                       P_("Column spacing"),
1361                       P_("The amount of space between two consecutive columns"),
1362                       0, G_MAXINT16, 0,
1363                       GTK_PARAM_READWRITE));
1364
1365   g_object_class_install_property (object_class, PROP_ROW_HOMOGENEOUS,
1366     g_param_spec_boolean ("row-homogeneous",
1367                           P_("Row Homogeneous"),
1368                           P_("If TRUE, the rows are all the same height"),
1369                           FALSE,
1370                           GTK_PARAM_READWRITE));
1371
1372   g_object_class_install_property (object_class, PROP_COLUMN_HOMOGENEOUS,
1373     g_param_spec_boolean ("column-homogeneous",
1374                           P_("Column Homogeneous"),
1375                           P_("If TRUE, the columns are all the same width"),
1376                           FALSE,
1377                           GTK_PARAM_READWRITE));
1378
1379   gtk_container_class_install_child_property (container_class, CHILD_PROP_LEFT_ATTACH,
1380     g_param_spec_int ("left-attach",
1381                       P_("Left attachment"),
1382                       P_("The column number to attach the left side of the child to"),
1383                       G_MININT, G_MAXINT, 0,
1384                       GTK_PARAM_READWRITE));
1385
1386   gtk_container_class_install_child_property (container_class, CHILD_PROP_TOP_ATTACH,
1387     g_param_spec_int ("top-attach",
1388                       P_("Top attachment"),
1389                       P_("The row number to attach the top side of a child widget to"),
1390                       G_MININT, G_MAXINT, 0,
1391                       GTK_PARAM_READWRITE));
1392
1393   gtk_container_class_install_child_property (container_class, CHILD_PROP_WIDTH,
1394     g_param_spec_int ("width",
1395                       P_("Width"),
1396                       P_("The number of columns that a child spans"),
1397                       1, G_MAXINT, 1,
1398                       GTK_PARAM_READWRITE));
1399
1400   gtk_container_class_install_child_property (container_class, CHILD_PROP_HEIGHT,
1401     g_param_spec_int ("height",
1402                       P_("Height"),
1403                       P_("The number of rows that a child spans"),
1404                       1, G_MAXINT, 1,
1405                       GTK_PARAM_READWRITE));
1406
1407   g_type_class_add_private (class, sizeof (GtkGridPrivate));
1408 }
1409
1410 /**
1411  * gtk_grid_new:
1412  *
1413  * Creates a new grid widget.
1414  *
1415  * Returns: the new #GtkGrid
1416  */
1417 GtkWidget *
1418 gtk_grid_new (void)
1419 {
1420   return g_object_new (GTK_TYPE_GRID, NULL);
1421 }
1422
1423 /**
1424  * gtk_grid_attach:
1425  * @grid: a #GtkGrid
1426  * @child: the widget to add
1427  * @left: the column number to attach the left side of @child to
1428  * @top: the row number to attach the top side of @child 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 position of @child is determined by @left and @top. The
1435  * number of 'cells' that @child will occupy is determined by
1436  * @width and @height.
1437  */
1438 void
1439 gtk_grid_attach (GtkGrid   *grid,
1440                  GtkWidget *child,
1441                  gint       left,
1442                  gint       top,
1443                  gint       width,
1444                  gint       height)
1445 {
1446   g_return_if_fail (GTK_IS_GRID (grid));
1447   g_return_if_fail (GTK_IS_WIDGET (child));
1448   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
1449   g_return_if_fail (width > 0);
1450   g_return_if_fail (height > 0);
1451
1452   grid_attach (grid, child, left, top, width, height);
1453 }
1454
1455 /**
1456  * gtk_grid_attach_next_to:
1457  * @grid: a #GtkGrid
1458  * @child: the widget to add
1459  * @sibling: (allow-none): the child of @grid that @child will be placed
1460  *     next to, or %NULL to place @child at the beginning or end
1461  * @side: the side of @sibling that @child is positioned next to
1462  * @width: the number of columns that @child will span
1463  * @height: the number of rows that @child will span
1464  *
1465  * Adds a widget to the grid.
1466  *
1467  * The widget is placed next to @sibling, on the side determined by
1468  * @side. When @sibling is %NULL, the widget is placed in row (for
1469  * left or right placement) or column 0 (for top or bottom placement),
1470  * at the end indicated by @side.
1471  *
1472  * Attaching widgets labeled [1], [2], [3] with @sibling == %NULL and
1473  * @side == %GTK_POS_LEFT yields a layout of [3][2][1].
1474  */
1475 void
1476 gtk_grid_attach_next_to (GtkGrid         *grid,
1477                          GtkWidget       *child,
1478                          GtkWidget       *sibling,
1479                          GtkPositionType  side,
1480                          gint             width,
1481                          gint             height)
1482 {
1483   GtkGridChild *grid_sibling;
1484   gint left, top;
1485
1486   g_return_if_fail (GTK_IS_GRID (grid));
1487   g_return_if_fail (GTK_IS_WIDGET (child));
1488   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
1489   g_return_if_fail (sibling == NULL || gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
1490   g_return_if_fail (width > 0);
1491   g_return_if_fail (height > 0);
1492
1493   if (sibling)
1494     {
1495       grid_sibling = find_grid_child (grid, sibling);
1496
1497       switch (side)
1498         {
1499         case GTK_POS_LEFT:
1500           left = CHILD_LEFT (grid_sibling) - width;
1501           top = CHILD_TOP (grid_sibling);
1502           break;
1503         case GTK_POS_RIGHT:
1504           left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling);
1505           top = CHILD_TOP (grid_sibling);
1506           break;
1507         case GTK_POS_TOP:
1508           left = CHILD_LEFT (grid_sibling);
1509           top = CHILD_TOP (grid_sibling) - height;
1510           break;
1511         case GTK_POS_BOTTOM:
1512           left = CHILD_LEFT (grid_sibling);
1513           top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling);
1514           break;
1515         default:
1516           g_assert_not_reached ();
1517         }
1518     }
1519   else
1520     {
1521       switch (side)
1522         {
1523         case GTK_POS_LEFT:
1524           left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, FALSE);
1525           left -= width;
1526           top = 0;
1527           break;
1528         case GTK_POS_RIGHT:
1529           left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, TRUE);
1530           top = 0;
1531           break;
1532         case GTK_POS_TOP:
1533           left = 0;
1534           top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, FALSE);
1535           top -= height;
1536           break;
1537         case GTK_POS_BOTTOM:
1538           left = 0;
1539           top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, TRUE);
1540           break;
1541         default:
1542           g_assert_not_reached ();
1543         }
1544     }
1545
1546   grid_attach (grid, child, left, top, width, height);
1547 }
1548
1549 /**
1550  * gtk_grid_get_child_at:
1551  * @grid: a #GtkGrid
1552  * @left: the left edge of the cell
1553  * @top: the top edge of the cell
1554  *
1555  * Gets the child of @grid whose area covers the grid
1556  * cell whose upper left corner is at @left, @top.
1557  *
1558  * Returns: the child at the given position, or %NULL
1559  *
1560  * Since: 3.2
1561  */
1562 GtkWidget *
1563 gtk_grid_get_child_at (GtkGrid *grid,
1564                        gint     left,
1565                        gint     top)
1566 {
1567   GtkGridPrivate *priv = grid->priv;
1568   GtkGridChild *child;
1569   GList *list;
1570
1571   for (list = priv->children; list; list = list->next)
1572     {
1573       child = list->data;
1574
1575       if (CHILD_LEFT (child) <= left &&
1576           CHILD_LEFT (child) + CHILD_WIDTH (child) > left &&
1577           CHILD_TOP (child) <= top &&
1578           CHILD_TOP (child) + CHILD_HEIGHT (child) > top)
1579         return child->widget;
1580     }
1581
1582   return NULL;
1583 }
1584
1585 /**
1586  * gtk_grid_insert_row:
1587  * @grid: a #GtkGrid
1588  * @position: the position to insert the row at
1589  *
1590  * Inserts a row at the specified position.
1591  *
1592  * Children which are attached at or below this position
1593  * are moved one row down. Children which span across this
1594  * position are grown to span the new row.
1595  *
1596  * Since: 3.2
1597  */
1598 void
1599 gtk_grid_insert_row (GtkGrid *grid,
1600                      gint     position)
1601 {
1602   GtkGridPrivate *priv = grid->priv;
1603   GtkGridChild *child;
1604   GList *list;
1605   gint top, height;
1606
1607   g_return_if_fail (GTK_IS_GRID (grid));
1608
1609   for (list = priv->children; list; list = list->next)
1610     {
1611       child = list->data;
1612
1613       top = CHILD_TOP (child);
1614       height = CHILD_HEIGHT (child);
1615
1616       if (top >= position)
1617         {
1618           CHILD_TOP (child) = top + 1;
1619           gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "top-attach");
1620         }
1621       else if (top + height > position)
1622         {
1623           CHILD_HEIGHT (child) = height + 1;
1624           gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "height");
1625         }
1626     }
1627 }
1628
1629 /**
1630  * gtk_grid_insert_column:
1631  * @grid: a #GtkGrid
1632  * @position: the position to insert the column at
1633  *
1634  * Inserts a column at the specified position.
1635  *
1636  * Children which are attached at or to the right of this position
1637  * are moved one column to the right. Children which span across this
1638  * position are grown to span the new column.
1639  *
1640  * Since: 3.2
1641  */
1642 void
1643 gtk_grid_insert_column (GtkGrid *grid,
1644                         gint     position)
1645 {
1646   GtkGridPrivate *priv = grid->priv;
1647   GtkGridChild *child;
1648   GList *list;
1649   gint left, width;
1650
1651   g_return_if_fail (GTK_IS_GRID (grid));
1652
1653   for (list = priv->children; list; list = list->next)
1654     {
1655       child = list->data;
1656
1657       left = CHILD_LEFT (child);
1658       width = CHILD_WIDTH (child);
1659
1660       if (left >= position)
1661         {
1662           CHILD_LEFT (child) = left + 1;
1663           gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "left-attach");
1664         }
1665       else if (left + width > position)
1666         {
1667           CHILD_WIDTH (child) = width + 1;
1668           gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "width");
1669         }
1670     }
1671 }
1672
1673 /**
1674  * gtk_grid_insert_next_to:
1675  * @grid: a #GtkGrid
1676  * @sibling: the child of @grid that the new row or column will be
1677  *     placed next to
1678  * @side: the side of @sibling that @child is positioned next to
1679  *
1680  * Inserts a row or column at the specified position.
1681  *
1682  * The new row or column is placed next to @sibling, on the side
1683  * determined by @side. If @side is %GTK_POS_TOP or %GTK_POS_BOTTOM,
1684  * a row is inserted. If @side is %GTK_POS_LEFT of %GTK_POS_RIGHT,
1685  * a column is inserted.
1686  *
1687  * Since: 3.2
1688  */
1689 void
1690 gtk_grid_insert_next_to (GtkGrid         *grid,
1691                          GtkWidget       *sibling,
1692                          GtkPositionType  side)
1693 {
1694   GtkGridChild *child;
1695
1696   g_return_if_fail (GTK_IS_GRID (grid));
1697   g_return_if_fail (GTK_IS_WIDGET (sibling));
1698   g_return_if_fail (gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
1699
1700   child = find_grid_child (grid, sibling);
1701
1702   switch (side)
1703     {
1704     case GTK_POS_LEFT:
1705       gtk_grid_insert_column (grid, CHILD_LEFT (child));
1706       break;
1707     case GTK_POS_RIGHT:
1708       gtk_grid_insert_column (grid, CHILD_LEFT (child) + CHILD_WIDTH (child));
1709       break;
1710     case GTK_POS_TOP:
1711       gtk_grid_insert_row (grid, CHILD_TOP (child));
1712       break;
1713     case GTK_POS_BOTTOM:
1714       gtk_grid_insert_row (grid, CHILD_TOP (child) + CHILD_HEIGHT (child));
1715       break;
1716     default:
1717       g_assert_not_reached ();
1718     }
1719 }
1720
1721 /**
1722  * gtk_grid_set_row_homogeneous:
1723  * @grid: a #GtkGrid
1724  * @homogeneous: %TRUE to make rows homogeneous
1725  *
1726  * Sets whether all rows of @grid will have the same height.
1727  */
1728 void
1729 gtk_grid_set_row_homogeneous (GtkGrid  *grid,
1730                               gboolean  homogeneous)
1731 {
1732   GtkGridPrivate *priv;
1733   g_return_if_fail (GTK_IS_GRID (grid));
1734
1735   priv = grid->priv;
1736
1737   /* Yes, homogeneous rows means all the columns have the same size */
1738   if (COLUMNS (priv)->homogeneous != homogeneous)
1739     {
1740       COLUMNS (priv)->homogeneous = homogeneous;
1741
1742       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1743         gtk_widget_queue_resize (GTK_WIDGET (grid));
1744
1745       g_object_notify (G_OBJECT (grid), "row-homogeneous");
1746     }
1747 }
1748
1749 /**
1750  * gtk_grid_get_row_homogeneous:
1751  * @grid: a #GtkGrid
1752  *
1753  * Returns whether all rows of @grid have the same height.
1754  *
1755  * Returns: whether all rows of @grid have the same height.
1756  */
1757 gboolean
1758 gtk_grid_get_row_homogeneous (GtkGrid *grid)
1759 {
1760   GtkGridPrivate *priv;
1761   g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
1762
1763   priv = grid->priv;
1764
1765   return COLUMNS (priv)->homogeneous;
1766 }
1767
1768 /**
1769  * gtk_grid_set_column_homogeneous:
1770  * @grid: a #GtkGrid
1771  * @homogeneous: %TRUE to make columns homogeneous
1772  *
1773  * Sets whether all columns of @grid will have the same width.
1774  */
1775 void
1776 gtk_grid_set_column_homogeneous (GtkGrid  *grid,
1777                                  gboolean  homogeneous)
1778 {
1779   GtkGridPrivate *priv;
1780   g_return_if_fail (GTK_IS_GRID (grid));
1781
1782   priv = grid->priv;
1783
1784   /* Yes, homogeneous columns means all the rows have the same size */
1785   if (ROWS (priv)->homogeneous != homogeneous)
1786     {
1787       ROWS (priv)->homogeneous = homogeneous;
1788
1789       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1790         gtk_widget_queue_resize (GTK_WIDGET (grid));
1791
1792       g_object_notify (G_OBJECT (grid), "column-homogeneous");
1793     }
1794 }
1795
1796 /**
1797  * gtk_grid_get_column_homogeneous:
1798  * @grid: a #GtkGrid
1799  *
1800  * Returns whether all columns of @grid have the same width.
1801  *
1802  * Returns: whether all columns of @grid have the same width.
1803  */
1804 gboolean
1805 gtk_grid_get_column_homogeneous (GtkGrid *grid)
1806 {
1807   GtkGridPrivate *priv;
1808   g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
1809
1810   priv = grid->priv;
1811
1812   return ROWS (priv)->homogeneous;
1813 }
1814
1815 /**
1816  * gtk_grid_set_row_spacing:
1817  * @grid: a #GtkGrid
1818  * @spacing: the amount of space to insert between rows
1819  *
1820  * Sets the amount of space between rows of @grid.
1821  */
1822 void
1823 gtk_grid_set_row_spacing (GtkGrid *grid,
1824                           guint    spacing)
1825 {
1826   GtkGridPrivate *priv;
1827   g_return_if_fail (GTK_IS_GRID (grid));
1828   g_return_if_fail (spacing <= G_MAXINT16);
1829
1830   priv = grid->priv;
1831
1832   if (COLUMNS (priv)->spacing != spacing)
1833     {
1834       COLUMNS (priv)->spacing = spacing;
1835
1836       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1837         gtk_widget_queue_resize (GTK_WIDGET (grid));
1838
1839       g_object_notify (G_OBJECT (grid), "row-spacing");
1840     }
1841 }
1842
1843 /**
1844  * gtk_grid_get_row_spacing:
1845  * @grid: a #GtkGrid
1846  *
1847  * Returns the amount of space between the rows of @grid.
1848  *
1849  * Returns: the row spacing of @grid
1850  */
1851 guint
1852 gtk_grid_get_row_spacing (GtkGrid *grid)
1853 {
1854   GtkGridPrivate *priv;
1855   g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1856
1857   priv = grid->priv;
1858
1859   return COLUMNS (priv)->spacing;
1860 }
1861
1862 /**
1863  * gtk_grid_set_column_spacing:
1864  * @grid: a #GtkGrid
1865  * @spacing: the amount of space to insert between columns
1866  *
1867  * Sets the amount of space between columns of @grid.
1868  */
1869 void
1870 gtk_grid_set_column_spacing (GtkGrid *grid,
1871                              guint    spacing)
1872 {
1873   GtkGridPrivate *priv;
1874   g_return_if_fail (GTK_IS_GRID (grid));
1875   g_return_if_fail (spacing <= G_MAXINT16);
1876
1877   priv = grid->priv;
1878
1879   if (ROWS (priv)->spacing != spacing)
1880     {
1881       ROWS (priv)->spacing = spacing;
1882
1883       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1884         gtk_widget_queue_resize (GTK_WIDGET (grid));
1885
1886       g_object_notify (G_OBJECT (grid), "column-spacing");
1887     }
1888 }
1889
1890 /**
1891  * gtk_grid_get_column_spacing:
1892  * @grid: a #GtkGrid
1893  *
1894  * Returns the amount of space between the columns of @grid.
1895  *
1896  * Returns: the column spacing of @grid
1897  */
1898 guint
1899 gtk_grid_get_column_spacing (GtkGrid *grid)
1900 {
1901   GtkGridPrivate *priv;
1902
1903   g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1904
1905   priv = grid->priv;
1906
1907   return ROWS (priv)->spacing;
1908 }