]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkgrid.c
filechooserbutton: New test for opening the Other item in the combo box and then...
[~andy/gtk] / gtk / gtkgrid.c
index 525f970df69d93952c323787d6670bd448f6d1fe..571f8266f2039408d600cfae7876dd4558c1811c 100644 (file)
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
 
+#include <string.h>
+
 #include "gtkgrid.h"
 
-#include "gtkorientable.h"
+#include "gtkorientableprivate.h"
 #include "gtksizerequest.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
@@ -42,7 +42,8 @@
  *
  * Children are added using gtk_grid_attach(). They can span multiple
  * rows or columns. It is also possible to add a child next to an
- * existing child, using gtk_grid_attach_next_to().
+ * existing child, using gtk_grid_attach_next_to(). The behaviour of
+ * GtkGrid when several children occupy the same grid cell is undefined.
  *
  * GtkGrid can be used like a #GtkBox by just using gtk_container_add(),
  * which will place children next to each other in the direction determined
@@ -161,19 +162,19 @@ gtk_grid_get_property (GObject    *object,
       break;
 
     case PROP_ROW_SPACING:
-      g_value_set_int (value, ROWS (priv)->spacing);
+      g_value_set_int (value, COLUMNS (priv)->spacing);
       break;
 
     case PROP_COLUMN_SPACING:
-      g_value_set_int (value, COLUMNS (priv)->spacing);
+      g_value_set_int (value, ROWS (priv)->spacing);
       break;
 
     case PROP_ROW_HOMOGENEOUS:
-      g_value_set_boolean (value, ROWS (priv)->homogeneous);
+      g_value_set_boolean (value, COLUMNS (priv)->homogeneous);
       break;
 
     case PROP_COLUMN_HOMOGENEOUS:
-      g_value_set_boolean (value, COLUMNS (priv)->homogeneous);
+      g_value_set_boolean (value, ROWS (priv)->homogeneous);
       break;
 
     default:
@@ -187,23 +188,11 @@ gtk_grid_set_orientation (GtkGrid        *grid,
                           GtkOrientation  orientation)
 {
   GtkGridPrivate *priv = grid->priv;
-  GtkStyleContext *context;
 
   if (priv->orientation != orientation)
     {
       priv->orientation = orientation;
-      context = gtk_widget_get_style_context (GTK_WIDGET (grid));
-
-      if (grid->priv->orientation == GTK_ORIENTATION_VERTICAL)
-        {
-          gtk_style_context_add_class (context, GTK_STYLE_CLASS_VERTICAL);
-          gtk_style_context_remove_class (context, GTK_STYLE_CLASS_HORIZONTAL);
-        }
-      else
-        {
-          gtk_style_context_add_class (context, GTK_STYLE_CLASS_HORIZONTAL);
-          gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VERTICAL);
-        }
+      _gtk_orientable_set_style_classes (GTK_ORIENTABLE (grid));
 
       g_object_notify (G_OBJECT (grid), "orientation");
     }
@@ -373,41 +362,93 @@ gtk_grid_init (GtkGrid *grid)
   priv->linedata[1].homogeneous = FALSE;
 }
 
-static void grid_attach (GtkGrid   *grid,
-                         GtkWidget *child,
-                         gint       left,
-                         gint       top,
-                         gint       width,
-                         gint       height);
-
 static void
