]> Pileus Git - ~andy/gtk/blob - gtk/gtkgrid.c
Updated Russian translation
[~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        * When doing homogeneous allocation though, try to keep the
775        * line allocations even, since we're going to force them to
776        * be the same anyway, and we don't want to introduce unnecessary
777        * extra space.
778        */
779       if (span_minimum < minimum)
780         {
781           if (linedata->homogeneous)
782             {
783               gint total, m;
784
785               total = minimum - (attach->span - 1) * linedata->spacing;
786               m = total / attach->span + (total % attach->span ? 1 : 0);
787               for (i = 0; i < attach->span; i++)
788                 {
789                   line = &lines->lines[attach->pos - lines->min + i];
790                   line->minimum = MAX(line->minimum, m);
791                 }
792             }
793           else
794             {
795               extra = minimum - span_minimum;
796               expand = span_expand;
797               for (i = 0; i < attach->span; i++)
798                 {
799                   line = &lines->lines[attach->pos - lines->min + i];
800                   if (force_expand || line->expand)
801                     {
802                       line_extra = extra / expand;
803                       line->minimum += line_extra;
804                       extra -= line_extra;
805                       expand -= 1;
806                     }
807                 }
808             }
809         }
810
811       if (span_natural < natural)
812         {
813           if (linedata->homogeneous)
814             {
815               gint total, n;
816
817               total = natural - (attach->span - 1) * linedata->spacing;
818               n = total / attach->span + (total % attach->span ? 1 : 0);
819               for (i = 0; i < attach->span; i++)
820                 {
821                   line = &lines->lines[attach->pos - lines->min + i];
822                   line->natural = MAX(line->natural, n);
823                 }
824             }
825           else
826             {
827               extra = natural - span_natural;
828               expand = span_expand;
829               for (i = 0; i < attach->span; i++)
830                 {
831                   line = &lines->lines[attach->pos - lines->min + i];
832                   if (force_expand || line->expand)
833                     {
834                       line_extra = extra / expand;
835                       line->natural += line_extra;
836                       extra -= line_extra;
837                       expand -= 1;
838                     }
839                 }
840             }
841         }
842     }
843 }
844
845 /* Marks empty and expanding lines and counts them.
846  */
847 static void
848 gtk_grid_request_compute_expand (GtkGridRequest *request,
849                                  GtkOrientation  orientation,
850                                  gint           *nonempty_lines,
851                                  gint           *expand_lines)
852 {
853   GtkGridPrivate *priv = request->grid->priv;
854   GtkGridChild *child;
855   GtkGridChildAttach *attach;
856   GList *list;
857   gint i;
858   GtkGridLines *lines;
859   GtkGridLine *line;
860   gboolean has_expand;
861   gint expand;
862   gint empty;
863
864   lines = &request->lines[orientation];
865
866   for (i = 0; i < lines->max - lines->min; i++)
867     {
868       lines->lines[i].need_expand = FALSE;
869       lines->lines[i].expand = FALSE;
870       lines->lines[i].empty = TRUE;
871     }
872
873   for (list = priv->children; list; list = list->next)
874     {
875       child = list->data;
876
877       if (!gtk_widget_get_visible (child->widget))
878         continue;
879
880       attach = &child->attach[orientation];
881       if (attach->span != 1)
882         continue;
883
884       line = &lines->lines[attach->pos - lines->min];
885       line->empty = FALSE;
886       if (gtk_widget_compute_expand (child->widget, orientation))
887         line->expand = TRUE;
888     }
889
890   for (list = priv->children; list; list = list->next)
891     {
892       child = list->data;
893
894       if (!gtk_widget_get_visible (child->widget))
895         continue;
896
897       attach = &child->attach[orientation];
898       if (attach->span == 1)
899         continue;
900
901       has_expand = FALSE;
902       for (i = 0; i < attach->span; i++)
903         {
904           line = &lines->lines[attach->pos - lines->min + i];
905           line->empty = FALSE;
906           if (line->expand)
907             has_expand = TRUE;
908         }
909
910       if (!has_expand && gtk_widget_compute_expand (child->widget, orientation))
911         {
912           for (i = 0; i < attach->span; i++)
913             {
914               line = &lines->lines[attach->pos - lines->min + i];
915               line->need_expand = TRUE;
916             }
917         }
918     }
919
920   empty = 0;
921   expand = 0;
922   for (i = 0; i < lines->max - lines->min; i++)
923     {
924       line = &lines->lines[i];
925
926       if (line->need_expand)
927         line->expand = TRUE;
928
929       if (line->empty)
930         empty += 1;
931
932       if (line->expand)
933         expand += 1;
934     }
935
936   if (nonempty_lines)
937     *nonempty_lines = lines->max - lines->min - empty;
938
939   if (expand_lines)
940     *expand_lines = expand;
941 }
942
943 /* Sums the minimum and natural fields of lines and their spacing.
944  */
945 static void
946 gtk_grid_request_sum (GtkGridRequest *request,
947                       GtkOrientation  orientation,
948                       gint           *minimum,
949                       gint           *natural)
950 {
951   GtkGridPrivate *priv = request->grid->priv;
952   GtkGridLineData *linedata;
953   GtkGridLines *lines;
954   gint i;
955   gint min, nat;
956   gint nonempty;
957
958   gtk_grid_request_compute_expand (request, orientation, &nonempty, NULL);
959
960   linedata = &priv->linedata[orientation];
961   lines = &request->lines[orientation];
962
963   min = 0;
964   nat = 0;
965   if (nonempty > 0)
966     {
967       min = (nonempty - 1) * linedata->spacing;
968       nat = (nonempty - 1) * linedata->spacing;
969     }
970
971   for (i = 0; i < lines->max - lines->min; i++)
972     {
973       min += lines->lines[i].minimum;
974       nat += lines->lines[i].natural;
975     }
976
977   if (minimum)
978     *minimum = min;
979
980   if (natural)
981     *natural = nat;
982 }
983
984 /* Computes minimum and natural fields of lines.
985  * When contextual is TRUE, requires allocation of
986  * lines in the opposite orientation to be set.
987  */
988 static void
989 gtk_grid_request_run (GtkGridRequest *request,
990                       GtkOrientation  orientation,
991                       gboolean        contextual)
992 {
993   gtk_grid_request_init (request, orientation);
994   gtk_grid_request_non_spanning (request, orientation, contextual);
995   gtk_grid_request_homogeneous (request, orientation);
996   gtk_grid_request_spanning (request, orientation, contextual);
997   gtk_grid_request_homogeneous (request, orientation);
998 }
999
1000 /* Requires that the minimum and natural fields of lines
1001  * have been set, computes the allocation field of lines
1002  * by distributing total_size among lines.
1003  */
1004 static void
1005 gtk_grid_request_allocate (GtkGridRequest *request,
1006                            GtkOrientation  orientation,
1007                            gint            total_size)
1008 {
1009   GtkGridPrivate *priv = request->grid->priv;
1010   GtkGridLineData *linedata;
1011   GtkGridLines *lines;
1012   GtkGridLine *line;
1013   gint nonempty;
1014   gint expand;
1015   gint i, j;
1016   GtkRequestedSize *sizes;
1017   gint extra;
1018   gint rest;
1019   gint size;
1020
1021   gtk_grid_request_compute_expand (request, orientation, &nonempty, &expand);
1022
1023   linedata = &priv->linedata[orientation];
1024   lines = &request->lines[orientation];
1025
1026   size = total_size - (nonempty - 1) * linedata->spacing;
1027
1028   if (linedata->homogeneous)
1029     {
1030       extra = size / nonempty;
1031       rest = size % nonempty;
1032
1033       for (i = 0; i < lines->max - lines->min; i++)
1034         {
1035           line = &lines->lines[i];
1036           if (line->empty)
1037             continue;
1038
1039           line->allocation = extra;
1040           if (rest > 0)
1041             {
1042               line->allocation += 1;
1043               rest -= 1;
1044             }
1045         }
1046     }
1047   else
1048     {
1049       sizes = g_newa (GtkRequestedSize, nonempty);
1050
1051       j = 0;
1052       for (i = 0; i < lines->max - lines->min; i++)
1053         {
1054           line = &lines->lines[i];
1055           if (line->empty)
1056             continue;
1057
1058           size -= line->minimum;
1059
1060           sizes[j].minimum_size = line->minimum;
1061           sizes[j].natural_size = line->natural;
1062           sizes[j].data = line;
1063           j++;
1064         }
1065
1066       size = gtk_distribute_natural_allocation (MAX (0, size), nonempty, sizes);
1067
1068       if (expand > 0)
1069         {
1070           extra = size / expand;
1071           rest = size % expand;
1072         }
1073       else
1074         {
1075           extra = 0;
1076           rest = 0;
1077         }
1078
1079       j = 0;
1080       for (i = 0; i < lines->max - lines->min; i++)
1081         {
1082           line = &lines->lines[i];
1083           if (line->empty)
1084             continue;
1085
1086           g_assert (line == sizes[j].data);
1087
1088           line->allocation = sizes[j].minimum_size;
1089           if (line->expand)
1090             {
1091               line->allocation += extra;
1092               if (rest > 0)
1093                 {
1094                   line->allocation += 1;
1095                   rest -= 1;
1096                 }
1097             }
1098
1099           j++;
1100         }
1101     }
1102 }
1103
1104 /* Computes the position fields from allocation and spacing.
1105  */
1106 static void
1107 gtk_grid_request_position (GtkGridRequest *request,
1108                            GtkOrientation  orientation)
1109 {
1110   GtkGridPrivate *priv = request->grid->priv;
1111   GtkGridLineData *linedata;
1112   GtkGridLines *lines;
1113   GtkGridLine *line;
1114   gint position;
1115   gint i;
1116
1117   linedata = &priv->linedata[orientation];
1118   lines = &request->lines[orientation];
1119
1120   position = 0;
1121   for (i = 0; i < lines->max - lines->min; i++)
1122     {
1123       line = &lines->lines[i];
1124       if (!line->empty)
1125         {
1126           line->position = position;
1127           position += line->allocation + linedata->spacing;
1128         }
1129     }
1130 }
1131
1132 static void
1133 gtk_grid_get_size (GtkGrid        *grid,
1134                    GtkOrientation  orientation,
1135                    gint           *minimum,
1136                    gint           *natural)
1137 {
1138   GtkGridRequest request;
1139   GtkGridLines *lines;
1140
1141   if (minimum)
1142     *minimum = 0;
1143
1144   if (natural)
1145     *natural = 0;
1146
1147   if (grid->priv->children == NULL)
1148     return;
1149
1150   request.grid = grid;
1151   gtk_grid_request_count_lines (&request);
1152   lines = &request.lines[orientation];
1153   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1154   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1155
1156   gtk_grid_request_run (&request, orientation, FALSE);
1157   gtk_grid_request_sum (&request, orientation, minimum, natural);
1158 }
1159
1160 static void
1161 gtk_grid_get_size_for_size (GtkGrid        *grid,
1162                             GtkOrientation  orientation,
1163                             gint            size,
1164                             gint           *minimum,
1165                             gint           *natural)
1166 {
1167   GtkGridRequest request;
1168   GtkGridLines *lines;
1169   gint min_size;
1170
1171   if (minimum)
1172     *minimum = 0;
1173
1174   if (natural)
1175     *natural = 0;
1176
1177   if (grid->priv->children == NULL)
1178     return;
1179
1180   request.grid = grid;
1181   gtk_grid_request_count_lines (&request);
1182   lines = &request.lines[0];
1183   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1184   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1185   lines = &request.lines[1];
1186   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1187   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1188
1189   gtk_grid_request_run (&request, 1 - orientation, FALSE);
1190   gtk_grid_request_sum (&request, 1 - orientation, &min_size, NULL);
1191   gtk_grid_request_allocate (&request, 1 - orientation, MAX (size, min_size));
1192
1193   gtk_grid_request_run (&request, orientation, TRUE);
1194   gtk_grid_request_sum (&request, orientation, minimum, natural);
1195 }
1196
1197 static void
1198 gtk_grid_get_preferred_width (GtkWidget *widget,
1199                               gint      *minimum,
1200                               gint      *natural)
1201 {
1202   GtkGrid *grid = GTK_GRID (widget);
1203
1204   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1205     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, 0, minimum, natural);
1206   else
1207     gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
1208 }
1209
1210 static void
1211 gtk_grid_get_preferred_height (GtkWidget *widget,
1212                                gint      *minimum,
1213                                gint      *natural)
1214 {
1215   GtkGrid *grid = GTK_GRID (widget);
1216
1217   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1218     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, 0, minimum, natural);
1219   else
1220     gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
1221 }
1222
1223 static void
1224 gtk_grid_get_preferred_width_for_height (GtkWidget *widget,
1225                                          gint       height,
1226                                          gint      *minimum,
1227                                          gint      *natural)
1228 {
1229   GtkGrid *grid = GTK_GRID (widget);
1230
1231   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1232     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
1233   else
1234     gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
1235 }
1236
1237 static void
1238 gtk_grid_get_preferred_height_for_width (GtkWidget *widget,
1239                                          gint       width,
1240                                          gint      *minimum,
1241                                          gint      *natural)
1242 {
1243   GtkGrid *grid = GTK_GRID (widget);
1244
1245   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1246     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
1247   else
1248     gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
1249 }
1250
1251 static void
1252 allocate_child (GtkGridRequest *request,
1253                 GtkOrientation  orientation,
1254                 GtkGridChild   *child,
1255                 gint           *position,
1256                 gint           *size)
1257 {
1258   GtkGridPrivate *priv = request->grid->priv;
1259   GtkGridLineData *linedata;
1260   GtkGridLines *lines;
1261   GtkGridLine *line;
1262   GtkGridChildAttach *attach;
1263   gint i;
1264
1265   linedata = &priv->linedata[orientation];
1266   lines = &request->lines[orientation];
1267   attach = &child->attach[orientation];
1268
1269   *position = lines->lines[attach->pos - lines->min].position;
1270
1271   *size = (attach->span - 1) * linedata->spacing;
1272   for (i = 0; i < attach->span; i++)
1273     {
1274       line = &lines->lines[attach->pos - lines->min + i];
1275       *size += line->allocation;
1276     }
1277 }
1278
1279 static void
1280 gtk_grid_request_allocate_children (GtkGridRequest *request)
1281 {
1282   GtkGridPrivate *priv = request->grid->priv;
1283   GList *list;
1284   GtkGridChild *child;
1285   GtkAllocation allocation;
1286   GtkAllocation child_allocation;
1287   gint x, y, width, height;
1288
1289   gtk_widget_get_allocation (GTK_WIDGET (request->grid), &allocation);
1290
1291   for (list = priv->children; list; list = list->next)
1292     {
1293       child = list->data;
1294
1295       if (!gtk_widget_get_visible (child->widget))
1296         continue;
1297
1298       allocate_child (request, GTK_ORIENTATION_HORIZONTAL, child, &x, &width);
1299       allocate_child (request, GTK_ORIENTATION_VERTICAL, child, &y, &height);
1300
1301       child_allocation.x = allocation.x + x;
1302       child_allocation.y = allocation.y + y;
1303       child_allocation.width = MAX (1, width);
1304       child_allocation.height = MAX (1, height);
1305
1306       if (gtk_widget_get_direction (GTK_WIDGET (request->grid)) == GTK_TEXT_DIR_RTL)
1307         child_allocation.x = allocation.x + allocation.width
1308                              - (child_allocation.x - allocation.x) - child_allocation.width;
1309
1310       gtk_widget_size_allocate (child->widget, &child_allocation);
1311     }
1312 }
1313
1314 #define GET_SIZE(allocation, orientation) (orientation == GTK_ORIENTATION_HORIZONTAL ? allocation->width : allocation->height)
1315
1316 static void
1317 gtk_grid_size_allocate (GtkWidget     *widget,
1318                         GtkAllocation *allocation)
1319 {
1320   GtkGrid *grid = GTK_GRID (widget);
1321   GtkGridPrivate *priv = grid->priv;
1322   GtkGridRequest request;
1323   GtkGridLines *lines;
1324   GtkOrientation orientation;
1325
1326   if (priv->children == NULL)
1327     {
1328       gtk_widget_set_allocation (widget, allocation);
1329       return;
1330     }
1331
1332   request.grid = grid;
1333
1334   gtk_grid_request_count_lines (&request);
1335   lines = &request.lines[0];
1336   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1337   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1338   lines = &request.lines[1];
1339   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1340   memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1341
1342   gtk_widget_set_allocation (widget, allocation);
1343
1344   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1345     orientation = GTK_ORIENTATION_HORIZONTAL;
1346   else
1347     orientation = GTK_ORIENTATION_VERTICAL;
1348
1349   gtk_grid_request_run (&request, 1 - orientation, FALSE);
1350   gtk_grid_request_allocate (&request, 1 - orientation, GET_SIZE (allocation, 1 - orientation));
1351   gtk_grid_request_run (&request, orientation, TRUE);
1352   gtk_grid_request_allocate (&request, orientation, GET_SIZE (allocation, orientation));
1353
1354   gtk_grid_request_position (&request, 0);
1355   gtk_grid_request_position (&request, 1);
1356
1357   gtk_grid_request_allocate_children (&request);
1358 }
1359
1360 static void
1361 gtk_grid_class_init (GtkGridClass *class)
1362 {
1363   GObjectClass *object_class = G_OBJECT_CLASS (class);
1364   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1365   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
1366
1367   object_class->get_property = gtk_grid_get_property;
1368   object_class->set_property = gtk_grid_set_property;
1369
1370   widget_class->size_allocate = gtk_grid_size_allocate;
1371   widget_class->get_preferred_width = gtk_grid_get_preferred_width;
1372   widget_class->get_preferred_height = gtk_grid_get_preferred_height;
1373   widget_class->get_preferred_width_for_height = gtk_grid_get_preferred_width_for_height;
1374   widget_class->get_preferred_height_for_width = gtk_grid_get_preferred_height_for_width;
1375
1376   container_class->add = gtk_grid_add;
1377   container_class->remove = gtk_grid_remove;
1378   container_class->forall = gtk_grid_forall;
1379   container_class->child_type = gtk_grid_child_type;
1380   container_class->set_child_property = gtk_grid_set_child_property;
1381   container_class->get_child_property = gtk_grid_get_child_property;
1382   gtk_container_class_handle_border_width (container_class);
1383
1384   g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
1385
1386   g_object_class_install_property (object_class, PROP_ROW_SPACING,
1387     g_param_spec_int ("row-spacing",
1388                       P_("Row spacing"),
1389                       P_("The amount of space between two consecutive rows"),
1390                       0, G_MAXINT16, 0,
1391                       GTK_PARAM_READWRITE));
1392
1393   g_object_class_install_property (object_class, PROP_COLUMN_SPACING,
1394     g_param_spec_int ("column-spacing",
1395                       P_("Column spacing"),
1396                       P_("The amount of space between two consecutive columns"),
1397                       0, G_MAXINT16, 0,
1398                       GTK_PARAM_READWRITE));
1399
1400   g_object_class_install_property (object_class, PROP_ROW_HOMOGENEOUS,
1401     g_param_spec_boolean ("row-homogeneous",
1402                           P_("Row Homogeneous"),
1403                           P_("If TRUE, the rows are all the same height"),
1404                           FALSE,
1405                           GTK_PARAM_READWRITE));
1406
1407   g_object_class_install_property (object_class, PROP_COLUMN_HOMOGENEOUS,
1408     g_param_spec_boolean ("column-homogeneous",
1409                           P_("Column Homogeneous"),
1410                           P_("If TRUE, the columns are all the same width"),
1411                           FALSE,
1412                           GTK_PARAM_READWRITE));
1413
1414   gtk_container_class_install_child_property (container_class, CHILD_PROP_LEFT_ATTACH,
1415     g_param_spec_int ("left-attach",
1416                       P_("Left attachment"),
1417                       P_("The column number to attach the left side of the child to"),
1418                       G_MININT, G_MAXINT, 0,
1419                       GTK_PARAM_READWRITE));
1420
1421   gtk_container_class_install_child_property (container_class, CHILD_PROP_TOP_ATTACH,
1422     g_param_spec_int ("top-attach",
1423                       P_("Top attachment"),
1424                       P_("The row number to attach the top side of a child widget to"),
1425                       G_MININT, G_MAXINT, 0,
1426                       GTK_PARAM_READWRITE));
1427
1428   gtk_container_class_install_child_property (container_class, CHILD_PROP_WIDTH,
1429     g_param_spec_int ("width",
1430                       P_("Width"),
1431                       P_("The number of columns that a child spans"),
1432                       1, G_MAXINT, 1,
1433                       GTK_PARAM_READWRITE));
1434
1435   gtk_container_class_install_child_property (container_class, CHILD_PROP_HEIGHT,
1436     g_param_spec_int ("height",
1437                       P_("Height"),
1438                       P_("The number of rows that a child spans"),
1439                       1, G_MAXINT, 1,
1440                       GTK_PARAM_READWRITE));
1441
1442   g_type_class_add_private (class, sizeof (GtkGridPrivate));
1443 }
1444
1445 /**
1446  * gtk_grid_new:
1447  *
1448  * Creates a new grid widget.
1449  *
1450  * Returns: the new #GtkGrid
1451  */
1452 GtkWidget *
1453 gtk_grid_new (void)
1454 {
1455   return g_object_new (GTK_TYPE_GRID, NULL);
1456 }
1457
1458 /**
1459  * gtk_grid_attach:
1460  * @grid: a #GtkGrid
1461  * @child: the widget to add
1462  * @left: the column number to attach the left side of @child to
1463  * @top: the row number to attach the top side of @child to
1464  * @width: the number of columns that @child will span
1465  * @height: the number of rows that @child will span
1466  *
1467  * Adds a widget to the grid.
1468  *
1469  * The position of @child is determined by @left and @top. The
1470  * number of 'cells' that @child will occupy is determined by
1471  * @width and @height.
1472  */
1473 void
1474 gtk_grid_attach (GtkGrid   *grid,
1475                  GtkWidget *child,
1476                  gint       left,
1477                  gint       top,
1478                  gint       width,
1479                  gint       height)
1480 {
1481   g_return_if_fail (GTK_IS_GRID (grid));
1482   g_return_if_fail (GTK_IS_WIDGET (child));
1483   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
1484   g_return_if_fail (width > 0);
1485   g_return_if_fail (height > 0);
1486
1487   grid_attach (grid, child, left, top, width, height);
1488 }
1489
1490 /**
1491  * gtk_grid_attach_next_to:
1492  * @grid: a #GtkGrid
1493  * @child: the widget to add
1494  * @sibling: (allow-none): the child of @grid that @child will be placed
1495  *     next to, or %NULL to place @child at the beginning or end
1496  * @side: the side of @sibling that @child is positioned next to
1497  * @width: the number of columns that @child will span
1498  * @height: the number of rows that @child will span
1499  *
1500  * Adds a widget to the grid.
1501  *
1502  * The widget is placed next to @sibling, on the side determined by
1503  * @side. When @sibling is %NULL, the widget is placed in row (for
1504  * left or right placement) or column 0 (for top or bottom placement),
1505  * at the end indicated by @side.
1506  *
1507  * Attaching widgets labeled [1], [2], [3] with @sibling == %NULL and
1508  * @side == %GTK_POS_LEFT yields a layout of [3][2][1].
1509  */
1510 void
1511 gtk_grid_attach_next_to (GtkGrid         *grid,
1512                          GtkWidget       *child,
1513                          GtkWidget       *sibling,
1514                          GtkPositionType  side,
1515                          gint             width,
1516                          gint             height)
1517 {
1518   GtkGridChild *grid_sibling;
1519   gint left, top;
1520
1521   g_return_if_fail (GTK_IS_GRID (grid));
1522   g_return_if_fail (GTK_IS_WIDGET (child));
1523   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
1524   g_return_if_fail (sibling == NULL || gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
1525   g_return_if_fail (width > 0);
1526   g_return_if_fail (height > 0);
1527
1528   if (sibling)
1529     {
1530       grid_sibling = find_grid_child (grid, sibling);
1531
1532       switch (side)
1533         {
1534         case GTK_POS_LEFT:
1535           left = CHILD_LEFT (grid_sibling) - width;
1536           top = CHILD_TOP (grid_sibling);
1537           break;
1538         case GTK_POS_RIGHT:
1539           left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling);
1540           top = CHILD_TOP (grid_sibling);
1541           break;
1542         case GTK_POS_TOP:
1543           left = CHILD_LEFT (grid_sibling);
1544           top = CHILD_TOP (grid_sibling) - height;
1545           break;
1546         case GTK_POS_BOTTOM:
1547           left = CHILD_LEFT (grid_sibling);
1548           top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling);
1549           break;
1550         default:
1551           g_assert_not_reached ();
1552         }
1553     }
1554   else
1555     {
1556       switch (side)
1557         {
1558         case GTK_POS_LEFT:
1559           left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, FALSE);
1560           left -= width;
1561           top = 0;
1562           break;
1563         case GTK_POS_RIGHT:
1564           left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, TRUE);
1565           top = 0;
1566           break;
1567         case GTK_POS_TOP:
1568           left = 0;
1569           top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, FALSE);
1570           top -= height;
1571           break;
1572         case GTK_POS_BOTTOM:
1573           left = 0;
1574           top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, TRUE);
1575           break;
1576         default:
1577           g_assert_not_reached ();
1578         }
1579     }
1580
1581   grid_attach (grid, child, left, top, width, height);
1582 }
1583
1584 /**
1585  * gtk_grid_get_child_at:
1586  * @grid: a #GtkGrid
1587  * @left: the left edge of the cell
1588  * @top: the top edge of the cell
1589  *
1590  * Gets the child of @grid whose area covers the grid
1591  * cell whose upper left corner is at @left, @top.
1592  *
1593  * Returns: the child at the given position, or %NULL
1594  *
1595  * Since: 3.2
1596  */
1597 GtkWidget *
1598 gtk_grid_get_child_at (GtkGrid *grid,
1599                        gint     left,
1600                        gint     top)
1601 {
1602   GtkGridPrivate *priv = grid->priv;
1603   GtkGridChild *child;
1604   GList *list;
1605
1606   for (list = priv->children; list; list = list->next)
1607     {
1608       child = list->data;
1609
1610       if (CHILD_LEFT (child) <= left &&
1611           CHILD_LEFT (child) + CHILD_WIDTH (child) > left &&
1612           CHILD_TOP (child) <= top &&
1613           CHILD_TOP (child) + CHILD_HEIGHT (child) > top)
1614         return child->widget;
1615     }
1616
1617   return NULL;
1618 }
1619
1620 /**
1621  * gtk_grid_insert_row:
1622  * @grid: a #GtkGrid
1623  * @position: the position to insert the row at
1624  *
1625  * Inserts a row at the specified position.
1626  *
1627  * Children which are attached at or below this position
1628  * are moved one row down. Children which span across this
1629  * position are grown to span the new row.
1630  *
1631  * Since: 3.2
1632  */
1633 void
1634 gtk_grid_insert_row (GtkGrid *grid,
1635                      gint     position)
1636 {
1637   GtkGridPrivate *priv = grid->priv;
1638   GtkGridChild *child;
1639   GList *list;
1640   gint top, height;
1641
1642   g_return_if_fail (GTK_IS_GRID (grid));
1643
1644   for (list = priv->children; list; list = list->next)
1645     {
1646       child = list->data;
1647
1648       top = CHILD_TOP (child);
1649       height = CHILD_HEIGHT (child);
1650
1651       if (top >= position)
1652         {
1653           CHILD_TOP (child) = top + 1;
1654           gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "top-attach");
1655         }
1656       else if (top + height > position)
1657         {
1658           CHILD_HEIGHT (child) = height + 1;
1659           gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "height");
1660         }
1661     }
1662 }
1663
1664 /**
1665  * gtk_grid_insert_column:
1666  * @grid: a #GtkGrid
1667  * @position: the position to insert the column at
1668  *
1669  * Inserts a column at the specified position.
1670  *
1671  * Children which are attached at or to the right of this position
1672  * are moved one column to the right. Children which span across this
1673  * position are grown to span the new column.
1674  *
1675  * Since: 3.2
1676  */
1677 void
1678 gtk_grid_insert_column (GtkGrid *grid,
1679                         gint     position)
1680 {
1681   GtkGridPrivate *priv = grid->priv;
1682   GtkGridChild *child;
1683   GList *list;
1684   gint left, width;
1685
1686   g_return_if_fail (GTK_IS_GRID (grid));
1687
1688   for (list = priv->children; list; list = list->next)
1689     {
1690       child = list->data;
1691
1692       left = CHILD_LEFT (child);
1693       width = CHILD_WIDTH (child);
1694
1695       if (left >= position)
1696         {
1697           CHILD_LEFT (child) = left + 1;
1698           gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "left-attach");
1699         }
1700       else if (left + width > position)
1701         {
1702           CHILD_WIDTH (child) = width + 1;
1703           gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "width");
1704         }
1705     }
1706 }
1707
1708 /**
1709  * gtk_grid_insert_next_to:
1710  * @grid: a #GtkGrid
1711  * @sibling: the child of @grid that the new row or column will be
1712  *     placed next to
1713  * @side: the side of @sibling that @child is positioned next to
1714  *
1715  * Inserts a row or column at the specified position.
1716  *
1717  * The new row or column is placed next to @sibling, on the side
1718  * determined by @side. If @side is %GTK_POS_TOP or %GTK_POS_BOTTOM,
1719  * a row is inserted. If @side is %GTK_POS_LEFT of %GTK_POS_RIGHT,
1720  * a column is inserted.
1721  *
1722  * Since: 3.2
1723  */
1724 void
1725 gtk_grid_insert_next_to (GtkGrid         *grid,
1726                          GtkWidget       *sibling,
1727                          GtkPositionType  side)
1728 {
1729   GtkGridChild *child;
1730
1731   g_return_if_fail (GTK_IS_GRID (grid));
1732   g_return_if_fail (GTK_IS_WIDGET (sibling));
1733   g_return_if_fail (gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
1734
1735   child = find_grid_child (grid, sibling);
1736
1737   switch (side)
1738     {
1739     case GTK_POS_LEFT:
1740       gtk_grid_insert_column (grid, CHILD_LEFT (child));
1741       break;
1742     case GTK_POS_RIGHT:
1743       gtk_grid_insert_column (grid, CHILD_LEFT (child) + CHILD_WIDTH (child));
1744       break;
1745     case GTK_POS_TOP:
1746       gtk_grid_insert_row (grid, CHILD_TOP (child));
1747       break;
1748     case GTK_POS_BOTTOM:
1749       gtk_grid_insert_row (grid, CHILD_TOP (child) + CHILD_HEIGHT (child));
1750       break;
1751     default:
1752       g_assert_not_reached ();
1753     }
1754 }
1755
1756 /**
1757  * gtk_grid_set_row_homogeneous:
1758  * @grid: a #GtkGrid
1759  * @homogeneous: %TRUE to make rows homogeneous
1760  *
1761  * Sets whether all rows of @grid will have the same height.
1762  */
1763 void
1764 gtk_grid_set_row_homogeneous (GtkGrid  *grid,
1765                               gboolean  homogeneous)
1766 {
1767   GtkGridPrivate *priv;
1768   g_return_if_fail (GTK_IS_GRID (grid));
1769
1770   priv = grid->priv;
1771
1772   /* Yes, homogeneous rows means all the columns have the same size */
1773   if (COLUMNS (priv)->homogeneous != homogeneous)
1774     {
1775       COLUMNS (priv)->homogeneous = homogeneous;
1776
1777       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1778         gtk_widget_queue_resize (GTK_WIDGET (grid));
1779
1780       g_object_notify (G_OBJECT (grid), "row-homogeneous");
1781     }
1782 }
1783
1784 /**
1785  * gtk_grid_get_row_homogeneous:
1786  * @grid: a #GtkGrid
1787  *
1788  * Returns whether all rows of @grid have the same height.
1789  *
1790  * Returns: whether all rows of @grid have the same height.
1791  */
1792 gboolean
1793 gtk_grid_get_row_homogeneous (GtkGrid *grid)
1794 {
1795   GtkGridPrivate *priv;
1796   g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
1797
1798   priv = grid->priv;
1799
1800   return COLUMNS (priv)->homogeneous;
1801 }
1802
1803 /**
1804  * gtk_grid_set_column_homogeneous:
1805  * @grid: a #GtkGrid
1806  * @homogeneous: %TRUE to make columns homogeneous
1807  *
1808  * Sets whether all columns of @grid will have the same width.
1809  */
1810 void
1811 gtk_grid_set_column_homogeneous (GtkGrid  *grid,
1812                                  gboolean  homogeneous)
1813 {
1814   GtkGridPrivate *priv;
1815   g_return_if_fail (GTK_IS_GRID (grid));
1816
1817   priv = grid->priv;
1818
1819   /* Yes, homogeneous columns means all the rows have the same size */
1820   if (ROWS (priv)->homogeneous != homogeneous)
1821     {
1822       ROWS (priv)->homogeneous = homogeneous;
1823
1824       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1825         gtk_widget_queue_resize (GTK_WIDGET (grid));
1826
1827       g_object_notify (G_OBJECT (grid), "column-homogeneous");
1828     }
1829 }
1830
1831 /**
1832  * gtk_grid_get_column_homogeneous:
1833  * @grid: a #GtkGrid
1834  *
1835  * Returns whether all columns of @grid have the same width.
1836  *
1837  * Returns: whether all columns of @grid have the same width.
1838  */
1839 gboolean
1840 gtk_grid_get_column_homogeneous (GtkGrid *grid)
1841 {
1842   GtkGridPrivate *priv;
1843   g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
1844
1845   priv = grid->priv;
1846
1847   return ROWS (priv)->homogeneous;
1848 }
1849
1850 /**
1851  * gtk_grid_set_row_spacing:
1852  * @grid: a #GtkGrid
1853  * @spacing: the amount of space to insert between rows
1854  *
1855  * Sets the amount of space between rows of @grid.
1856  */
1857 void
1858 gtk_grid_set_row_spacing (GtkGrid *grid,
1859                           guint    spacing)
1860 {
1861   GtkGridPrivate *priv;
1862   g_return_if_fail (GTK_IS_GRID (grid));
1863   g_return_if_fail (spacing <= G_MAXINT16);
1864
1865   priv = grid->priv;
1866
1867   if (COLUMNS (priv)->spacing != spacing)
1868     {
1869       COLUMNS (priv)->spacing = spacing;
1870
1871       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1872         gtk_widget_queue_resize (GTK_WIDGET (grid));
1873
1874       g_object_notify (G_OBJECT (grid), "row-spacing");
1875     }
1876 }
1877
1878 /**
1879  * gtk_grid_get_row_spacing:
1880  * @grid: a #GtkGrid
1881  *
1882  * Returns the amount of space between the rows of @grid.
1883  *
1884  * Returns: the row spacing of @grid
1885  */
1886 guint
1887 gtk_grid_get_row_spacing (GtkGrid *grid)
1888 {
1889   GtkGridPrivate *priv;
1890   g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1891
1892   priv = grid->priv;
1893
1894   return COLUMNS (priv)->spacing;
1895 }
1896
1897 /**
1898  * gtk_grid_set_column_spacing:
1899  * @grid: a #GtkGrid
1900  * @spacing: the amount of space to insert between columns
1901  *
1902  * Sets the amount of space between columns of @grid.
1903  */
1904 void
1905 gtk_grid_set_column_spacing (GtkGrid *grid,
1906                              guint    spacing)
1907 {
1908   GtkGridPrivate *priv;
1909   g_return_if_fail (GTK_IS_GRID (grid));
1910   g_return_if_fail (spacing <= G_MAXINT16);
1911
1912   priv = grid->priv;
1913
1914   if (ROWS (priv)->spacing != spacing)
1915     {
1916       ROWS (priv)->spacing = spacing;
1917
1918       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
1919         gtk_widget_queue_resize (GTK_WIDGET (grid));
1920
1921       g_object_notify (G_OBJECT (grid), "column-spacing");
1922     }
1923 }
1924
1925 /**
1926  * gtk_grid_get_column_spacing:
1927  * @grid: a #GtkGrid
1928  *
1929  * Returns the amount of space between the columns of @grid.
1930  *
1931  * Returns: the column spacing of @grid
1932  */
1933 guint
1934 gtk_grid_get_column_spacing (GtkGrid *grid)
1935 {
1936   GtkGridPrivate *priv;
1937
1938   g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1939
1940   priv = grid->priv;
1941
1942   return ROWS (priv)->spacing;
1943 }