+
+static void
+count_expand_children (GtkBox *box,
+ gint *visible_children,
+ gint *expand_children)
+{
+ GtkBoxPrivate *private = box->priv;
+ GList *children;
+ GtkBoxChild *child;
+
+ *visible_children = *expand_children = 0;
+
+ for (children = private->children; children; children = children->next)
+ {
+ child = children->data;
+
+ if (gtk_widget_get_visible (child->widget))
+ {
+ *visible_children += 1;
+ if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
+ *expand_children += 1;
+ }
+ }
+}
+
+static void
+gtk_box_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkBox *box = GTK_BOX (widget);
+ GtkBoxPrivate *private = box->priv;
+ GtkBoxChild *child;
+ GList *children;
+ gint nvis_children;
+ gint nexpand_children;
+
+ GtkTextDirection direction;
+ GtkAllocation child_allocation;
+ GtkRequestedSize *sizes;
+
+ GtkPackType packing;
+
+ gint size;
+ gint extra;
+ gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */
+ gint x = 0, y = 0, i;
+ gint child_size;
+
+
+ gtk_widget_set_allocation (widget, allocation);
+
+ count_expand_children (box, &nvis_children, &nexpand_children);
+
+ /* If there is no visible child, simply return. */
+ if (nvis_children <= 0)
+ return;
+
+ direction = gtk_widget_get_direction (widget);
+ sizes = g_newa (GtkRequestedSize, nvis_children);
+
+ if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
+ size = allocation->width - (nvis_children - 1) * private->spacing;
+ else
+ size = allocation->height - (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))
+ continue;
+
+ if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
+ gtk_widget_get_preferred_width_for_height (child->widget,
+ allocation->height,
+ &sizes[i].minimum_size,
+ &sizes[i].natural_size);
+ else
+ gtk_widget_get_preferred_height_for_width (child->widget,
+ allocation->width,
+ &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 for %s %d",
+ gtk_widget_get_name (GTK_WIDGET (child->widget)),
+ (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
+ sizes[i].minimum_size,
+ (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
+ (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
+
+ if (sizes[i].natural_size < sizes[i].minimum_size)
+ g_error ("GtkBox child %s natural %s: %d < minimum %d for %s %d",
+ gtk_widget_get_name (GTK_WIDGET (child->widget)),
+ (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
+ sizes[i].natural_size,
+ sizes[i].minimum_size,
+ (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
+ (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
+
+ size -= sizes[i].minimum_size;
+ size -= child->padding * 2;
+
+ sizes[i].data = child;
+
+ i++;
+ }
+
+ 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
+ */
+ if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
+ size = allocation->width - (nvis_children - 1) * private->spacing;
+ else
+ size = allocation->height - (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)
+ {
+ if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ child_allocation.y = allocation->y;
+ child_allocation.height = MAX (1, allocation->height);
+ if (packing == GTK_PACK_START)
+ x = allocation->x;
+ else
+ x = allocation->x + allocation->width;
+ }
+ else
+ {
+ child_allocation.x = allocation->x;
+ child_allocation.width = MAX (1, allocation->width);
+ if (packing == GTK_PACK_START)
+ y = allocation->y;
+ else
+ y = allocation->y + allocation->height;
+ }
+
+ 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;
+ }
+
+ /* 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--;
+ }
+ }
+ }
+
+ /* Assign the child's position. */
+ if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ if (child->fill)
+ {
+ child_allocation.width = MAX (1, child_size - child->padding * 2);
+ child_allocation.x = x + child->padding;
+ }
+ else
+ {
+ child_allocation.width = sizes[i].minimum_size;
+ child_allocation.x = x + (child_size - child_allocation.width) / 2;
+ }
+
+ if (packing == GTK_PACK_START)
+ {
+ x += child_size + private->spacing;
+ }
+ else
+ {
+ x -= child_size + private->spacing;
+
+ child_allocation.x -= child_size;
+ }
+
+ if (direction == GTK_TEXT_DIR_RTL)
+ child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
+
+ }
+ else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
+ {
+ if (child->fill)
+ {
+ child_allocation.height = MAX (1, child_size - child->padding * 2);
+ child_allocation.y = y + child->padding;
+ }
+ else
+ {
+ child_allocation.height = sizes[i].minimum_size;
+ child_allocation.y = y + (child_size - child_allocation.height) / 2;
+ }
+
+ if (packing == GTK_PACK_START)
+ {
+ y += child_size + private->spacing;
+ }
+ else
+ {
+ y -= child_size + private->spacing;
+
+ child_allocation.y -= child_size;
+ }
+ }
+ gtk_widget_size_allocate (child->widget, &child_allocation);
+
+ i++;
+ }
+ }
+}
+
+static void
+gtk_box_compute_expand (GtkWidget *widget,
+ gboolean *hexpand_p,
+ gboolean *vexpand_p)
+{
+ GtkBoxPrivate *private = GTK_BOX (widget)->priv;
+ GList *children;
+ GtkBoxChild *child;
+ gboolean our_expand;
+ gboolean opposite_expand;
+ GtkOrientation opposite_orientation;
+
+ if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
+ opposite_orientation = GTK_ORIENTATION_VERTICAL;
+ else
+ opposite_orientation = GTK_ORIENTATION_HORIZONTAL;
+
+ our_expand = FALSE;
+ opposite_expand = FALSE;
+
+ for (children = private->children; children; children = children->next)
+ {
+ child = children->data;
+
+ /* we don't recurse into children anymore as soon as we know
+ * expand=TRUE in an orientation
+ */
+
+ if (child->expand || (!our_expand && gtk_widget_compute_expand (child->widget, private->orientation)))
+ our_expand = TRUE;
+
+ if (!opposite_expand && gtk_widget_compute_expand (child->widget, opposite_orientation))
+ opposite_expand = TRUE;
+
+ if (our_expand && opposite_expand)
+ break;
+ }
+
+ if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ *hexpand_p = our_expand;
+ *vexpand_p = opposite_expand;
+ }
+ else
+ {
+ *hexpand_p = opposite_expand;
+ *vexpand_p = our_expand;
+ }
+}
+
+static GType
+gtk_box_child_type (GtkContainer *container)