-gtk_grid_add (GtkContainer *container,
-              GtkWidget    *child)
+grid_attach (GtkGrid   *grid,
+             GtkWidget *widget,
+             gint       left,
+             gint       top,
+             gint       width,
+             gint       height)
+{
+  GtkGridPrivate *priv = grid->priv;
+  GtkGridChild *child;
+
+  child = g_slice_new (GtkGridChild);
+  child->widget = widget;
+  CHILD_LEFT (child) = left;
+  CHILD_TOP (child) = top;
+  CHILD_WIDTH (child) = width;
+  CHILD_HEIGHT (child) = height;
+
+  priv->children = g_list_prepend (priv->children, child);
+
+  gtk_widget_set_parent (widget, GTK_WIDGET (grid));
+}
+
+/* Find the position 'touching' existing
+ * children. @orientation and @max determine
+ * from which direction to approach (horizontal
+ * + max = right, vertical + !max = top, etc).
+ * @op_pos, @op_span determine the rows/columns
+ * in which the touching has to happen.
+ */
+static gint
+find_attach_position (GtkGrid         *grid,
+                      GtkOrientation   orientation,
+                      gint             op_pos,
+                      gint             op_span,
+                      gboolean         max)
 {
-  GtkGrid *grid = GTK_GRID (container);
   GtkGridPrivate *priv = grid->priv;
   GtkGridChild *grid_child;
   GtkGridChildAttach *attach;
   GtkGridChildAttach *opposite;
   GList *list;
   gint pos;
+  gboolean hit;
+
+  if (max)
+    pos = -G_MAXINT;
+  else
+    pos = G_MAXINT;
+
+  hit = FALSE;
 
-  pos = 0;
   for (list = priv->children; list; list = list->next)
     {
       grid_child = list->data;
 
-      attach = &grid_child->attach[priv->orientation];
-      opposite = &grid_child->attach[1 - priv->orientation];
+      attach = &grid_child->attach[orientation];
+      opposite = &grid_child->attach[1 - orientation];
+
+      /* check if the ranges overlap */
+      if (opposite->pos <= op_pos + op_span && op_pos <= opposite->pos + opposite->span)
+        {
+          hit = TRUE;
 
-      if (opposite->pos <= 0 && opposite->pos + opposite->span > 0)
-        pos = MAX (pos, attach->pos + attach->span);
+          if (max)
+            pos = MAX (pos, attach->pos + attach->span);
+          else
+            pos = MIN (pos, attach->pos);
+        }
      }
 
-  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
-    grid_attach (grid, child, pos, 0, 1, 1);
-  else
-    grid_attach (grid, child, 0, pos, 1, 1);
+  if (!hit)
+    pos = 0;
+
+  return pos;
+}
+
+static void
+gtk_grid_add (GtkContainer *container,
+              GtkWidget    *child)
+{
+  GtkGrid *grid = GTK_GRID (container);
+  GtkGridPrivate *priv = grid->priv;
+  gint pos[2] = { 0, 0 };
+
+  pos[priv->orientation] = find_attach_position (grid, priv->orientation, 0, 1, TRUE);
+  grid_attach (grid, child, pos[0], pos[1], 1, 1);
 }
 
 static void
@@ -468,17 +509,6 @@ gtk_grid_child_type (GtkContainer *container)
   return GTK_TYPE_WIDGET;
 }
 
-static GtkSizeRequestMode
-gtk_grid_get_request_mode (GtkWidget *widget)
-{
-  GtkGridPrivate *priv = GTK_GRID (widget)->priv;
-
-  if (priv->orientation == GTK_ORIENTATION_VERTICAL)
-    return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
-  else
-    return GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
-}
-
 /* Calculates the min and max numbers for both orientations.
  */
 static void
