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