* gtkcols.c - implementation of the `Columns' GTK layout container.
*/
+#include <gtk/gtk.h>
+#include "gtkcompat.h"
#include "gtkcols.h"
static void columns_init(Columns *cols);
static void columns_class_init(ColumnsClass *klass);
static void columns_map(GtkWidget *widget);
static void columns_unmap(GtkWidget *widget);
+#if !GTK_CHECK_VERSION(2,0,0)
static void columns_draw(GtkWidget *widget, GdkRectangle *area);
static gint columns_expose(GtkWidget *widget, GdkEventExpose *event);
+#endif
static void columns_base_add(GtkContainer *container, GtkWidget *widget);
static void columns_remove(GtkContainer *container, GtkWidget *widget);
static void columns_forall(GtkContainer *container, gboolean include_internals,
GtkCallback callback, gpointer callback_data);
+#if !GTK_CHECK_VERSION(2,0,0)
static gint columns_focus(GtkContainer *container, GtkDirectionType dir);
-static GtkType columns_child_type(GtkContainer *container);
+#endif
+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;
-GtkType columns_get_type(void)
+#if !GTK_CHECK_VERSION(2,0,0)
+GType columns_get_type(void)
{
- static GtkType columns_type = 0;
+ static GType columns_type = 0;
if (!columns_type) {
static const GtkTypeInfo columns_info = {
return columns_type;
}
+#else
+GType columns_get_type(void)
+{
+ static GType columns_type = 0;
+
+ if (!columns_type) {
+ static const GTypeInfo columns_info = {
+ sizeof(ColumnsClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) columns_class_init,
+ NULL,
+ NULL,
+ sizeof(Columns),
+ 0,
+ (GInstanceInitFunc)columns_init,
+ };
+ columns_type = g_type_register_static(GTK_TYPE_CONTAINER, "Columns",
+ &columns_info, 0);
+ }
+
+ return columns_type;
+}
+#endif
+
+#if !GTK_CHECK_VERSION(2,0,0)
static gint (*columns_inherited_focus)(GtkContainer *container,
GtkDirectionType direction);
+#endif
static void columns_class_init(ColumnsClass *klass)
{
- GtkObjectClass *object_class;
- GtkWidgetClass *widget_class;
- GtkContainerClass *container_class;
-
- object_class = (GtkObjectClass *)klass;
- widget_class = (GtkWidgetClass *)klass;
- container_class = (GtkContainerClass *)klass;
-
+#if !GTK_CHECK_VERSION(2,0,0)
+ /* GtkObjectClass *object_class = (GtkObjectClass *)klass; */
+ GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
+ GtkContainerClass *container_class = (GtkContainerClass *)klass;
+#else
+ /* GObjectClass *object_class = G_OBJECT_CLASS(klass); */
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
+#endif
+
+#if !GTK_CHECK_VERSION(2,0,0)
parent_class = gtk_type_class(GTK_TYPE_CONTAINER);
-
- /*
- * FIXME: do we have to do all this faffing with set_arg,
- * get_arg and child_arg_type? Ick.
- */
+#else
+ parent_class = g_type_class_peek_parent(klass);
+#endif
widget_class->map = columns_map;
widget_class->unmap = columns_unmap;
+#if !GTK_CHECK_VERSION(2,0,0)
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;
container_class->remove = columns_remove;
container_class->forall = columns_forall;
container_class->child_type = columns_child_type;
+#if !GTK_CHECK_VERSION(2,0,0)
/* Save the previous value of this method. */
if (!columns_inherited_focus)
columns_inherited_focus = container_class->focus;
container_class->focus = columns_focus;
+#endif
}
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;
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);
}
}
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);
}
}
+#if !GTK_CHECK_VERSION(2,0,0)
static void columns_draw(GtkWidget *widget, GdkRectangle *area)
{
Columns *cols;
}
return FALSE;
}
+#endif
static void columns_base_add(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));
cols->taborder = g_list_remove_link(cols->taborder, children);
g_list_free(children);
+#if GTK_CHECK_VERSION(2,0,0)
+ gtk_container_set_focus_chain(container, cols->taborder);
+#endif
break;
}
}
{
Columns *cols;
ColumnsChild *child;
- GList *children;
+ GList *children, *next;
g_return_if_fail(container != NULL);
g_return_if_fail(IS_COLUMNS(container));
for (children = cols->children;
children && (child = children->data);
- children = children->next)
+ children = next) {
+ /*
+ * We can't wait until after the callback to assign
+ * `children = children->next', because the callback might
+ * be gtk_widget_destroy, which would remove the link
+ * `children' from the list! So instead we must get our
+ * hands on the value of the `next' pointer _before_ the
+ * callback.
+ */
+ next = children->next;
if (child->widget)
callback(child->widget, callback_data);
+ }
}
-static GtkType columns_child_type(GtkContainer *container)
+static GType columns_child_type(GtkContainer *container)
{
return GTK_TYPE_WIDGET;
}
{
Columns *cols;
+#if !GTK_CHECK_VERSION(2,0,0)
cols = gtk_type_new(columns_get_type());
+#else
+ cols = g_object_new(TYPE_COLUMNS, NULL);
+#endif
+
cols->spacing = spacing;
return GTK_WIDGET(cols);
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);
gtk_widget_set_parent(child, GTK_WIDGET(cols));
- if (GTK_WIDGET_REALIZED(cols))
+#if GTK_CHECK_VERSION(2,0,0)
+ gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);
+#endif
+
+ 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)
cols->taborder = g_list_remove_link(cols->taborder, children);
g_list_free(children);
cols->taborder = g_list_append(cols->taborder, widget);
+#if GTK_CHECK_VERSION(2,0,0)
+ gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);
+#endif
break;
}
}
+#if !GTK_CHECK_VERSION(2,0,0)
/*
* Override GtkContainer's focus movement so the user can
* explicitly specify the tab order.
} else
return columns_inherited_focus(container, 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);
+#ifdef COLUMNS_WIDTH_DIAGNOSTICS
+ printf("compute_width(%p): start\n", cols);
+#endif
- cols = COLUMNS(widget);
-
- 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;
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
* 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;
* 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;
/*
* 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
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