+static void
+gtk_box_get_size (GtkWidget *widget,
+ GtkOrientation orientation,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkBox *box;
+ GtkBoxPrivate *private;
+ GList *children;
+ gint nvis_children;
+ gint minimum, natural;
+
+ box = GTK_BOX (widget);
+ private = box->priv;
+
+ minimum = natural = 0;
+
+ nvis_children = 0;
+
+ for (children = private->children; children; children = children->next)
+ {
+ GtkBoxChild *child = children->data;
+
+ if (gtk_widget_get_visible (child->widget))
+ {
+ gint child_minimum, child_natural;
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ gtk_widget_get_preferred_width (child->widget,
+ &child_minimum, &child_natural);
+ else
+ gtk_widget_get_preferred_height (child->widget,
+ &child_minimum, &child_natural);
+
+ if (private->orientation == orientation)
+ {
+ if (private->homogeneous)
+ {
+ gint largest;
+
+ largest = child_minimum + child->padding * 2;
+ minimum = MAX (minimum, largest);
+
+ largest = child_natural + child->padding * 2;
+ natural = MAX (natural, largest);
+ }
+ else
+ {
+ minimum += child_minimum + child->padding * 2;
+ natural += child_natural + child->padding * 2;
+ }
+ }
+ else
+ {
+ /* The biggest mins and naturals in the opposing orientation */
+ minimum = MAX (minimum, child_minimum);
+ natural = MAX (natural, child_natural);
+ }
+
+ nvis_children += 1;
+ }
+ }
+
+ if (nvis_children > 0 && private->orientation == orientation)
+ {
+ if (private->homogeneous)
+ {
+ minimum *= nvis_children;
+ natural *= nvis_children;
+ }
+ minimum += (nvis_children - 1) * private->spacing;
+ natural += (nvis_children - 1) * private->spacing;
+ }
+
+ if (minimum_size)
+ *minimum_size = minimum;
+
+ if (natural_size)
+ *natural_size = natural;
+}
+
+static void
+gtk_box_get_preferred_width (GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ gtk_box_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
+}
+
+static void
+gtk_box_get_preferred_height (GtkWidget *widget,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ gtk_box_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
+}
+
+static void
+gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
+ gint avail_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkBoxPrivate *private = box->priv;
+ GtkBoxChild *child;
+ GList *children;
+ gint nvis_children;
+ gint nexpand_children;
+ gint computed_minimum = 0, computed_natural = 0;
+ GtkRequestedSize *sizes;
+ GtkPackType packing;
+ gint size, extra, i;
+ gint child_size, child_minimum, child_natural;
+ gint n_extra_widgets = 0;
+
+ count_expand_children (box, &nvis_children, &nexpand_children);
+
+ if (nvis_children <= 0)
+ return;
+
+ sizes = g_newa (GtkRequestedSize, nvis_children);
+ size = avail_size - (nvis_children - 1) * private->spacing;
+
+ /* Retrieve desired size for visible children */
+ for (i = 0, children = private->children; children; children = children->next)
+ {
+ child = children->data;
+
+ if (gtk_widget_get_visible (child->widget))
+ {
+ if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
+ gtk_widget_get_preferred_width (child->widget,
+ &sizes[i].minimum_size,
+ &sizes[i].natural_size);
+ else
+ gtk_widget_get_preferred_height (child->widget,
+ &sizes[i].minimum_size,
+ &sizes[i].natural_size);
+
+ /* Assert the api is working properly */
+ if (sizes[i].minimum_size < 0)
+ g_error ("GtkBox child %s minimum %s: %d < 0",
+ gtk_widget_get_name (GTK_WIDGET (child->widget)),
+ (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
+ sizes[i].minimum_size);
+
+ if (sizes[i].natural_size < sizes[i].minimum_size)
+ g_error ("GtkBox child %s natural %s: %d < minimum %d",
+ gtk_widget_get_name (GTK_WIDGET (child->widget)),
+ (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
+ sizes[i].natural_size,
+ sizes[i].minimum_size);
+
+ size -= sizes[i].minimum_size;
+ size -= child->padding * 2;
+
+ sizes[i].data = child;
+
+ i += 1;
+ }
+ }
+
+ if (private->homogeneous)
+ {
+ /* If were homogenous we still need to run the above loop to get the
+ * minimum sizes for children that are not going to fill
+ */
+ size = avail_size - (nvis_children - 1) * private->spacing;
+ extra = size / nvis_children;
+ n_extra_widgets = size % nvis_children;
+ }
+ else
+ {
+ /* Bring children up to size first */
+ size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
+
+ /* Calculate space which hasn't distributed yet,
+ * and is available for expanding children.
+ */
+ if (nexpand_children > 0)
+ {
+ extra = size / nexpand_children;
+ n_extra_widgets = size % nexpand_children;
+ }
+ else
+ extra = 0;
+ }
+
+ /* Allocate child positions. */
+ for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
+ {
+ for (i = 0, children = private->children;
+ children;
+ children = children->next)
+ {
+ child = children->data;
+
+ /* If widget is not visible, skip it. */
+ if (!gtk_widget_get_visible (child->widget))
+ continue;
+
+ /* If widget is packed differently skip it, but still increment i,
+ * since widget is visible and will be handled in next loop iteration.
+ */
+ if (child->pack != packing)
+ {
+ i++;
+ continue;
+ }
+
+ if (child->pack == packing)
+ {
+ /* Assign the child's size. */
+ if (private->homogeneous)
+ {
+ child_size = extra;
+
+ if (n_extra_widgets > 0)
+ {
+ child_size++;
+ n_extra_widgets--;
+ }
+ }
+ else
+ {
+ child_size = sizes[i].minimum_size + child->padding * 2;
+
+ if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
+ {
+ child_size += extra;
+
+ if (n_extra_widgets > 0)
+ {
+ child_size++;
+ n_extra_widgets--;
+ }
+ }
+ }
+
+ if (child->fill)
+ {
+ child_size = MAX (1, child_size - child->padding * 2);
+ }
+ else
+ {
+ child_size = sizes[i].minimum_size;
+ }
+
+
+ /* Assign the child's position. */
+ if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
+ gtk_widget_get_preferred_height_for_width (child->widget,
+ child_size, &child_minimum, &child_natural);
+ else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
+ gtk_widget_get_preferred_width_for_height (child->widget,
+ child_size, &child_minimum, &child_natural);
+
+
+ computed_minimum = MAX (computed_minimum, child_minimum);
+ computed_natural = MAX (computed_natural, child_natural);
+ }
+ i += 1;
+ }
+ }
+
+ if (minimum_size)
+ *minimum_size = computed_minimum;
+ if (natural_size)
+ *natural_size = computed_natural;
+}
+
+static void
+gtk_box_compute_size_for_orientation (GtkBox *box,
+ gint avail_size,
+ gint *minimum_size,
+ gint *natural_size)
+{
+ GtkBoxPrivate *private = box->priv;
+ GList *children;
+ gint nvis_children = 0;
+ gint required_size = 0, required_natural = 0, child_size, child_natural;
+ gint largest_child = 0, largest_natural = 0;
+
+ for (children = private->children; children != NULL;
+ children = children->next)
+ {
+ GtkBoxChild *child = children->data;
+
+ if (gtk_widget_get_visible (child->widget))
+ {
+
+ if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
+ gtk_widget_get_preferred_width_for_height (child->widget,
+ avail_size, &child_size, &child_natural);
+ else
+ gtk_widget_get_preferred_height_for_width (child->widget,
+ avail_size, &child_size, &child_natural);
+
+
+ child_size += child->padding * 2;
+ child_natural += child->padding * 2;
+
+ if (child_size > largest_child)
+ largest_child = child_size;
+
+ if (child_natural > largest_natural)
+ largest_natural = child_natural;
+
+ required_size += child_size;
+ required_natural += child_natural;
+
+ nvis_children += 1;
+ }
+ }
+
+ if (nvis_children > 0)
+ {
+ if (private->homogeneous)
+ {
+ required_size = largest_child * nvis_children;
+ required_natural = largest_natural * nvis_children;
+ }
+
+ required_size += (nvis_children - 1) * private->spacing;
+ required_natural += (nvis_children - 1) * private->spacing;
+ }
+
+ if (minimum_size)
+ *minimum_size = required_size;
+
+ if (natural_size)
+ *natural_size = required_natural;
+}
+
+static void
+gtk_box_get_preferred_width_for_height (GtkWidget *widget,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ GtkBox *box = GTK_BOX (widget);
+ GtkBoxPrivate *private = box->priv;
+
+ if (private->orientation == GTK_ORIENTATION_VERTICAL)
+ gtk_box_compute_size_for_opposing_orientation (box, height, minimum_width, natural_width);
+ else
+ gtk_box_compute_size_for_orientation (box, height, minimum_width, natural_width);
+}
+
+static void
+gtk_box_get_preferred_height_for_width (GtkWidget *widget,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ GtkBox *box = GTK_BOX (widget);
+ GtkBoxPrivate *private = box->priv;
+
+ if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
+ gtk_box_compute_size_for_opposing_orientation (box, width, minimum_height, natural_height);
+ else
+ gtk_box_compute_size_for_orientation (box, width, minimum_height, natural_height);
+}
+