gtkbox.c 55 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2 3 4
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
16
 */
17 18

/*
19
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20 21
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
22
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 24
 */

25 26
/**
 * SECTION:gtkbox
27
 * @Short_description: A container box
28
 * @Title: GtkBox
29
 * @See_also: #GtkFrame, #GtkGrid, #GtkLayout
30
 *
31
 * The GtkBox widget organizes child widgets into a rectangular area.
32 33
 *
 * The rectangular area of a GtkBox is organized into either a single row
34
 * or a single column of child widgets depending upon the orientation.
35 36
 * Thus, all children of a GtkBox are allocated one dimension in common,
 * which is the height of a row, or the width of a column.
37
 *
38 39 40
 * GtkBox uses a notion of <emphasis>packing</emphasis>. Packing refers
 * to adding widgets with reference to a particular position in a
 * #GtkContainer. For a GtkBox, there are two reference positions: the
41
 * <emphasis>start</emphasis> and the <emphasis>end</emphasis> of the box.
42 43 44
 * For a vertical #GtkBox, the start is defined as the top of the box and
 * the end is defined as the bottom. For a horizontal #GtkBox the start
 * is defined as the left side and the end is defined as the right side.
45 46
 *
 * Use repeated calls to gtk_box_pack_start() to pack widgets into a
47 48
 * GtkBox from start to end. Use gtk_box_pack_end() to add widgets from
 * end to start. You may intersperse these calls and add widgets from
49 50
 * both ends of the same GtkBox.
 *
51 52 53 54
 * Because GtkBox is a #GtkContainer, you may also use gtk_container_add()
 * to insert widgets into the box, and they will be packed with the default
 * values for #GtkBox:expand and #GtkBox:fill. Use gtk_container_remove()
 * to remove widgets from the GtkBox.
55 56 57 58 59
 *
 * Use gtk_box_set_homogeneous() to specify whether or not all children
 * of the GtkBox are forced to get the same amount of space.
 *
 * Use gtk_box_set_spacing() to determine how much space will be
60 61 62 63
 * minimally placed between all children in the GtkBox. Note that
 * spacing is added <emphasis>between</emphasis> the children, while
 * padding added by gtk_box_pack_start() or gtk_box_pack_end() is added
 * <emphasis>on either side</emphasis> of the widget it belongs to.
64 65 66 67 68 69 70
 *
 * Use gtk_box_reorder_child() to move a GtkBox child to a different
 * place in the box.
 *
 * Use gtk_box_set_child_packing() to reset the #GtkBox:expand,
 * #GtkBox:fill and #GtkBox:padding child properties.
 * Use gtk_box_query_child_packing() to query these fields.
71
 *
72 73 74 75
 * <note><para>
 * Note that a single-row or single-column #GtkGrid provides exactly
 * the same functionality as #GtkBox.
 * </para></note>
76 77
 */

78
#include "config.h"
79

Elliot Lee's avatar
Elliot Lee committed
80
#include "gtkbox.h"
81
#include "gtkboxprivate.h"
82
#include "gtkintl.h"
83
#include "gtkorientable.h"
84
#include "gtkprivate.h"
85 86 87
#include "gtktypebuiltins.h"
#include "gtksizerequest.h"
#include "gtkwidgetpath.h"
88
#include "gtkwidgetprivate.h"
89
#include "a11y/gtkboxaccessible.h"
90

Elliot Lee's avatar
Elliot Lee committed
91

92
enum {
93
  PROP_0,
94
  PROP_ORIENTATION,
95 96
  PROP_SPACING,
  PROP_HOMOGENEOUS
97
};
Elliot Lee's avatar
Elliot Lee committed
98

99
enum {
Tim Janik's avatar
Tim Janik committed
100 101 102 103 104 105
  CHILD_PROP_0,
  CHILD_PROP_EXPAND,
  CHILD_PROP_FILL,
  CHILD_PROP_PADDING,
  CHILD_PROP_PACK_TYPE,
  CHILD_PROP_POSITION
106 107
};

108
struct _GtkBoxPrivate
109
{
110 111
  GList          *children;

112
  GtkOrientation  orientation;
113 114 115 116 117
  gint16          spacing;

  guint           default_expand : 1;
  guint           homogeneous    : 1;
  guint           spacing_set    : 1;
118 119
};

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
typedef struct _GtkBoxChild        GtkBoxChild;

/*
 * GtkBoxChild:
 * @widget: the child widget, packed into the GtkBox.
 * @padding: the number of extra pixels to put between this child and its
 *  neighbors, set when packed, zero by default.
 * @expand: flag indicates whether extra space should be given to this child.
 *  Any extra space given to the parent GtkBox is divided up among all children
 *  with this attribute set to %TRUE; set when packed, %TRUE by default.
 * @fill: flag indicates whether any extra space given to this child due to its
 *  @expand attribute being set is actually allocated to the child, rather than
 *  being used as padding around the widget; set when packed, %TRUE by default.
 * @pack: one of #GtkPackType indicating whether the child is packed with
 *  reference to the start (top/left) or end (bottom/right) of the GtkBox.
 */