@@ -741,37 +771,72 @@ gtk_grid_request_spanning (GtkGridRequest *request,
       /* If we need to request more space for this child to fill
        * its requisition, then divide up the needed space amongst the
        * lines it spans, favoring expandable lines if any.
+       *
+       * When doing homogeneous allocation though, try to keep the
+       * line allocations even, since we're going to force them to
+       * be the same anyway, and we don't want to introduce unnecessary
+       * extra space.
        */
       if (span_minimum < minimum)
         {
-          extra = minimum - span_minimum;
-          expand = span_expand;
-          for (i = 0; i < attach->span; i++)
+          if (linedata->homogeneous)
             {
-              line = &lines->lines[attach->pos - lines->min + i];
-              if (force_expand || line->expand)
+              gint total, m;
+
+              total = minimum - (attach->span - 1) * linedata->spacing;
+              m = total / attach->span + (total % attach->span ? 1 : 0);
+              for (i = 0; i < attach->span; i++)
+                {
+                  line = &lines->lines[attach->pos - lines->min + i];
+                  line->minimum = MAX(line->minimum, m);
+                }
+            }
+          else
+            {
+              extra = minimum - span_minimum;
+              expand = span_expand;
+              for (i = 0; i < attach->span; i++)
                 {
-                  line_extra = extra / expand;
-                  line->minimum += line_extra;
-                  extra -= line_extra;
-                  expand -= 1;
+                  line = &lines->lines[attach->pos - lines->min + i];
+                  if (force_expand || line->expand)
+                    {
+                      line_extra = extra / expand;
+                      line->minimum += line_extra;
+                      extra -= line_extra;
+                      expand -= 1;
+                    }
                 }
             }
         }
 
       if (span_natural < natural)
         {
-          extra = natural - span_natural;
-          expand = span_expand;
-          for (i = 0; i < attach->span; i++)
+          if (linedata->homogeneous)
             {
-              line = &lines->lines[attach->pos - lines->min + i];
-              if (force_expand || line->expand)
+              gint total, n;
+
+              total = natural - (attach->span - 1) * linedata->spacing;
+              n = total / attach->span + (total % attach->span ? 1 : 0);
+              for (i = 0; i < attach->span; i++)
+                {
+                  line = &lines->lines[attach->pos - lines->min + i];
+                  line->natural = MAX(line->natural, n);
+                }
+            }
+          else
+            {
+              extra = natural - span_natural;
+              expand = span_expand;
+              for (i = 0; i < attach->span; i++)
                 {
-                  line_extra = extra / expand;
-                  line->natural += line_extra;
-                  extra -= line_extra;
-                  expand -= 1;
+                  line = &lines->lines[attach->pos - lines->min + i];
+                  if (force_expand || line->expand)
+                    {
+                      line_extra = extra / expand;
+                      line->natural += line_extra;
+                      extra -= line_extra;
+                      expand -= 1;
+                    }
                 }
             }
         }
@@ -896,8 +961,13 @@ gtk_grid_request_sum (GtkGridRequest *request,
   linedata = &priv->linedata[orientation];
   lines = &request->lines[orientation];
 
-  min = (nonempty - 1) * linedata->spacing;
-  nat = (nonempty - 1) * linedata->spacing;
+  min = 0;
+  nat = 0;
+  if (nonempty > 0)
+    {
+      min = (nonempty - 1) * linedata->spacing;
+      nat = (nonempty - 1) * linedata->spacing;
+    }
 
   for (i = 0; i < lines->max - lines->min; i++)
     {
@@ -951,6 +1021,9 @@ gtk_grid_request_allocate (GtkGridRequest *request,
 
   gtk_grid_request_compute_expand (request, orientation, &nonempty, &expand);
 
+  if (nonempty == 0)
+    return;
+
   linedata = &priv->linedata[orientation];
   lines = &request->lines[orientation];
 
@@ -1069,10 +1142,20 @@ gtk_grid_get_size (GtkGrid        *grid,
   GtkGridRequest request;
   GtkGridLines *lines;
 
+  if (minimum)
+    *minimum = 0;
+
+  if (natural)
+    *natural = 0;
+
+  if (grid->priv->children == NULL)
+    return;
+
   request.grid = grid;
   gtk_grid_request_count_lines (&request);
   lines = &request.lines[orientation];
   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
+  memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
 
   gtk_grid_request_run (&request, orientation, FALSE);
   gtk_grid_request_sum (&request, orientation, minimum, natural);
@@ -1089,12 +1172,23 @@ gtk_grid_get_size_for_size (GtkGrid        *grid,
   GtkGridLines *lines;
   gint min_size;
 
+  if (minimum)
+    *minimum = 0;
+
+  if (natural)
+    *natural = 0;
+
+  if (grid->priv->children == NULL)
+    return;
+
   request.grid = grid;
   gtk_grid_request_count_lines (&request);
   lines = &request.lines[0];
   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
+  memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
   lines = &request.lines[1];
   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
+  memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
 
   gtk_grid_request_run (&request, 1 - orientation, FALSE);
   gtk_grid_request_sum (&request, 1 - orientation, &min_size, NULL);
@@ -1111,8 +1205,7 @@ gtk_grid_get_preferred_width (GtkWidget *widget,
 {
   GtkGrid *grid = GTK_GRID (widget);
 
-  if (gtk_grid_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
-
+  if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, 0, minimum, natural);
   else
     gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
@@ -1125,7 +1218,7 @@ gtk_grid_get_preferred_height (GtkWidget *widget,
 {
   GtkGrid *grid = GTK_GRID (widget);
 
-  if (gtk_grid_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
+  if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, 0, minimum, natural);
   else
     gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
@@ -1139,7 +1232,7 @@ gtk_grid_get_preferred_width_for_height (GtkWidget *widget,
 {
   GtkGrid *grid = GTK_GRID (widget);
 
-  if (gtk_grid_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
+  if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
   else
     gtk_grid_get_size (grid, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
@@ -1153,7 +1246,7 @@ gtk_grid_get_preferred_height_for_width (GtkWidget *widget,
 {
   GtkGrid *grid = GTK_GRID (widget);
 
-  if (gtk_grid_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
+  if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
     gtk_grid_get_size_for_size (grid, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
   else
     gtk_grid_get_size (grid, GTK_ORIENTATION_VERTICAL, minimum, natural);
@@ -1214,6 +1307,10 @@ gtk_grid_request_allocate_children (GtkGridRequest *request)
       child_allocation.width = MAX (1, width);
       child_allocation.height = MAX (1, height);
 
+      if (gtk_widget_get_direction (GTK_WIDGET (request->grid)) == GTK_TEXT_DIR_RTL)
+        child_allocation.x = allocation.x + allocation.width
+                             - (child_allocation.x - allocation.x) - child_allocation.width;
+
       gtk_widget_size_allocate (child->widget, &child_allocation);
     }
 }
@@ -1228,20 +1325,35 @@ gtk_grid_size_allocate (GtkWidget     *widget,
   GtkGridPrivate *priv = grid->priv;
   GtkGridRequest request;
   GtkGridLines *lines;
+  GtkOrientation orientation;
+
+  if (priv->children == NULL)
+    {
+      gtk_widget_set_allocation (widget, allocation);
+      return;
+    }
 
   request.grid = grid;
+
   gtk_grid_request_count_lines (&request);
   lines = &request.lines[0];
   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
+  memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
   lines = &request.lines[1];
   lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
+  memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
 
   gtk_widget_set_allocation (widget, allocation);
 
-  gtk_grid_request_run (&request, 1 - priv->orientation, FALSE);
-  gtk_grid_request_allocate (&request, 1 - priv->orientation, GET_SIZE (allocation, 1 - priv->orientation));
-  gtk_grid_request_run (&request, priv->orientation, TRUE);
-  gtk_grid_request_allocate (&request, priv->orientation, GET_SIZE (allocation, priv->orientation));
+  if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
+    orientation = GTK_ORIENTATION_HORIZONTAL;
+  else
+    orientation = GTK_ORIENTATION_VERTICAL;
+
+  gtk_grid_request_run (&request, 1 - orientation, FALSE);
+  gtk_grid_request_allocate (&request, 1 - orientation, GET_SIZE (allocation, 1 - orientation));
+  gtk_grid_request_run (&request, orientation, TRUE);
+  gtk_grid_request_allocate (&request, orientation, GET_SIZE (allocation, orientation));
 
   gtk_grid_request_position (&request, 0);
   gtk_grid_request_position (&request, 1);
@@ -1262,7 +1374,6 @@ gtk_grid_class_init (GtkGridClass *class)
   widget_class->size_allocate = gtk_grid_size_allocate;
   widget_class->get_preferred_width = gtk_grid_get_preferred_width;
   widget_class->get_preferred_height = gtk_grid_get_preferred_height;
-  widget_class->get_request_mode = gtk_grid_get_request_mode;
   widget_class->get_preferred_width_for_height = gtk_grid_get_preferred_width_for_height;
   widget_class->get_preferred_height_for_width = gtk_grid_get_preferred_height_for_width;
 
@@ -1348,29 +1459,6 @@ gtk_grid_new (void)
   return g_object_new (GTK_TYPE_GRID, NULL);
 }
 
-static void
-grid_attach (GtkGrid   *grid,
-             GtkWidget *widget,
-             gint       left,
-             gint       top,
-             gint       width,
-             gint       height)
-{
-  GtkGridPrivate *priv = grid->priv;
-  GtkGridChild *child;
-
-  child = g_slice_new (GtkGridChild);
-  child->widget = widget;
-  CHILD_LEFT (child) = left;
-  CHILD_TOP (child) = top;
-  CHILD_WIDTH (child) = width;
-  CHILD_HEIGHT (child) = height;
-
-  priv->children = g_list_prepend (priv->children, child);
-
-  gtk_widget_set_parent (widget, GTK_WIDGET (grid));
-}
-
 /**
  * gtk_grid_attach:
  * @grid: a #GtkGrid
@@ -1407,7 +1495,8 @@ gtk_grid_attach (GtkGrid   *grid,
  * gtk_grid_attach_next_to:
  * @grid: a #GtkGrid
  * @child: the widget to add
- * @sibling: the child of @grid that @child will be placed next to
+ * @sibling: (allow-none): the child of @grid that @child will be placed
+ *     next to, or %NULL to place @child at the beginning or end
  * @side: the side of @sibling that @child is positioned next to
  * @width: the number of columns that @child will span
  * @height: the number of rows that @child will span
@@ -1415,7 +1504,12 @@ gtk_grid_attach (GtkGrid   *grid,
  * Adds a widget to the grid.
  *
  * The widget is placed next to @sibling, on the side determined by
- * @side.
+ * @side. When @sibling is %NULL, the widget is placed in row (for
+ * left or right placement) or column 0 (for top or bottom placement),
+ * at the end indicated by @side.
+ *
+ * Attaching widgets labeled [1], [2], [3] with @sibling == %NULL and
+ * @side == %GTK_POS_LEFT yields a layout of [3][2][1].
  */
 void
 gtk_grid_attach_next_to (GtkGrid         *grid,
@@ -1431,35 +1525,244 @@ gtk_grid_attach_next_to (GtkGrid         *grid,
   g_return_if_fail (GTK_IS_GRID (grid));
   g_return_if_fail (GTK_IS_WIDGET (child));
   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
-  g_return_if_fail (gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
+  g_return_if_fail (sibling == NULL || gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
   g_return_if_fail (width > 0);
   g_return_if_fail (height > 0);
 
-  grid_sibling = find_grid_child (grid, sibling);
+  if (sibling)
+    {
+      grid_sibling = find_grid_child (grid, sibling);
+
+      switch (side)
+        {
+        case GTK_POS_LEFT:
+          left = CHILD_LEFT (grid_sibling) - width;
+          top = CHILD_TOP (grid_sibling);
+          break;
+        case GTK_POS_RIGHT:
+          left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling);
+          top = CHILD_TOP (grid_sibling);
+          break;
+        case GTK_POS_TOP:
+          left = CHILD_LEFT (grid_sibling);
+          top = CHILD_TOP (grid_sibling) - height;
+          break;
+        case GTK_POS_BOTTOM:
+          left = CHILD_LEFT (grid_sibling);
+          top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling);
+          break;
+        default:
+          g_assert_not_reached ();
+        }
+    }
+  else
+    {
+      switch (side)
+        {
+        case GTK_POS_LEFT:
+          left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, FALSE);
+          left -= width;
+          top = 0;
+          break;
+        case GTK_POS_RIGHT:
+          left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, TRUE);
+          top = 0;
+          break;
+        case GTK_POS_TOP:
+          left = 0;
+          top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, FALSE);
+          top -= height;
+          break;
+        case GTK_POS_BOTTOM:
+          left = 0;
+          top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, TRUE);
+          break;
+        default:
+          g_assert_not_reached ();
+        }
+    }
+
+  grid_attach (grid, child, left, top, width, height);
+}
+
+/**
+ * gtk_grid_get_child_at:
+ * @grid: a #GtkGrid
+ * @left: the left edge of the cell
+ * @top: the top edge of the cell
+ *
+ * Gets the child of @grid whose area covers the grid
+ * cell whose upper left corner is at @left, @top.
+ *
+ * Returns: (transfer none): the child at the given position, or %NULL
+ *
+ * Since: 3.2
+ */
+GtkWidget *
+gtk_grid_get_child_at (GtkGrid *grid,
+                       gint     left,
+                       gint     top)
+{
+  GtkGridPrivate *priv;
+  GtkGridChild *child;
+  GList *list;
+
+  g_return_val_if_fail (GTK_IS_GRID (grid), NULL);
+
+  priv = grid->priv;
+
+  for (list = priv->children; list; list = list->next)
+    {
+      child = list->data;
+
+      if (CHILD_LEFT (child) <= left &&
+          CHILD_LEFT (child) + CHILD_WIDTH (child) > left &&
+          CHILD_TOP (child) <= top &&
+          CHILD_TOP (child) + CHILD_HEIGHT (child) > top)
+        return child->widget;
+    }
+
+  return NULL;
+}
+
+/**
+ * gtk_grid_insert_row:
+ * @grid: a #GtkGrid
+ * @position: the position to insert the row at
+ *
+ * Inserts a row at the specified position.
+ *
+ * Children which are attached at or below this position
+ * are moved one row down. Children which span across this
+ * position are grown to span the new row.
+ *
+ * Since: 3.2
+ */
+void
+gtk_grid_insert_row (GtkGrid *grid,
+                     gint     position)
+{
+  GtkGridPrivate *priv;
+  GtkGridChild *child;
+  GList *list;
+  gint top, height;
+
+  g_return_if_fail (GTK_IS_GRID (grid));
+
+  priv = grid->priv;
+
+  for (list = priv->children; list; list = list->next)
+    {
+      child = list->data;
+
+      top = CHILD_TOP (child);
+      height = CHILD_HEIGHT (child);
+
+      if (top >= position)
+        {
+          CHILD_TOP (child) = top + 1;
+          gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "top-attach");
+        }
+      else if (top + height > position)
+        {
+          CHILD_HEIGHT (child) = height + 1;
+          gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "height");
+        }
+    }
+}
+
+/**
+ * gtk_grid_insert_column:
+ * @grid: a #GtkGrid
+ * @position: the position to insert the column at
+ *
+ * Inserts a column at the specified position.
+ *
+ * Children which are attached at or to the right of this position
+ * are moved one column to the right. Children which span across this
+ * position are grown to span the new column.
+ *
+ * Since: 3.2
+ */
+void
+gtk_grid_insert_column (GtkGrid *grid,
+                        gint     position)
+{
+  GtkGridPrivate *priv;
+  GtkGridChild *child;
+  GList *list;
+  gint left, width;
+
+  g_return_if_fail (GTK_IS_GRID (grid));
+
+  priv = grid->priv;
+
+  for (list = priv->children; list; list = list->next)
+    {
+      child = list->data;
+
+      left = CHILD_LEFT (child);
+      width = CHILD_WIDTH (child);
+
+      if (left >= position)
+        {
+          CHILD_LEFT (child) = left + 1;
+          gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "left-attach");
+        }
+      else if (left + width > position)
+        {
+          CHILD_WIDTH (child) = width + 1;
+          gtk_container_child_notify (GTK_CONTAINER (grid), child->widget, "width");
+        }
+    }
+}
+
+/**
+ * gtk_grid_insert_next_to:
+ * @grid: a #GtkGrid
+ * @sibling: the child of @grid that the new row or column will be
+ *     placed next to
+ * @side: the side of @sibling that @child is positioned next to
+ *
+ * Inserts a row or column at the specified position.
+ *
+ * The new row or column is placed next to @sibling, on the side
+ * determined by @side. If @side is %GTK_POS_TOP or %GTK_POS_BOTTOM,
+ * a row is inserted. If @side is %GTK_POS_LEFT of %GTK_POS_RIGHT,
+ * a column is inserted.
+ *
+ * Since: 3.2
+ */
+void
+gtk_grid_insert_next_to (GtkGrid         *grid,
+                         GtkWidget       *sibling,
+                         GtkPositionType  side)
+{
+  GtkGridChild *child;
+
+  g_return_if_fail (GTK_IS_GRID (grid));
+  g_return_if_fail (GTK_IS_WIDGET (sibling));
+  g_return_if_fail (gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
+
+  child = find_grid_child (grid, sibling);
 
   switch (side)
     {
     case GTK_POS_LEFT:
-      left = CHILD_LEFT (grid_sibling) - width;
-      top = CHILD_TOP (grid_sibling);
+      gtk_grid_insert_column (grid, CHILD_LEFT (child));
       break;
     case GTK_POS_RIGHT:
-      left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling);
-      top = CHILD_TOP (grid_sibling);
+      gtk_grid_insert_column (grid, CHILD_LEFT (child) + CHILD_WIDTH (child));
       break;
     case GTK_POS_TOP:
-      left = CHILD_LEFT (grid_sibling);
-      top = CHILD_TOP (grid_sibling) - height;
+      gtk_grid_insert_row (grid, CHILD_TOP (child));
       break;
     case GTK_POS_BOTTOM:
-      left = CHILD_LEFT (grid_sibling);
-      top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling);
+      gtk_grid_insert_row (grid, CHILD_TOP (child) + CHILD_HEIGHT (child));
       break;
     default:
       g_assert_not_reached ();
     }
-
-  grid_attach (grid, child, left, top, width, height);
 }
 
 /**
@@ -1478,9 +1781,10 @@ gtk_grid_set_row_homogeneous (GtkGrid  *grid,
 
   priv = grid->priv;
 
-  if (ROWS (priv)->homogeneous != homogeneous)
+  /* Yes, homogeneous rows means all the columns have the same size */
+  if (COLUMNS (priv)->homogeneous != homogeneous)
     {
-      ROWS (priv)->homogeneous = homogeneous;
+      COLUMNS (priv)->homogeneous = homogeneous;
 
       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
         gtk_widget_queue_resize (GTK_WIDGET (grid));
@@ -1505,7 +1809,7 @@ gtk_grid_get_row_homogeneous (GtkGrid *grid)
 
   priv = grid->priv;
 
-  return ROWS (priv)->homogeneous;
+  return COLUMNS (priv)->homogeneous;
 }
 
 /**
@@ -1524,9 +1828,10 @@ gtk_grid_set_column_homogeneous (GtkGrid  *grid,
 
   priv = grid->priv;
 
-  if (COLUMNS (priv)->homogeneous != homogeneous)
+  /* Yes, homogeneous columns means all the rows have the same size */
+  if (ROWS (priv)->homogeneous != homogeneous)
     {
-      COLUMNS (priv)->homogeneous = homogeneous;
+      ROWS (priv)->homogeneous = homogeneous;
 
       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
         gtk_widget_queue_resize (GTK_WIDGET (grid));
@@ -1551,7 +1856,7 @@ gtk_grid_get_column_homogeneous (GtkGrid *grid)
 
   priv = grid->priv;
 
-  return COLUMNS (priv)->homogeneous;
+  return ROWS (priv)->homogeneous;
 }
 
 /**
@@ -1571,9 +1876,9 @@ gtk_grid_set_row_spacing (GtkGrid *grid,
 
   priv = grid->priv;
 
-  if (ROWS (priv)->spacing != spacing)
+  if (COLUMNS (priv)->spacing != spacing)
     {
-      ROWS (priv)->spacing = spacing;
+      COLUMNS (priv)->spacing = spacing;
 
       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
         gtk_widget_queue_resize (GTK_WIDGET (grid));
@@ -1598,7 +1903,7 @@ gtk_grid_get_row_spacing (GtkGrid *grid)
 
   priv = grid->priv;
 
-  return ROWS (priv)->spacing;
+  return COLUMNS (priv)->spacing;
 }
 
 /**
@@ -1618,9 +1923,9 @@ gtk_grid_set_column_spacing (GtkGrid *grid,
 
   priv = grid->priv;
 
-  if (COLUMNS (priv)->spacing != spacing)
+  if (ROWS (priv)->spacing != spacing)
     {
-      COLUMNS (priv)->spacing = spacing;
+      ROWS (priv)->spacing = spacing;
 
       if (gtk_widget_get_visible (GTK_WIDGET (grid)))
         gtk_widget_queue_resize (GTK_WIDGET (grid));
@@ -1646,5 +1951,5 @@ gtk_grid_get_column_spacing (GtkGrid *grid)
 
   priv = grid->priv;
 
-  return COLUMNS (priv)->spacing;
+  return ROWS (priv)->spacing;
 }