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