struct _GtkBoxChild
{
  GtkWidget *widget;

  guint16    padding;

  guint      expand : 1;
  guint      fill   : 1;
  guint      pack   : 1;
};
146 147 148

static void gtk_box_size_allocate         (GtkWidget              *widget,
                                           GtkAllocation          *allocation);
149

150 151 152
static void gtk_box_compute_expand     (GtkWidget      *widget,
                                        gboolean       *hexpand,
                                        gboolean       *vexpand);
153 154
static void gtk_box_direction_changed  (GtkWidget        *widget,
                                        GtkTextDirection  previous_direction);
155

156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
static void gtk_box_set_property       (GObject        *object,
                                        guint           prop_id,
                                        const GValue   *value,
                                        GParamSpec     *pspec);
static void gtk_box_get_property       (GObject        *object,
                                        guint           prop_id,
                                        GValue         *value,
                                        GParamSpec     *pspec);
static void gtk_box_add                (GtkContainer   *container,
                                        GtkWidget      *widget);
static void gtk_box_remove             (GtkContainer   *container,
                                        GtkWidget      *widget);
static void gtk_box_forall             (GtkContainer   *container,
                                        gboolean        include_internals,
                                        GtkCallback     callback,
                                        gpointer        callback_data);
static void gtk_box_set_child_property (GtkContainer   *container,
                                        GtkWidget      *child,
                                        guint           property_id,
                                        const GValue   *value,
                                        GParamSpec     *pspec);
static void gtk_box_get_child_property (GtkContainer   *container,
                                        GtkWidget      *child,
                                        guint           property_id,
                                        GValue         *value,
                                        GParamSpec     *pspec);
static GType gtk_box_child_type        (GtkContainer   *container);
183 184 185
static GtkWidgetPath * gtk_box_get_path_for_child
                                       (GtkContainer   *container,
                                        GtkWidget      *child);
Elliot Lee's avatar
Elliot Lee committed
186

187

188 189 190 191 192 193 194 195 196 197 198 199 200 201
static void               gtk_box_get_preferred_width            (GtkWidget           *widget,
                                                                  gint                *minimum_size,
                                                                  gint                *natural_size);
static void               gtk_box_get_preferred_height           (GtkWidget           *widget,
                                                                  gint                *minimum_size,
                                                                  gint                *natural_size);
static void               gtk_box_get_preferred_width_for_height (GtkWidget           *widget,
                                                                  gint                 height,
                                                                  gint                *minimum_width,
                                                                  gint                *natural_width);
static void               gtk_box_get_preferred_height_for_width (GtkWidget           *widget,
                                                                  gint                 width,
                                                                  gint                *minimum_height,
                                                                  gint                *natural_height);
202

Matthias Clasen's avatar
Matthias Clasen committed
203

204 205
G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
206
                                                NULL))
Elliot Lee's avatar
Elliot Lee committed
207 208 209 210

static void
gtk_box_class_init (GtkBoxClass *class)
{
211 212
  GObjectClass *object_class = G_OBJECT_CLASS (class);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
Tim Janik's avatar
Tim Janik committed
213
  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
214

215 216 217
  object_class->set_property = gtk_box_set_property;
  object_class->get_property = gtk_box_get_property;

218 219 220 221 222
  widget_class->size_allocate                  = gtk_box_size_allocate;
  widget_class->get_preferred_width            = gtk_box_get_preferred_width;
  widget_class->get_preferred_height           = gtk_box_get_preferred_height;
  widget_class->get_preferred_height_for_width = gtk_box_get_preferred_height_for_width;
  widget_class->get_preferred_width_for_height = gtk_box_get_preferred_width_for_height;
223
  widget_class->compute_expand                 = gtk_box_compute_expand;
224
  widget_class->direction_changed              = gtk_box_direction_changed;
225

Tim Janik's avatar
Tim Janik committed
226 227 228 229 230 231
  container_class->add = gtk_box_add;
  container_class->remove = gtk_box_remove;
  container_class->forall = gtk_box_forall;
  container_class->child_type = gtk_box_child_type;
  container_class->set_child_property = gtk_box_set_child_property;
  container_class->get_child_property = gtk_box_get_child_property;
232
  container_class->get_path_for_child = gtk_box_get_path_for_child;
233
  gtk_container_class_handle_border_width (container_class);
Tim Janik's avatar
Tim Janik committed
234

235 236 237 238 239
  g_object_class_override_property (object_class,
                                    PROP_ORIENTATION,
                                    "orientation");

  g_object_class_install_property (object_class,
240 241
                                   PROP_SPACING,
                                   g_param_spec_int ("spacing",
242 243
                                                     P_("Spacing"),
                                                     P_("The amount of space between children"),
244 245 246
                                                     0,
                                                     G_MAXINT,
                                                     0,
247
                                                     GTK_PARAM_READWRITE));
248

249
  g_object_class_install_property (object_class,
250 251
                                   PROP_HOMOGENEOUS,
                                   g_param_spec_boolean ("homogeneous",
252 253
							 P_("Homogeneous"),
							 P_("Whether the children should all be the same size"),
254
							 FALSE,
255
							 GTK_PARAM_READWRITE));
256

257 258 259 260 261 262 263 264
  /**
   * GtkBox:expand:
   *
   * Whether the child should receive extra space when the parent grows.
   *
   * Note that the default value for this property is %FALSE for GtkBox,
   * but #GtkHBox, #GtkVBox and other subclasses use the old default
   * of %TRUE.
265 266 267 268
   *
   * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand
   * and #GtkWidget:vexpand properties are the preferred way to influence
   * child size allocation in containers.
269
   */
Tim Janik's avatar
Tim Janik committed
270 271
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_EXPAND,
272 273
					      g_param_spec_boolean ("expand",
								    P_("Expand"),
274
								    P_("Whether the child should receive extra space when the parent grows"),
275
								    FALSE,
276
								    GTK_PARAM_READWRITE));
