]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - unix/gtkcols.c
Compile fix for GTK 3.18: avoid gtk_adjustment_changed().
[PuTTY.git] / unix / gtkcols.c
index cb654b0310e6accf5048f64736c372ab8952f3dc..e8223a726e3bab89fb6357419ccf4807632dddb1 100644 (file)
@@ -2,8 +2,9 @@
  * gtkcols.c - implementation of the `Columns' GTK layout container.
  */
 
-#include "gtkcols.h"
 #include <gtk/gtk.h>
+#include "gtkcompat.h"
+#include "gtkcols.h"
 
 static void columns_init(Columns *cols);
 static void columns_class_init(ColumnsClass *klass);
@@ -20,16 +21,29 @@ static void columns_forall(GtkContainer *container, gboolean include_internals,
 #if !GTK_CHECK_VERSION(2,0,0)
 static gint columns_focus(GtkContainer *container, GtkDirectionType dir);
 #endif
-static GtkType columns_child_type(GtkContainer *container);
+static GType columns_child_type(GtkContainer *container);
+#if GTK_CHECK_VERSION(3,0,0)
+static void columns_get_preferred_width(GtkWidget *widget,
+                                        gint *min, gint *nat);
+static void columns_get_preferred_height(GtkWidget *widget,
+                                         gint *min, gint *nat);
+static void columns_get_preferred_width_for_height(GtkWidget *widget,
+                                                   gint height,
+                                                   gint *min, gint *nat);
+static void columns_get_preferred_height_for_width(GtkWidget *widget,
+                                                   gint width,
+                                                   gint *min, gint *nat);
+#else
 static void columns_size_request(GtkWidget *widget, GtkRequisition *req);
+#endif
 static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc);
 
 static GtkContainerClass *parent_class = NULL;
 
 #if !GTK_CHECK_VERSION(2,0,0)
-GtkType columns_get_type(void)
+GType columns_get_type(void)
 {
-    static GtkType columns_type = 0;
+    static GType columns_type = 0;
 
     if (!columns_type) {
         static const GtkTypeInfo columns_info = {
@@ -82,7 +96,7 @@ static gint (*columns_inherited_focus)(GtkContainer *container,
 static void columns_class_init(ColumnsClass *klass)
 {
 #if !GTK_CHECK_VERSION(2,0,0)
-    GtkObjectClass *object_class = (GtkObjectClass *)klass;
+    /* GtkObjectClass *object_class = (GtkObjectClass *)klass; */
     GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
     GtkContainerClass *container_class = (GtkContainerClass *)klass;
 #else
@@ -92,9 +106,9 @@ static void columns_class_init(ColumnsClass *klass)
 #endif
 
 #if !GTK_CHECK_VERSION(2,0,0)
-    parent_class = g_type_class_peek_parent(klass);
-#else
     parent_class = gtk_type_class(GTK_TYPE_CONTAINER);
+#else
+    parent_class = g_type_class_peek_parent(klass);
 #endif
 
     widget_class->map = columns_map;
@@ -103,7 +117,16 @@ static void columns_class_init(ColumnsClass *klass)
     widget_class->draw = columns_draw;
     widget_class->expose_event = columns_expose;
 #endif
+#if GTK_CHECK_VERSION(3,0,0)
+    widget_class->get_preferred_width = columns_get_preferred_width;
+    widget_class->get_preferred_height = columns_get_preferred_height;
+    widget_class->get_preferred_width_for_height =
+        columns_get_preferred_width_for_height;
+    widget_class->get_preferred_height_for_width =
+        columns_get_preferred_height_for_width;
+#else
     widget_class->size_request = columns_size_request;
+#endif
     widget_class->size_allocate = columns_size_allocate;
 
     container_class->add = columns_base_add;
@@ -120,7 +143,7 @@ static void columns_class_init(ColumnsClass *klass)
 
 static void columns_init(Columns *cols)
 {
-    GTK_WIDGET_SET_FLAGS(cols, GTK_NO_WINDOW);
+    gtk_widget_set_has_window(GTK_WIDGET(cols), FALSE);
 
     cols->children = NULL;
     cols->spacing = 0;
@@ -141,14 +164,14 @@ static void columns_map(GtkWidget *widget)
     g_return_if_fail(IS_COLUMNS(widget));
 
     cols = COLUMNS(widget);
-    GTK_WIDGET_SET_FLAGS(cols, GTK_MAPPED);
+    gtk_widget_set_mapped(GTK_WIDGET(cols), TRUE);
 
     for (children = cols->children;
          children && (child = children->data);
          children = children->next) {
         if (child->widget &&
-           GTK_WIDGET_VISIBLE(child->widget) &&
-            !GTK_WIDGET_MAPPED(child->widget))
+           gtk_widget_get_visible(child->widget) &&
+            !gtk_widget_get_mapped(child->widget))
             gtk_widget_map(child->widget);
     }
 }
@@ -162,14 +185,14 @@ static void columns_unmap(GtkWidget *widget)
     g_return_if_fail(IS_COLUMNS(widget));
 
     cols = COLUMNS(widget);
-    GTK_WIDGET_UNSET_FLAGS(cols, GTK_MAPPED);
+    gtk_widget_set_mapped(GTK_WIDGET(cols), FALSE);
 
     for (children = cols->children;
          children && (child = children->data);
          children = children->next) {
         if (child->widget &&
-           GTK_WIDGET_VISIBLE(child->widget) &&
-            GTK_WIDGET_MAPPED(child->widget))
+           gtk_widget_get_visible(child->widget) &&
+            gtk_widget_get_mapped(child->widget))
             gtk_widget_unmap(child->widget);
     }
 }
@@ -263,10 +286,18 @@ static void columns_remove(GtkContainer *container, GtkWidget *widget)
         if (child->widget != widget)
             continue;
 
-        was_visible = GTK_WIDGET_VISIBLE(widget);
+        was_visible = gtk_widget_get_visible(widget);
         gtk_widget_unparent(widget);
         cols->children = g_list_remove_link(cols->children, children);
         g_list_free(children);
+
+        if (child->same_height_as) {
+            g_return_if_fail(child->same_height_as->same_height_as == child);
+            child->same_height_as->same_height_as = NULL;
+            if (gtk_widget_get_visible(child->same_height_as->widget))
+                gtk_widget_queue_resize(GTK_WIDGET(container));
+        }
+
         g_free(child);
         if (was_visible)
             gtk_widget_queue_resize(GTK_WIDGET(container));
@@ -318,7 +349,7 @@ static void columns_forall(GtkContainer *container, gboolean include_internals,
     }
 }
 
-static GtkType columns_child_type(GtkContainer *container)
+static GType columns_child_type(GtkContainer *container)
 {
     return GTK_TYPE_WIDGET;
 }
@@ -367,13 +398,14 @@ void columns_add(Columns *cols, GtkWidget *child,
     g_return_if_fail(cols != NULL);
     g_return_if_fail(IS_COLUMNS(cols));
     g_return_if_fail(child != NULL);
-    g_return_if_fail(child->parent == NULL);
+    g_return_if_fail(gtk_widget_get_parent(child) == NULL);
 
     childdata = g_new(ColumnsChild, 1);
     childdata->widget = child;
     childdata->colstart = colstart;
     childdata->colspan = colspan;
     childdata->force_left = FALSE;
+    childdata->same_height_as = NULL;
 
     cols->children = g_list_append(cols->children, childdata);
     cols->taborder = g_list_append(cols->taborder, child);
@@ -384,36 +416,67 @@ void columns_add(Columns *cols, GtkWidget *child,
     gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);
 #endif
 
-    if (GTK_WIDGET_REALIZED(cols))
+    if (gtk_widget_get_realized(GTK_WIDGET(cols)))
         gtk_widget_realize(child);
 
-    if (GTK_WIDGET_VISIBLE(cols) && GTK_WIDGET_VISIBLE(child)) {
-        if (GTK_WIDGET_MAPPED(cols))
+    if (gtk_widget_get_visible(GTK_WIDGET(cols)) &&
+        gtk_widget_get_visible(child)) {
+        if (gtk_widget_get_mapped(GTK_WIDGET(cols)))
             gtk_widget_map(child);
         gtk_widget_queue_resize(child);
     }
 }
 
+static ColumnsChild *columns_find_child(Columns *cols, GtkWidget *widget)
+{
+    GList *children;
+    ColumnsChild *child;
+
+    for (children = cols->children;
+         children && (child = children->data);
+         children = children->next) {
+
+        if (child->widget == widget)
+            return child;
+    }
+
+    return NULL;
+}
+
 void columns_force_left_align(Columns *cols, GtkWidget *widget)
 {
     ColumnsChild *child;
-    GList *children;
 
     g_return_if_fail(cols != NULL);
     g_return_if_fail(IS_COLUMNS(cols));
     g_return_if_fail(widget != NULL);
 
-    for (children = cols->children;
-         children && (child = children->data);
-         children = children->next) {
-        if (child->widget != widget)
-            continue;
+    child = columns_find_child(cols, widget);
+    g_return_if_fail(child != NULL);
 
-       child->force_left = TRUE;
-        if (GTK_WIDGET_VISIBLE(widget))
-            gtk_widget_queue_resize(GTK_WIDGET(cols));
-        break;
-    }
+    child->force_left = TRUE;
+    if (gtk_widget_get_visible(widget))
+        gtk_widget_queue_resize(GTK_WIDGET(cols));
+}
+
+void columns_force_same_height(Columns *cols, GtkWidget *cw1, GtkWidget *cw2)
+{
+    ColumnsChild *child1, *child2;
+
+    g_return_if_fail(cols != NULL);
+    g_return_if_fail(IS_COLUMNS(cols));
+    g_return_if_fail(cw1 != NULL);
+    g_return_if_fail(cw2 != NULL);
+
+    child1 = columns_find_child(cols, cw1);
+    g_return_if_fail(child1 != NULL);
+    child2 = columns_find_child(cols, cw2);
+    g_return_if_fail(child2 != NULL);
+
+    child1->same_height_as = child2;
+    child2->same_height_as = child1;
+    if (gtk_widget_get_visible(cw1) || gtk_widget_get_visible(cw2))
+        gtk_widget_queue_resize(GTK_WIDGET(cols));
 }
 
 void columns_taborder_last(Columns *cols, GtkWidget *widget)
@@ -508,77 +571,74 @@ static gint columns_focus(GtkContainer *container, GtkDirectionType dir)
 #endif
 
 /*
- * Now here comes the interesting bit. The actual layout part is
- * done in the following two functions:
- * 
- * columns_size_request() examines the list of widgets held in the
- * Columns, and returns a requisition stating the absolute minimum
- * size it can bear to be.
- * 
- * columns_size_allocate() is given an allocation telling it what
- * size the whole container is going to be, and it calls
- * gtk_widget_size_allocate() on all of its (visible) children to
- * set their size and position relative to the top left of the
- * container.
+ * Underlying parts of the layout algorithm, to compute the Columns
+ * container's width or height given the widths or heights of its
+ * children. These will be called in various ways with different
+ * notions of width and height in use, so we abstract them out and
+ * pass them a 'get width' or 'get height' function pointer.
  */
 
-static void columns_size_request(GtkWidget *widget, GtkRequisition *req)
+typedef gint (*widget_dim_fn_t)(ColumnsChild *child);
+
+static gint columns_compute_width(Columns *cols, widget_dim_fn_t get_width)
 {
-    Columns *cols;
     ColumnsChild *child;
     GList *children;
-    gint i, ncols, colspan, *colypos;
+    gint i, ncols, colspan, retwidth, childwidth;
     const gint *percentages;
     static const gint onecol[] = { 100 };
 
-    g_return_if_fail(widget != NULL);
-    g_return_if_fail(IS_COLUMNS(widget));
-    g_return_if_fail(req != NULL);
-
-    cols = COLUMNS(widget);
+#ifdef COLUMNS_WIDTH_DIAGNOSTICS
+    printf("compute_width(%p): start\n", cols);
+#endif
 
-    req->width = 0;
-    req->height = cols->spacing;
+    retwidth = 0;
 
     ncols = 1;
-    colypos = g_new(gint, 1);
-    colypos[0] = 0;
     percentages = onecol;
 
     for (children = cols->children;
          children && (child = children->data);
          children = children->next) {
-        GtkRequisition creq;
 
        if (!child->widget) {
            /* Column reconfiguration. */
-           for (i = 1; i < ncols; i++) {
-               if (colypos[0] < colypos[i])
-                   colypos[0] = colypos[i];
-           }
            ncols = child->ncols;
            percentages = child->percentages;
-           colypos = g_renew(gint, colypos, ncols);
-           for (i = 1; i < ncols; i++)
-               colypos[i] = colypos[0];
            continue;
        }
 
         /* Only take visible widgets into account. */
-        if (!GTK_WIDGET_VISIBLE(child->widget))
+        if (!gtk_widget_get_visible(child->widget))
             continue;
 
-        gtk_widget_size_request(child->widget, &creq);
+        childwidth = get_width(child);
        colspan = child->colspan ? child->colspan : ncols-child->colstart;
 
+#ifdef COLUMNS_WIDTH_DIAGNOSTICS
+        printf("compute_width(%p): ", cols);
+        if (GTK_IS_LABEL(child->widget))
+            printf("label %p '%s' wrap=%s: ", child->widget,
+                   gtk_label_get_text(GTK_LABEL(child->widget)),
+                   (gtk_label_get_line_wrap(GTK_LABEL(child->widget))
+                    ? "TRUE" : "FALSE"));
+        else
+            printf("widget %p: ", child->widget);
+        {
+            gint min, nat;
+            gtk_widget_get_preferred_width(child->widget, &min, &nat);
+            printf("minwidth=%d natwidth=%d ", min, nat);
+        }
+        printf("thiswidth=%d span=%d\n", childwidth, colspan);
+#endif
+
         /*
-         * To compute width: we know that creq.width plus
-         * cols->spacing needs to equal a certain percentage of the
-         * full width of the container. So we work this value out,
-         * figure out how wide the container will need to be to
-         * make that percentage of it equal to that width, and
-         * ensure our returned width is at least that much. Very
-         * simple really.
+         * To compute width: we know that childwidth + cols->spacing
+         * needs to equal a certain percentage of the full width of
+         * the container. So we work this value out, figure out how
+         * wide the container will need to be to make that percentage
+         * of it equal to that width, and ensure our returned width is
+         * at least that much. Very simple really.
          */
         {
             int percent, thiswid, fullwid;
@@ -587,15 +647,19 @@ static void columns_size_request(GtkWidget *widget, GtkRequisition *req)
             for (i = 0; i < colspan; i++)
                 percent += percentages[child->colstart+i];
 
-            thiswid = creq.width + cols->spacing;
+            thiswid = childwidth + cols->spacing;
             /*
-             * Since creq is the _minimum_ size the child needs, we
-             * must ensure that it gets _at least_ that size.
-             * Hence, when scaling thiswid up to fullwid, we must
-             * round up, which means adding percent-1 before
-             * dividing by percent.
+             * Since childwidth is (at least sometimes) the _minimum_
+             * size the child needs, we must ensure that it gets _at
+             * least_ that size. Hence, when scaling thiswid up to
+             * fullwid, we must round up, which means adding percent-1
+             * before dividing by percent.
              */
             fullwid = (thiswid * 100 + percent - 1) / percent;
+#ifdef COLUMNS_WIDTH_DIAGNOSTICS
+            printf("compute_width(%p): after %p, thiswid=%d fullwid=%d\n",
+                   cols, child->widget, thiswid, fullwid);
+#endif
 
             /*
              * The above calculation assumes every widget gets
@@ -603,59 +667,30 @@ static void columns_size_request(GtkWidget *widget, GtkRequisition *req)
              * cols->spacing here to account for the extra load of
              * spacing on the right.
              */
-            if (req->width < fullwid - cols->spacing)
-                req->width = fullwid - cols->spacing;
-        }
-
-        /*
-         * To compute height: the widget's top will be positioned
-         * at the largest y value so far reached in any of the
-         * columns it crosses. Then it will go down by creq.height
-         * plus padding; and the point it reaches at the bottom is
-         * the new y value in all those columns, and minus the
-         * padding it is also a lower bound on our own size
-         * request.
-         */
-        {
-            int topy, boty;
-
-            topy = 0;
-            for (i = 0; i < colspan; i++) {
-                if (topy < colypos[child->colstart+i])
-                    topy = colypos[child->colstart+i];
-            }
-            boty = topy + creq.height + cols->spacing;
-            for (i = 0; i < colspan; i++) {
-                colypos[child->colstart+i] = boty;
-            }
-
-            if (req->height < boty - cols->spacing)
-                req->height = boty - cols->spacing;
+            if (retwidth < fullwid - cols->spacing)
+                retwidth = fullwid - cols->spacing;
         }
     }
 
-    req->width += 2*GTK_CONTAINER(cols)->border_width;
-    req->height += 2*GTK_CONTAINER(cols)->border_width;
+    retwidth += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
 
-    g_free(colypos);
+#ifdef COLUMNS_WIDTH_DIAGNOSTICS
+    printf("compute_width(%p): done, returning %d\n", cols, retwidth);
+#endif
+
+    return retwidth;
 }
 
-static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
+static void columns_alloc_horiz(Columns *cols, gint ourwidth,
+                                widget_dim_fn_t get_width)
 {
-    Columns *cols;
     ColumnsChild *child;
     GList *children;
-    gint i, ncols, colspan, border, *colxpos, *colypos;
+    gint i, ncols, colspan, border, *colxpos, childwidth;
     const gint *percentages;
     static const gint onecol[] = { 100 };
 
-    g_return_if_fail(widget != NULL);
-    g_return_if_fail(IS_COLUMNS(widget));
-    g_return_if_fail(alloc != NULL);
-
-    cols = COLUMNS(widget);
-    widget->allocation = *alloc;
-    border = GTK_CONTAINER(cols)->border_width;
+    border = gtk_container_get_border_width(GTK_CONTAINER(cols));
 
     ncols = 1;
     percentages = onecol;
@@ -665,46 +700,34 @@ static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
      * subtracting the spacing. */
     colxpos = g_new(gint, 2);
     colxpos[0] = 0;
-    colxpos[1] = alloc->width - 2*border + cols->spacing;
-    /* As in size_request, colypos is the lowest y reached in each column. */
-    colypos = g_new(gint, 1);
-    colypos[0] = 0;
+    colxpos[1] = ourwidth - 2*border + cols->spacing;
 
     for (children = cols->children;
          children && (child = children->data);
          children = children->next) {
-        GtkRequisition creq;
-        GtkAllocation call;
 
        if (!child->widget) {
            gint percent;
 
            /* Column reconfiguration. */
-           for (i = 1; i < ncols; i++) {
-               if (colypos[0] < colypos[i])
-                   colypos[0] = colypos[i];
-           }
            ncols = child->ncols;
            percentages = child->percentages;
-           colypos = g_renew(gint, colypos, ncols);
-           for (i = 1; i < ncols; i++)
-               colypos[i] = colypos[0];
            colxpos = g_renew(gint, colxpos, ncols + 1);
            colxpos[0] = 0;
            percent = 0;
            for (i = 0; i < ncols; i++) {
                percent += percentages[i];
-               colxpos[i+1] = (((alloc->width - 2*border) + cols->spacing)
+               colxpos[i+1] = (((ourwidth - 2*border) + cols->spacing)
                                * percent / 100);
            }
            continue;
        }
 
         /* Only take visible widgets into account. */
-        if (!GTK_WIDGET_VISIBLE(child->widget))
+        if (!gtk_widget_get_visible(child->widget))
             continue;
 
-        gtk_widget_get_child_requisition(child->widget, &creq);
+        childwidth = get_width(child);
        colspan = child->colspan ? child->colspan : ncols-child->colstart;
 
         /*
@@ -714,12 +737,130 @@ static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
         * Unless we're forcing left, in which case the width is
         * exactly the requisition width.
          */
-        call.x = alloc->x + border + colxpos[child->colstart];
+        child->x = colxpos[child->colstart];
        if (child->force_left)
-           call.width = creq.width;
+           child->w = childwidth;
        else
-           call.width = (colxpos[child->colstart+colspan] -
-                         colxpos[child->colstart] - cols->spacing);
+           child->w = (colxpos[child->colstart+colspan] -
+                        colxpos[child->colstart] - cols->spacing);
+    }
+
+    g_free(colxpos);
+}
+
+static gint columns_compute_height(Columns *cols, widget_dim_fn_t get_height)
+{
+    ColumnsChild *child;
+    GList *children;
+    gint i, ncols, colspan, *colypos, retheight, childheight;
+
+    retheight = cols->spacing;
+
+    ncols = 1;
+    colypos = g_new(gint, 1);
+    colypos[0] = 0;
+
+    for (children = cols->children;
+         children && (child = children->data);
+         children = children->next) {
+
+       if (!child->widget) {
+           /* Column reconfiguration. */
+           for (i = 1; i < ncols; i++) {
+               if (colypos[0] < colypos[i])
+                   colypos[0] = colypos[i];
+           }
+           ncols = child->ncols;
+           colypos = g_renew(gint, colypos, ncols);
+           for (i = 1; i < ncols; i++)
+               colypos[i] = colypos[0];
+           continue;
+       }
+
+        /* Only take visible widgets into account. */
+        if (!gtk_widget_get_visible(child->widget))
+            continue;
+
+        childheight = get_height(child);
+        if (child->same_height_as) {
+            gint childheight2 = get_height(child->same_height_as);
+            if (childheight < childheight2)
+                childheight = childheight2;
+        }
+       colspan = child->colspan ? child->colspan : ncols-child->colstart;
+
+        /*
+         * To compute height: the widget's top will be positioned at
+         * the largest y value so far reached in any of the columns it
+         * crosses. Then it will go down by childheight plus padding;
+         * and the point it reaches at the bottom is the new y value
+         * in all those columns, and minus the padding it is also a
+         * lower bound on our own height.
+         */
+        {
+            int topy, boty;
+
+            topy = 0;
+            for (i = 0; i < colspan; i++) {
+                if (topy < colypos[child->colstart+i])
+                    topy = colypos[child->colstart+i];
+            }
+            boty = topy + childheight + cols->spacing;
+            for (i = 0; i < colspan; i++) {
+                colypos[child->colstart+i] = boty;
+            }
+
+            if (retheight < boty - cols->spacing)
+                retheight = boty - cols->spacing;
+        }
+    }
+
+    retheight += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
+
+    g_free(colypos);
+
+    return retheight;
+}
+
+static void columns_alloc_vert(Columns *cols, gint ourheight,
+                               widget_dim_fn_t get_height)
+{
+    ColumnsChild *child;
+    GList *children;
+    gint i, ncols, colspan, *colypos, realheight, fakeheight;
+
+    ncols = 1;
+    /* As in size_request, colypos is the lowest y reached in each column. */
+    colypos = g_new(gint, 1);
+    colypos[0] = 0;
+
+    for (children = cols->children;
+         children && (child = children->data);
+         children = children->next) {
+       if (!child->widget) {
+           /* Column reconfiguration. */
+           for (i = 1; i < ncols; i++) {
+               if (colypos[0] < colypos[i])
+                   colypos[0] = colypos[i];
+           }
+           ncols = child->ncols;
+           colypos = g_renew(gint, colypos, ncols);
+           for (i = 1; i < ncols; i++)
+               colypos[i] = colypos[0];
+           continue;
+       }
+
+        /* Only take visible widgets into account. */
+        if (!gtk_widget_get_visible(child->widget))
+            continue;
+
+        realheight = fakeheight = get_height(child);
+        if (child->same_height_as) {
+            gint childheight2 = get_height(child->same_height_as);
+            if (fakeheight < childheight2)
+                fakeheight = childheight2;
+        }
+       colspan = child->colspan ? child->colspan : ncols-child->colstart;
 
         /*
          * To compute height: the widget's top will be positioned
@@ -736,17 +877,279 @@ static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
                 if (topy < colypos[child->colstart+i])
                     topy = colypos[child->colstart+i];
             }
-            call.y = alloc->y + border + topy;
-            call.height = creq.height;
-            boty = topy + creq.height + cols->spacing;
+            child->y = topy + fakeheight/2 - realheight/2;
+            child->h = realheight;
+            boty = topy + fakeheight + cols->spacing;
             for (i = 0; i < colspan; i++) {
                 colypos[child->colstart+i] = boty;
             }
         }
-
-        gtk_widget_size_allocate(child->widget, &call);
     }
 
-    g_free(colxpos);
     g_free(colypos);    
 }
+
+/*
+ * Now here comes the interesting bit. The actual layout part is
+ * done in the following two functions:
+ *
+ * columns_size_request() examines the list of widgets held in the
+ * Columns, and returns a requisition stating the absolute minimum
+ * size it can bear to be.
+ *
+ * columns_size_allocate() is given an allocation telling it what
+ * size the whole container is going to be, and it calls
+ * gtk_widget_size_allocate() on all of its (visible) children to
+ * set their size and position relative to the top left of the
+ * container.
+ */
+
+#if !GTK_CHECK_VERSION(3,0,0)
+
+static gint columns_gtk2_get_width(ColumnsChild *child)
+{
+    GtkRequisition creq;
+    gtk_widget_size_request(child->widget, &creq);
+    return creq.width;
+}
+
+static gint columns_gtk2_get_height(ColumnsChild *child)
+{
+    GtkRequisition creq;
+    gtk_widget_size_request(child->widget, &creq);
+    return creq.height;
+}
+
+static void columns_size_request(GtkWidget *widget, GtkRequisition *req)
+{
+    Columns *cols;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_COLUMNS(widget));
+    g_return_if_fail(req != NULL);
+
+    cols = COLUMNS(widget);
+
+    req->width = columns_compute_width(cols, columns_gtk2_get_width);
+    req->height = columns_compute_height(cols, columns_gtk2_get_height);
+}
+
+static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
+{
+    Columns *cols;
+    ColumnsChild *child;
+    GList *children;
+    gint border;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_COLUMNS(widget));
+    g_return_if_fail(alloc != NULL);
+
+    cols = COLUMNS(widget);
+    gtk_widget_set_allocation(widget, alloc);
+
+    border = gtk_container_get_border_width(GTK_CONTAINER(cols));
+
+    columns_alloc_horiz(cols, alloc->width, columns_gtk2_get_width);
+    columns_alloc_vert(cols, alloc->height, columns_gtk2_get_height);
+
+    for (children = cols->children;
+         children && (child = children->data);
+         children = children->next) {
+        if (child->widget && gtk_widget_get_visible(child->widget)) {
+            GtkAllocation call;
+            call.x = alloc->x + border + child->x;
+            call.y = alloc->y + border + child->y;
+            call.width = child->w;
+            call.height = child->h;
+            gtk_widget_size_allocate(child->widget, &call);
+        }
+    }
+}
+
+#else /* GTK_CHECK_VERSION(3,0,0) */
+
+static gint columns_gtk3_get_min_width(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_width(child->widget, &ret, NULL);
+    return ret;
+}
+
+static gint columns_gtk3_get_nat_width(ColumnsChild *child)
+{
+    gint ret;
+
+    if ((GTK_IS_LABEL(child->widget) &&
+         gtk_label_get_line_wrap(GTK_LABEL(child->widget))) ||
+        GTK_IS_ENTRY(child->widget)) {
+        /*
+         * We treat wrapping GtkLabels as a special case in this
+         * layout class, because the whole point of those is that I
+         * _don't_ want them to take up extra horizontal space for
+         * long text, but instead to wrap it to whatever size is used
+         * by the rest of the layout.
+         *
+         * GtkEntry gets similar treatment, because in OS X GTK I've
+         * found that it requests a natural width regardless of the
+         * output of gtk_entry_set_width_chars.
+         */
+        gtk_widget_get_preferred_width(child->widget, &ret, NULL);
+    } else {
+        gtk_widget_get_preferred_width(child->widget, NULL, &ret);
+    }
+    return ret;
+}
+
+static gint columns_gtk3_get_minfh_width(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_width_for_height(child->widget, child->h,
+                                              &ret, NULL);
+    return ret;
+}
+
+static gint columns_gtk3_get_natfh_width(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_width_for_height(child->widget, child->h,
+                                              NULL, &ret);
+    return ret;
+}
+
+static gint columns_gtk3_get_min_height(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_height(child->widget, &ret, NULL);
+    return ret;
+}
+
+static gint columns_gtk3_get_nat_height(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_height(child->widget, NULL, &ret);
+    return ret;
+}
+
+static gint columns_gtk3_get_minfw_height(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_height_for_width(child->widget, child->w,
+                                              &ret, NULL);
+    return ret;
+}
+
+static gint columns_gtk3_get_natfw_height(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_height_for_width(child->widget, child->w,
+                                              NULL, &ret);
+    return ret;
+}
+
+static void columns_get_preferred_width(GtkWidget *widget,
+                                        gint *min, gint *nat)
+{
+    Columns *cols;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_COLUMNS(widget));
+
+    cols = COLUMNS(widget);
+
+    if (min)
+        *min = columns_compute_width(cols, columns_gtk3_get_min_width);
+    if (nat)
+        *nat = columns_compute_width(cols, columns_gtk3_get_nat_width);
+}
+
+static void columns_get_preferred_height(GtkWidget *widget,
+                                         gint *min, gint *nat)
+{
+    Columns *cols;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_COLUMNS(widget));
+
+    cols = COLUMNS(widget);
+
+    if (min)
+        *min = columns_compute_height(cols, columns_gtk3_get_min_height);
+    if (nat)
+        *nat = columns_compute_height(cols, columns_gtk3_get_nat_height);
+}
+
+static void columns_get_preferred_width_for_height(GtkWidget *widget,
+                                                   gint height,
+                                                   gint *min, gint *nat)
+{
+    Columns *cols;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_COLUMNS(widget));
+
+    cols = COLUMNS(widget);
+
+    /* FIXME: which one should the get-height function here be? */
+    columns_alloc_vert(cols, height, columns_gtk3_get_nat_height);
+
+    if (min)
+        *min = columns_compute_width(cols, columns_gtk3_get_minfh_width);
+    if (nat)
+        *nat = columns_compute_width(cols, columns_gtk3_get_natfh_width);
+}
+
+static void columns_get_preferred_height_for_width(GtkWidget *widget,
+                                                   gint width,
+                                                   gint *min, gint *nat)
+{
+    Columns *cols;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_COLUMNS(widget));
+
+    cols = COLUMNS(widget);
+
+    /* FIXME: which one should the get-height function here be? */
+    columns_alloc_horiz(cols, width, columns_gtk3_get_nat_width);
+
+    if (min)
+        *min = columns_compute_height(cols, columns_gtk3_get_minfw_height);
+    if (nat)
+        *nat = columns_compute_height(cols, columns_gtk3_get_natfw_height);
+}
+
+static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
+{
+    Columns *cols;
+    ColumnsChild *child;
+    GList *children;
+    gint border;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_COLUMNS(widget));
+    g_return_if_fail(alloc != NULL);
+
+    cols = COLUMNS(widget);
+    gtk_widget_set_allocation(widget, alloc);
+
+    border = gtk_container_get_border_width(GTK_CONTAINER(cols));
+
+    columns_alloc_horiz(cols, alloc->width, columns_gtk3_get_min_width);
+    columns_alloc_vert(cols, alloc->height, columns_gtk3_get_minfw_height);
+
+    for (children = cols->children;
+         children && (child = children->data);
+         children = children->next) {
+        if (child->widget && gtk_widget_get_visible(child->widget)) {
+            GtkAllocation call;
+            call.x = alloc->x + border + child->x;
+            call.y = alloc->y + border + child->y;
+            call.width = child->w;
+            call.height = child->h;
+            gtk_widget_size_allocate(child->widget, &call);
+        }
+    }
+}
+
+#endif