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