277 278 279 280 281

  /**
   * GtkBox:fill:
   *
   * Whether the child should receive extra space when the parent grows.
282 283 284 285
   *
   * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand
   * and #GtkWidget:vexpand properties are the preferred way to influence
   * child size allocation in containers.
286
   */
Tim Janik's avatar
Tim Janik committed
287 288
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_FILL,
289 290
					      g_param_spec_boolean ("fill",
								    P_("Fill"),
291
								    P_("Whether extra space given to the child should be allocated to the child or used as padding"),
292
								    TRUE,
293
								    GTK_PARAM_READWRITE));
294

Tim Janik's avatar
Tim Janik committed
295 296
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_PADDING,
297 298
					      g_param_spec_uint ("padding",
								 P_("Padding"),
299
								 P_("Extra space to put between the child and its neighbors, in pixels"),
Tim Janik's avatar
Tim Janik committed
300
								 0, G_MAXINT, 0,
301
								 GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
302 303
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_PACK_TYPE,
304
					      g_param_spec_enum ("pack-type",
305
								 P_("Pack type"),
306
								 P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
Tim Janik's avatar
Tim Janik committed
307
								 GTK_TYPE_PACK_TYPE, GTK_PACK_START,
308
								 GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
309 310
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_POSITION,
311 312
					      g_param_spec_int ("position",
								P_("Position"),
313
								P_("The index of the child in the parent"),
Tim Janik's avatar
Tim Janik committed
314
								-1, G_MAXINT, 0,
315
								GTK_PARAM_READWRITE));
316

317
  g_type_class_add_private (object_class, sizeof (GtkBoxPrivate));
318 319

  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_BOX_ACCESSIBLE);
Elliot Lee's avatar
Elliot Lee committed
320 321 322 323 324
}

static void
gtk_box_init (GtkBox *box)
{
325
  GtkBoxPrivate *private;
326 327 328

  box->priv = G_TYPE_INSTANCE_GET_PRIVATE (box,
                                           GTK_TYPE_BOX,
329
                                           GtkBoxPrivate);
330
  private = box->priv;
331

332
  gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
333
  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), FALSE);
334 335

  private->orientation = GTK_ORIENTATION_HORIZONTAL;
336 337
  private->children = NULL;

338
  private->default_expand = FALSE;
339 340
  private->homogeneous = FALSE;
  private->spacing = 0;
341
  private->spacing_set = FALSE;
Elliot Lee's avatar
Elliot Lee committed
342 343
}

344 345 346 347 348
static void
gtk_box_set_property (GObject      *object,
                      guint         prop_id,
                      const GValue *value,
                      GParamSpec   *pspec)
349
{
350
  GtkBox *box = GTK_BOX (object);
351
  GtkBoxPrivate *private = box->priv;
352

353
  switch (prop_id)
354
    {
355 356 357 358
    case PROP_ORIENTATION:
      private->orientation = g_value_get_enum (value);
      gtk_widget_queue_resize (GTK_WIDGET (box));
      break;
359 360
    case PROP_SPACING:
      gtk_box_set_spacing (box, g_value_get_int (value));
361
      break;
362 363
    case PROP_HOMOGENEOUS:
      gtk_box_set_homogeneous (box, g_value_get_boolean (value));
364
      break;
Tim Janik's avatar
Tim Janik committed
365
    default:
366
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
367
      break;
368 369 370
    }
}

371 372 373 374 375
static void
gtk_box_get_property (GObject    *object,
                      guint       prop_id,
                      GValue     *value,
                      GParamSpec *pspec)
376
{
377
  GtkBox *box = GTK_BOX (object);
378
  GtkBoxPrivate *private = box->priv;
379

380
  switch (prop_id)
381
    {
382 383 384
    case PROP_ORIENTATION:
      g_value_set_enum (value, private->orientation);
      break;
385
    case PROP_SPACING:
386
      g_value_set_int (value, private->spacing);
387
      break;
388
    case PROP_HOMOGENEOUS:
389
      g_value_set_boolean (value, private->homogeneous);
390 391
      break;
    default:
392
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
393 394 395 396
      break;
    }
}

397

398
static void
399 400 401
count_expand_children (GtkBox *box,
                       gint *visible_children,
                       gint *expand_children)
402
{
403
  GtkBoxPrivate  *private = box->priv;
404
  GList       *children;
405 406
  GtkBoxChild *child;

407
  *visible_children = *expand_children = 0;
408

409
  for (children = private->children; children; children = children->next)
410 411 412
    {
      child = children->data;

413
      if (gtk_widget_get_visible (child->widget))
414
	{
415
	  *visible_children += 1;
416
	  if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
417
	    *expand_children += 1;
418 419
	}
    }
420
}
421 422 423 424 425 426

static void
gtk_box_size_allocate (GtkWidget     *widget,
                       GtkAllocation *allocation)
{
  GtkBox *box = GTK_BOX (widget);
427
  GtkBoxPrivate *private = box->priv;
428 429
  GtkBoxChild *child;
  GList *children;
430 431
  gint nvis_children;
  gint nexpand_children;
432

433 434 435 436 437 438 439 440 441 442 443 444 445
  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;


446
  gtk_widget_set_allocation (widget, allocation);
447

448
  count_expand_children (box, &nvis_children, &nexpand_children);
449

450 451 452 453 454 455 456 457
  /* 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)
458
    size = allocation->width - (nvis_children - 1) * private->spacing;
459
  else
460
    size = allocation->height - (nvis_children - 1) * private->spacing;
461 462 463

  /* Retrieve desired size for visible children. */
  for (i = 0, children = private->children; children; children = children->next)
464
    {
465
      child = children->data;
466

467 468
      if (!gtk_widget_get_visible (child->widget))
	continue;
469

470
      if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
471 472 473 474
	gtk_widget_get_preferred_width_for_height (child->widget,
                                                   allocation->height,
                                                   &sizes[i].minimum_size,
                                                   &sizes[i].natural_size);
475
      else
476 477 478 479
	gtk_widget_get_preferred_height_for_width (child->widget,
                                                   allocation->width,
                                                   &sizes[i].minimum_size,
                                                   &sizes[i].natural_size);
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506


      /* 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++;
    }
507

508 509 510 511 512
  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
       */
513
      if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
514
	size = allocation->width - (nvis_children - 1) * private->spacing;
515
      else
516
	size = allocation->height - (nvis_children - 1) * private->spacing;
517

518 519 520 521 522 523
      extra = size / nvis_children;
      n_extra_widgets = size % nvis_children;
    }
  else
    {
      /* Bring children up to size first */
524
      size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
525

526 527 528 529 530 531 532
      /* 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;
533
	}
534 535 536
      else
	extra = 0;
    }
537

538 539 540 541
  /* Allocate child positions. */
  for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
    {
      if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
542
	{
543 544
	  child_allocation.y = allocation->y;
	  child_allocation.height = MAX (1, allocation->height);
545
	  if (packing == GTK_PACK_START)
546
	    x = allocation->x;
547
	  else
548
	    x = allocation->x + allocation->width;
549
	}
550
      else
551
	{
552 553
	  child_allocation.x = allocation->x;
	  child_allocation.width = MAX (1, allocation->width);
554
	  if (packing == GTK_PACK_START)
555
	    y = allocation->y;
556
	  else
557
	    y = allocation->y + allocation->height;
558
	}
559

560 561 562 563 564
      for (i = 0, children = private->children;
	   children;
	   children = children->next)
	{
	  child = children->data;
565

566 567
	  /* If widget is not visible, skip it. */
	  if (!gtk_widget_get_visible (child->widget))
568
	    continue;
569

570 571 572 573 574 575 576 577 578
	  /* 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;
	    }

579 580
	  /* Assign the child's size. */
	  if (private->homogeneous)
581
	    {
582 583 584 585 586 587 588 589 590 591 592 593
	      child_size = extra;

	      if (n_extra_widgets > 0)
		{
		  child_size++;
		  n_extra_widgets--;
		}
	    }
	  else
	    {
	      child_size = sizes[i].minimum_size + child->padding * 2;

594
	      if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
		{
		  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;

634
	    }
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
	  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++;
662 663 664 665
	}
    }
}

666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
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;
    }
}

Manish Singh's avatar
Manish Singh committed
716 717
static GType
gtk_box_child_type (GtkContainer   *container)
718 719 720 721 722
{
  return GTK_TYPE_WIDGET;
}

static void
723 724 725 726 727
gtk_box_set_child_property (GtkContainer *container,
                            GtkWidget    *child,
                            guint         property_id,
                            const GValue *value,
                            GParamSpec   *pspec)
728 729 730 731 732 733
{
  gboolean expand = 0;
  gboolean fill = 0;
  guint padding = 0;
  GtkPackType pack_type = 0;

Tim Janik's avatar
Tim Janik committed
734
  if (property_id != CHILD_PROP_POSITION)
735 736 737 738 739 740
    gtk_box_query_child_packing (GTK_BOX (container),
				 child,
				 &expand,
				 &fill,
				 &padding,
				 &pack_type);
Tim Janik's avatar
Tim Janik committed
741
  switch (property_id)
742
    {
Tim Janik's avatar
Tim Janik committed
743
    case CHILD_PROP_EXPAND:
744 745
      gtk_box_set_child_packing (GTK_BOX (container),
				 child,
Tim Janik's avatar
Tim Janik committed
746
				 g_value_get_boolean (value),
747 748 749 750
				 fill,
				 padding,
				 pack_type);
      break;
Tim Janik's avatar
Tim Janik committed
751
    case CHILD_PROP_FILL:
752 753 754
      gtk_box_set_child_packing (GTK_BOX (container),
				 child,
				 expand,
Tim Janik's avatar
Tim Janik committed
755
				 g_value_get_boolean (value),
756 757 758
				 padding,
				 pack_type);
      break;
Tim Janik's avatar
Tim Janik committed
759
    case CHILD_PROP_PADDING:
760 761 762 763
      gtk_box_set_child_packing (GTK_BOX (container),
				 child,
				 expand,
				 fill,
Tim Janik's avatar
Tim Janik committed
764
				 g_value_get_uint (value),
765 766
				 pack_type);
      break;
Tim Janik's avatar
Tim Janik committed
767
    case CHILD_PROP_PACK_TYPE:
768 769 770 771 772
      gtk_box_set_child_packing (GTK_BOX (container),
				 child,
				 expand,
				 fill,
				 padding,
Tim Janik's avatar
Tim Janik committed
773
				 g_value_get_enum (value));
774
      break;
Tim Janik's avatar
Tim Janik committed
775
    case CHILD_PROP_POSITION:
776 777
      gtk_box_reorder_child (GTK_BOX (container),
			     child,
Tim Janik's avatar
Tim Janik committed
778
			     g_value_get_int (value));
779 780
      break;
    default:
Tim Janik's avatar
Tim Janik committed
781
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
782 783 784 785 786
      break;
    }
}

static void
Tim Janik's avatar
Tim Janik committed
787 788 789 790 791
gtk_box_get_child_property (GtkContainer *container,
			    GtkWidget    *child,
			    guint         property_id,
			    GValue       *value,
			    GParamSpec   *pspec)
792
{
793 794
  gboolean expand = FALSE;
  gboolean fill = FALSE;
795 796 797 798
  guint padding = 0;
  GtkPackType pack_type = 0;
  GList *list;

Tim Janik's avatar
Tim Janik committed
799
  if (property_id != CHILD_PROP_POSITION)
800 801 802 803 804 805
    gtk_box_query_child_packing (GTK_BOX (container),
				 child,
				 &expand,
				 &fill,
				 &padding,
				 &pack_type);
Tim Janik's avatar
Tim Janik committed
806
  switch (property_id)
807
    {
Tim Janik's avatar
Tim Janik committed
808 809 810
      guint i;
    case CHILD_PROP_EXPAND:
      g_value_set_boolean (value, expand);
811
      break;
Tim Janik's avatar
Tim Janik committed
812 813
    case CHILD_PROP_FILL:
      g_value_set_boolean (value, fill);
814
      break;
Tim Janik's avatar
Tim Janik committed
815 816
    case CHILD_PROP_PADDING:
      g_value_set_uint (value, padding);
817
      break;
Tim Janik's avatar
Tim Janik committed
818 819
    case CHILD_PROP_PACK_TYPE:
      g_value_set_enum (value, pack_type);
820
      break;
Tim Janik's avatar
Tim Janik committed
821 822
    case CHILD_PROP_POSITION:
      i = 0;
823
      for (list = GTK_BOX (container)->priv->children; list; list = list->next)
824 825 826 827 828 829
	{
	  GtkBoxChild *child_entry;

	  child_entry = list->data;
	  if (child_entry->widget == child)
	    break;
Tim Janik's avatar
Tim Janik committed
830
	  i++;
831
	}
Tim Janik's avatar
Tim Janik committed
832
      g_value_set_int (value, list ? i : -1);
833 834
      break;
    default:
Tim Janik's avatar
Tim Janik committed
835
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
836 837 838 839
      break;
    }
}

840 841 842 843
typedef struct _CountingData CountingData;
struct _CountingData {
  GtkWidget *widget;
  gboolean found;
844 845
  guint before;
  guint after;
846 847 848 849 850 851 852 853 854 855 856 857
};

static void
count_widget_position (GtkWidget *widget,
                       gpointer   data)
{
  CountingData *count = data;

  if (!gtk_widget_get_visible (widget))
    return;

  if (count->widget == widget)
858 859 860 861 862
    count->found = TRUE;
  else if (count->found)
    count->after++;
  else
    count->before++;
863 864
}

865 866
static gint
gtk_box_get_visible_position (GtkBox    *box,
867 868
                              GtkWidget *child)
{
869
  CountingData count = { child, FALSE, 0, 0 };
870

871 872 873 874 875 876 877 878 879 880 881
  /* foreach iterates in visible order */
  gtk_container_foreach (GTK_CONTAINER (box),
                         count_widget_position,
                         &count);

  /* the child wasn't found, it's likely an internal child of some
   * subclass, return -1 to indicate that there is no sibling relation
   * to the regular box children
   */
  if (!count.found)
    return -1;
882

883 884 885 886 887
  if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
      gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
    return count.after;
  else
    return count.before;
888 889 890 891 892 893
}

static GtkWidgetPath *
gtk_box_get_path_for_child (GtkContainer *container,
                            GtkWidget    *child)
{
894
  GtkWidgetPath *path, *sibling_path;
895 896
  GtkBox *box;
  GtkBoxPrivate *private;
897
  GList *list, *children;
898 899 900 901

  box = GTK_BOX (container);
  private = box->priv;

902
  path = _gtk_widget_create_path (GTK_WIDGET (container));
903 904

  if (gtk_widget_get_visible (child))
905
    {
906 907
      gint position;

908
      sibling_path = gtk_widget_path_new ();
909 910 911 912 913 914 915 916 917 918

      /* get_children works in visible order */
      children = gtk_container_get_children (container);
      if (private->orientation == GTK_ORIENTATION_HORIZONTAL &&
          gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
        children = g_list_reverse (children);

      for (list = children; list; list = list->next)
        {
          if (!gtk_widget_get_visible (list->data))
919 920
            continue;

921
          gtk_widget_path_append_for_widget (sibling_path, list->data);
922
        }
923

924 925 926 927 928 929 930 931 932 933
      g_list_free (children);

      position = gtk_box_get_visible_position (box, child);

      if (position >= 0)
        gtk_widget_path_append_with_siblings (path, sibling_path, position);
      else
        gtk_widget_path_append_for_widget (path, child);

      gtk_widget_path_unref (sibling_path);
934
    }
935 936 937 938 939 940
  else
    gtk_widget_path_append_for_widget (path, child);

  return path;
}

941 942 943 944 945 946
static void
gtk_box_invalidate_order_foreach (GtkWidget *widget)
{
  _gtk_widget_invalidate_style_context (widget, GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
}

947 948 949
static void
gtk_box_invalidate_order (GtkBox *box)
{
950
  gtk_container_foreach (GTK_CONTAINER (box),
951
                         (GtkCallback) gtk_box_invalidate_order_foreach,
952
                         NULL);
953 954
}

955 956 957 958 959 960
static void
gtk_box_direction_changed (GtkWidget        *widget,
                           GtkTextDirection  previous_direction)
{
  gtk_box_invalidate_order (GTK_BOX (widget));
}
961

962 963 964 965 966 967 968 969 970 971
static void
box_child_visibility_notify_cb (GObject *obj,
                                GParamSpec *pspec,
                                gpointer user_data)
{
  GtkBox *box = user_data;

  gtk_box_invalidate_order (box);
}

972 973 974 975 976 977 978 979
static void
gtk_box_pack (GtkBox      *box,
              GtkWidget   *child,
              gboolean     expand,
              gboolean     fill,
              guint        padding,
              GtkPackType  pack_type)
{
980
  GtkBoxPrivate *private = box->priv;
981 982 983 984
  GtkBoxChild *child_info;

  g_return_if_fail (GTK_IS_BOX (box));
  g_return_if_fail (GTK_IS_WIDGET (child));
985
  g_return_if_fail (gtk_widget_get_parent (child) == NULL);
986 987 988 989 990 991 992 993

  child_info = g_new (GtkBoxChild, 1);
  child_info->widget = child;
  child_info->padding = padding;
  child_info->expand = expand ? TRUE : FALSE;
  child_info->fill = fill ? TRUE : FALSE;
  child_info->pack = pack_type;

994
  private->children = g_list_append (private->children, child_info);
995 996 997

  gtk_widget_freeze_child_notify (child);

998
  gtk_box_invalidate_order (box);
999
  gtk_widget_set_parent (child, GTK_WIDGET (box));
1000

1001 1002 1003
  g_signal_connect (child, "notify::visible",
                    G_CALLBACK (box_child_visibility_notify_cb), box);

1004 1005 1006 1007 1008 1009 1010 1011
  gtk_widget_child_notify (child, "expand");
  gtk_widget_child_notify (child, "fill");
  gtk_widget_child_notify (child, "padding");
  gtk_widget_child_notify (child, "pack-type");
  gtk_widget_child_notify (child, "position");
  gtk_widget_thaw_child_notify (child);
}

1012
static void
1013 1014 1015 1016
gtk_box_get_size (GtkWidget      *widget,
		  GtkOrientation  orientation,
		  gint           *minimum_size,
		  gint           *natural_size)
1017 1018
{
  GtkBox *box;
1019
  GtkBoxPrivate *private;
1020 1021
  GList *children;
  gint nvis_children;
1022
  gint minimum, natural;
1023

1024
  box = GTK_BOX (widget);
1025
  private = box->priv;
1026

1027
  minimum = natural = 0;
1028 1029 1030

  nvis_children = 0;

1031
  for (children = private->children; children; children = children->next)
1032 1033
    {
      GtkBoxChild *child = children->data;
1034 1035 1036

      if (gtk_widget_get_visible (child->widget))
        {
1037
          gint child_minimum, child_natural;
1038

1039
	  if (orientation == GTK_ORIENTATION_HORIZONTAL)
1040 1041
	    gtk_widget_get_preferred_width (child->widget,
                                            &child_minimum, &child_natural);
1042
	  else
1043 1044
	    gtk_widget_get_preferred_height (child->widget,
                                             &child_minimum, &child_natural);
1045

1046 1047
          if (private->orientation == orientation)
	    {
1048
              if (private->homogeneous)
1049
                {
1050
                  gint largest;
1051

1052 1053
                  largest = child_minimum + child->padding * 2;
                  minimum = MAX (minimum, largest);
1054

1055 1056
                  largest = child_natural + child->padding * 2;
                  natural = MAX (natural, largest);
1057 1058 1059
                }
              else
                {
1060 1061
                  minimum += child_minimum + child->padding * 2;
                  natural += child_natural + child->padding * 2;
1062
                }
1063 1064 1065 1066 1067 1068 1069
	    }
	  else
	    {
	      /* The biggest mins and naturals in the opposing orientation */
              minimum = MAX (minimum, child_minimum);
              natural = MAX (natural, child_natural);
	    }
1070 1071 1072 1073

          nvis_children += 1;
        }
    }
1074 1075

  if (nvis_children > 0 && private->orientation == orientation)
1076
    {
1077
      if (private->homogeneous)
1078 1079 1080 1081
	{
	  minimum *= nvis_children;
	  natural *= nvis_children;
	}
1082 1083
      minimum += (nvis_children - 1) * private->spacing;
      natural += (nvis_children - 1) * private->spacing;
1084
    }
1085

1086 1087
  if (minimum_size)
    *minimum_size = minimum;
1088

1089 1090 1091
  if (natural_size)
    *natural_size = natural;
}
1092

1093
static void
1094 1095 1096
gtk_box_get_preferred_width (GtkWidget *widget,
                             gint      *minimum_size,
                             gint      *natural_size)
1097
{
1098
  gtk_box_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
1099 1100
}

1101
static void
1102 1103 1104
gtk_box_get_preferred_height (GtkWidget *widget,
                              gint      *minimum_size,
                              gint      *natural_size)
1105
{
1106
  gtk_box_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
1107
}
1108

1109
static void
1110 1111 1112 1113
gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
					       gint    avail_size,
					       gint   *minimum_size,
					       gint   *natural_size)
1114
{
1115
  GtkBoxPrivate       *private = box->priv;
1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
  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;
1126

1127
  count_expand_children (box, &nvis_children, &nexpand_children);
1128

1129 1130
  if (nvis_children <= 0)
    return;
1131

1132
  sizes = g_newa (GtkRequestedSize, nvis_children);
1133
  size = avail_size - (nvis_children - 1) * private->spacing;
1134

1135 1136 1137 1138
  /* Retrieve desired size for visible children */
  for (i = 0, children = private->children; children; children = children->next)
    {
      child = children->data;
1139

1140 1141 1142
      if (gtk_widget_get_visible (child->widget))
	{
	  if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1143 1144 1145
	    gtk_widget_get_preferred_width (child->widget,
                                            &sizes[i].minimum_size,
                                            &sizes[i].natural_size);
1146
	  else
1147 1148 1149
	    gtk_widget_get_preferred_height (child->widget,
                                             &sizes[i].minimum_size,
                                             &sizes[i].natural_size);
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170

	  /* 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;
1171
	}
1172
    }
1173

1174 1175 1176 1177 1178
  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
       */
1179
      size = avail_size - (nvis_children - 1) * private->spacing;
1180 1181 1182 1183 1184 1185
      extra = size / nvis_children;
      n_extra_widgets = size % nvis_children;
    }
  else
    {
      /* Bring children up to size first */
1186
      size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
1187 1188 1189 1190 1191

      /* Calculate space which hasn't distributed yet,
       * and is available for expanding children.
       */
      if (nexpand_children > 0)
1192
	{
1193 1194 1195
	  extra = size / nexpand_children;
	  n_extra_widgets = size % nexpand_children;
	}
1196
      else
1197 1198 1199 1200 1201 1202 1203 1204 1205
	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)
1206
	{
1207
	  child = children->data;
1208

1209 1210
	  /* If widget is not visible, skip it. */
	  if (!gtk_widget_get_visible (child->widget))
1211 1212
	    continue;

1213 1214 1215 1216 1217 1218 1219 1220 1221
	  /* 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;
	    }

1222
	  if (child->pack == packing)
1223
	    {
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238
	      /* 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;

1239
		  if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
1240 1241 1242 1243
		    {
		      child_size += extra;

		      if (n_extra_widgets > 0)
1244
			{
1245 1246
			  child_size++;
			  n_extra_widgets--;
1247
			}
1248 1249
		    }
		}
1250

1251 1252 1253 1254 1255 1256 1257 1258
	      if (child->fill)
		{
		  child_size = MAX (1, child_size - child->padding * 2);
		}
	      else
		{
		  child_size = sizes[i].minimum_size;
		}
1259 1260


1261 1262
	      /* Assign the child's position. */
	      if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1263 1264
		gtk_widget_get_preferred_height_for_width (child->widget,
                                                           child_size, &child_minimum, &child_natural);
1265
	      else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1266 1267
		gtk_widget_get_preferred_width_for_height (child->widget,
                                                           child_size, &child_minimum, &child_natural);
1268 1269 1270 1271


	      computed_minimum = MAX (computed_minimum, child_minimum);
	      computed_natural = MAX (computed_natural, child_natural);
1272
	    }
1273
	  i += 1;
1274
	}
1275 1276
    }

1277
  if (minimum_size)
1278
    *minimum_size = computed_minimum;
1279
  if (natural_size)
1280
    *natural_size = computed_natural;
1281
}
1282

1283
static void
1284 1285 1286 1287 1288
gtk_box_compute_size_for_orientation (GtkBox *box,
				      gint    avail_size,
				      gint   *minimum_size,
				      gint   *natural_size)
{
1289
  GtkBoxPrivate    *private = box->priv;
1290 1291 1292 1293 1294
  GList         *children;
  gint           nvis_children = 0;
  gint           required_size = 0, required_natural = 0, child_size, child_natural;
  gint           largest_child = 0, largest_natural = 0;

1295
  for (children = private->children; children != NULL;
1296
       children = children->next)
1297 1298 1299 1300 1301 1302 1303
    {
      GtkBoxChild *child = children->data;

      if (gtk_widget_get_visible (child->widget))
        {

          if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1304 1305
	    gtk_widget_get_preferred_width_for_height (child->widget,
                                                       avail_size, &child_size, &child_natural);
1306
	  else
1307 1308
	    gtk_widget_get_preferred_height_for_width (child->widget,
						       avail_size, &child_size, &child_natural);
1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321


	  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;
1322 1323

          nvis_children += 1;
1324 1325 1326 1327 1328
        }
    }

  if (nvis_children > 0)
    {
1329
      if (private->homogeneous)
1330 1331 1332 1333 1334
	{
	  required_size    = largest_child   * nvis_children;
	  required_natural = largest_natural * nvis_children;
	}

1335 1336
      required_size     += (nvis_children - 1) * private->spacing;
      required_natural  += (nvis_children - 1) * private->spacing;
1337 1338 1339 1340 1341 1342 1343 1344 1345
    }

  if (minimum_size)
    *minimum_size = required_size;

  if (natural_size)
    *natural_size = required_natural;
}

1346
static void
1347 1348 1349 1350
gtk_box_get_preferred_width_for_height (GtkWidget *widget,
                                        gint       height,
                                        gint      *minimum_width,
                                        gint      *natural_width)
1351
{
1352
  GtkBox        *box     = GTK_BOX (widget);
1353
  GtkBoxPrivate *private = box->priv;
1354 1355

  if (private->orientation == GTK_ORIENTATION_VERTICAL)
1356
    gtk_box_compute_size_for_opposing_orientation (box, height, minimum_width, natural_width);