gtkbbox.c 26.6 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
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20

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

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
/**
 * SECTION:gtkbbox
 * @Short_description: Base class for GtkHButtonBox and GtkVButtonBox
 * @Title: GtkButtonBox
 * @See_also: #GtkVButtonBox, #GtkHButtonBox
 *
 * The primary purpose of this class is to keep track of the various properties
 * of #GtkHButtonBox and #GtkVButtonBox widgets.
 *
 * gtk_button_box_get_child_size() retrieves the minimum width and height
 * for widgets in a given button box.
 *
 * The internal padding of buttons can be retrieved and changed per button box
 * using gtk_button_box_get_child_ipadding() and
 * gtk_button_box_set_child_ipadding() respectively.
 *
 * gtk_button_box_get_spacing() and gtk_button_box_set_spacing() retrieve and
 * change default number of pixels between buttons, respectively.
 *
 * gtk_button_box_get_layout() and gtk_button_box_set_layout() retrieve and
 * alter the method used to spread the buttons in a button box across the
 * container, respectively.
 *
 * The main purpose of GtkButtonBox is to make sure the children have all the
 * same size. Therefore it ignores the homogeneous property which it inherited
 * from GtkBox, and always behaves as if homogeneous was %TRUE.
 */

55
#include "config.h"
Elliot Lee's avatar
Elliot Lee committed
56
#include "gtkbbox.h"
57 58
#include "gtkhbbox.h"
#include "gtkvbbox.h"
59
#include "gtkorientable.h"
60
#include "gtkprivate.h"
Havoc Pennington's avatar
Havoc Pennington committed
61
#include "gtkintl.h"
62

Elliot Lee's avatar
Elliot Lee committed
63

64 65 66 67 68 69 70 71 72 73
struct _GtkButtonBoxPriv
{
  GtkButtonBoxStyle layout_style;

  gint child_ipad_x;
  gint child_ipad_y;
  gint child_min_width;
  gint child_min_height;
};

74 75
enum {
  PROP_0,
76
  PROP_LAYOUT_STYLE
77
};
Elliot Lee's avatar
Elliot Lee committed
78

79 80 81 82 83 84 85 86 87 88 89 90 91
enum {
  CHILD_PROP_0,
  CHILD_PROP_SECONDARY
};

static void gtk_button_box_set_property       (GObject           *object,
					       guint              prop_id,
					       const GValue      *value,
					       GParamSpec        *pspec);
static void gtk_button_box_get_property       (GObject           *object,
					       guint              prop_id,
					       GValue            *value,
					       GParamSpec        *pspec);
92 93 94 95
static void gtk_button_box_size_request       (GtkWidget         *widget,
                                               GtkRequisition    *requisition);
static void gtk_button_box_size_allocate      (GtkWidget         *widget,
                                               GtkAllocation     *allocation);
96 97 98 99 100 101 102 103 104 105
static void gtk_button_box_set_child_property (GtkContainer      *container,
					       GtkWidget         *child,
					       guint              property_id,
					       const GValue      *value,
					       GParamSpec        *pspec);
static void gtk_button_box_get_child_property (GtkContainer      *container,
					       GtkWidget         *child,
					       guint              property_id,
					       GValue            *value,
					       GParamSpec        *pspec);
Elliot Lee's avatar
Elliot Lee committed
106

Havoc Pennington's avatar
Havoc Pennington committed
107 108
#define DEFAULT_CHILD_MIN_WIDTH 85
#define DEFAULT_CHILD_MIN_HEIGHT 27
109
#define DEFAULT_CHILD_IPAD_X 4
Havoc Pennington's avatar
Havoc Pennington committed
110
#define DEFAULT_CHILD_IPAD_Y 0
Elliot Lee's avatar
Elliot Lee committed
111

112
G_DEFINE_TYPE (GtkButtonBox, gtk_button_box, GTK_TYPE_BOX)
Elliot Lee's avatar
Elliot Lee committed
113 114 115 116 117

static void
gtk_button_box_class_init (GtkButtonBoxClass *class)
{
  GtkWidgetClass *widget_class;
118
  GObjectClass *gobject_class;
119
  GtkContainerClass *container_class;
Elliot Lee's avatar
Elliot Lee committed
120

121
  gobject_class = G_OBJECT_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
122
  widget_class = (GtkWidgetClass*) class;
123
  container_class = (GtkContainerClass*) class;
124

125 126
  gobject_class->set_property = gtk_button_box_set_property;
  gobject_class->get_property = gtk_button_box_get_property;
127

128 129 130
  widget_class->size_request = gtk_button_box_size_request;
  widget_class->size_allocate = gtk_button_box_size_allocate;

131 132
  container_class->set_child_property = gtk_button_box_set_child_property;
  container_class->get_child_property = gtk_button_box_get_child_property;
133

134 135 136
  /* FIXME we need to override the "spacing" property on GtkBox once
   * libgobject allows that.
   */
Havoc Pennington's avatar
Havoc Pennington committed
137
  gtk_widget_class_install_style_property (widget_class,
138
					   g_param_spec_int ("child-min-width",
139 140
							     P_("Minimum child width"),
							     P_("Minimum width of buttons inside the box"),
Havoc Pennington's avatar
Havoc Pennington committed
141 142 143
							     0,
							     G_MAXINT,
                                                             DEFAULT_CHILD_MIN_WIDTH,
144
							     GTK_PARAM_READABLE));
Havoc Pennington's avatar
Havoc Pennington committed
145 146

  gtk_widget_class_install_style_property (widget_class,
147
					   g_param_spec_int ("child-min-height",
148 149
							     P_("Minimum child height"),
							     P_("Minimum height of buttons inside the box"),
Havoc Pennington's avatar
Havoc Pennington committed
150 151 152
							     0,
							     G_MAXINT,
                                                             DEFAULT_CHILD_MIN_HEIGHT,
153
							     GTK_PARAM_READABLE));
Havoc Pennington's avatar
Havoc Pennington committed
154 155

  gtk_widget_class_install_style_property (widget_class,
156
					   g_param_spec_int ("child-internal-pad-x",
157 158
							     P_("Child internal width padding"),
							     P_("Amount to increase child's size on either side"),
Havoc Pennington's avatar
Havoc Pennington committed
159 160 161
							     0,
							     G_MAXINT,
                                                             DEFAULT_CHILD_IPAD_X,
162
							     GTK_PARAM_READABLE));
Havoc Pennington's avatar
Havoc Pennington committed
163 164

  gtk_widget_class_install_style_property (widget_class,
165
					   g_param_spec_int ("child-internal-pad-y",
166 167
							     P_("Child internal height padding"),
							     P_("Amount to increase child's size on the top and bottom"),
Havoc Pennington's avatar
Havoc Pennington committed
168 169 170
							     0,
							     G_MAXINT,
                                                             DEFAULT_CHILD_IPAD_Y,
171
							     GTK_PARAM_READABLE));
172 173
  g_object_class_install_property (gobject_class,
                                   PROP_LAYOUT_STYLE,
174
                                   g_param_spec_enum ("layout-style",
175 176
                                                      P_("Layout style"),
                                                      P_("How to layout the buttons in the box. Possible values are default, spread, edge, start and end"),
177 178
						      GTK_TYPE_BUTTON_BOX_STYLE,
						      GTK_BUTTONBOX_DEFAULT_STYLE,
179
                                                      GTK_PARAM_READWRITE));
180 181 182 183

  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_SECONDARY,
					      g_param_spec_boolean ("secondary", 
184 185
								    P_("Secondary"),
								    P_("If TRUE, the child appears in a secondary group of children, suitable for, e.g., help buttons"),
186
								    FALSE,
187
								    GTK_PARAM_READWRITE));
188 189

  g_type_class_add_private (class, sizeof (GtkButtonBoxPriv));
Elliot Lee's avatar
Elliot Lee committed
190 191 192 193 194
}

static void
gtk_button_box_init (GtkButtonBox *button_box)
{
195 196 197 198 199 200 201 202 203 204 205 206 207
  GtkButtonBoxPriv *priv;

  button_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (button_box,
                                                  GTK_TYPE_BUTTON_BOX,
                                                  GtkButtonBoxPriv);
  priv = button_box->priv;

  gtk_box_set_spacing (GTK_BOX (button_box), 0);
  priv->child_min_width = GTK_BUTTONBOX_DEFAULT;
  priv->child_min_height = GTK_BUTTONBOX_DEFAULT;
  priv->child_ipad_x = GTK_BUTTONBOX_DEFAULT;
  priv->child_ipad_y = GTK_BUTTONBOX_DEFAULT;
  priv->layout_style = GTK_BUTTONBOX_DEFAULT_STYLE;
Elliot Lee's avatar
Elliot Lee committed
208 209
}

210 211 212 213 214 215
static void
gtk_button_box_set_property (GObject         *object,
			     guint            prop_id,
			     const GValue    *value,
			     GParamSpec      *pspec)
{
216
  switch (prop_id)
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
    {
    case PROP_LAYOUT_STYLE:
      gtk_button_box_set_layout (GTK_BUTTON_BOX (object),
				 g_value_get_enum (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_button_box_get_property (GObject         *object,
			     guint            prop_id,
			     GValue          *value,
			     GParamSpec      *pspec)
{
234 235
  GtkButtonBoxPriv *priv = GTK_BUTTON_BOX (object)->priv;

236 237 238
  switch (prop_id)
    {
    case PROP_LAYOUT_STYLE:
239
      g_value_set_enum (value, priv->layout_style);
240 241 242 243 244 245 246
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
static void
gtk_button_box_set_child_property (GtkContainer    *container,
				   GtkWidget       *child,
				   guint            property_id,
				   const GValue    *value,
				   GParamSpec      *pspec)
{
  switch (property_id)
    {
    case CHILD_PROP_SECONDARY:
      gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), child,
					  g_value_get_boolean (value));
      break;
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      break;
    }
}

static void
gtk_button_box_get_child_property (GtkContainer *container,
				   GtkWidget    *child,
				   guint         property_id,
				   GValue       *value,
				   GParamSpec   *pspec)
{
  switch (property_id)
    {
    case CHILD_PROP_SECONDARY:
Matthias Clasen's avatar
Matthias Clasen committed
276 277 278
      g_value_set_boolean (value, 
			   gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (container), 
							       child));
279 280 281 282 283 284
      break;
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      break;
    }
}
285

286 287 288 289 290 291 292
/**
 * gtk_button_box_set_layout:
 * @widget: a #GtkButtonBox
 * @layout_style: the new layout style
 *
 * Changes the way buttons are arranged in their container.
 */
293 294 295
void
gtk_button_box_set_layout (GtkButtonBox      *widget, 
                           GtkButtonBoxStyle  layout_style)
Elliot Lee's avatar
Elliot Lee committed
296
{
297 298
  GtkButtonBoxPriv *priv;

299
  g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
300
  g_return_if_fail (layout_style >= GTK_BUTTONBOX_DEFAULT_STYLE &&
301
		    layout_style <= GTK_BUTTONBOX_CENTER);
302

303 304 305
  priv = widget->priv;

  if (priv->layout_style != layout_style)
306
    {
307
      priv->layout_style = layout_style;
308
      g_object_notify (G_OBJECT (widget), "layout-style");
309 310
      gtk_widget_queue_resize (GTK_WIDGET (widget));
    }
Elliot Lee's avatar
Elliot Lee committed
311 312
}

313 314 315 316 317 318 319 320
/**
 * gtk_button_box_get_layout:
 * @widget: a #GtkButtonBox
 *
 * Retrieves the method being used to arrange the buttons in a button box.
 *
 * Returns: the method used to layout buttons in @widget.
 */
321 322
GtkButtonBoxStyle 
gtk_button_box_get_layout (GtkButtonBox *widget)
Elliot Lee's avatar
Elliot Lee committed
323
{
324 325
  g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), GTK_BUTTONBOX_SPREAD);
  
326
  return widget->priv->layout_style;
Elliot Lee's avatar
Elliot Lee committed
327 328
}

Matthias Clasen's avatar
Matthias Clasen committed
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
/**
 * gtk_button_box_get_child_secondary:
 * @widget: a #GtkButtonBox
 * @child: a child of @widget 
 * 
 * Returns whether @child should appear in a secondary group of children.
 *
 * Return value: whether @child should appear in a secondary group of children.
 *
 * Since: 2.4
 **/
gboolean 
gtk_button_box_get_child_secondary (GtkButtonBox *widget,
				    GtkWidget    *child)
{
  GtkBoxChild *child_info;
345
  GList *list;
Matthias Clasen's avatar
Matthias Clasen committed
346

347 348
  g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE);
  g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
Matthias Clasen's avatar
Matthias Clasen committed
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365

  child_info = NULL;
  list = GTK_BOX (widget)->children;
  while (list)
    {
      child_info = list->data;
      if (child_info->widget == child)
	break;

      list = list->next;
    }

  g_return_val_if_fail (list != NULL, FALSE);

  return child_info->is_secondary;
}

366 367 368 369 370 371 372 373 374 375 376 377
/**
 * gtk_button_box_set_child_secondary
 * @widget: a #GtkButtonBox
 * @child: a child of @widget
 * @is_secondary: if %TRUE, the @child appears in a secondary group of the
 *                button box.
 *
 * Sets whether @child should appear in a secondary group of children.
 * A typical use of a secondary child is the help button in a dialog.
 *
 * This group appears after the other children if the style
 * is %GTK_BUTTONBOX_START, %GTK_BUTTONBOX_SPREAD or
378
 * %GTK_BUTTONBOX_EDGE, and before the other children if the style
379
 * is %GTK_BUTTONBOX_END. For horizontal button boxes, the definition
Matthias Clasen's avatar
Matthias Clasen committed
380 381 382
 * of before/after depends on direction of the widget (see
 * gtk_widget_set_direction()). If the style is %GTK_BUTTONBOX_START
 * or %GTK_BUTTONBOX_END, then the secondary children are aligned at
383 384 385 386 387 388 389 390 391 392 393 394 395
 * the other end of the button box from the main children. For the
 * other styles, they appear immediately next to the main children.
 **/
void 
gtk_button_box_set_child_secondary (GtkButtonBox *widget, 
				    GtkWidget    *child,
				    gboolean      is_secondary)
{
  GList *list;
  
  g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
  g_return_if_fail (GTK_IS_WIDGET (child));
  g_return_if_fail (child->parent == GTK_WIDGET (widget));
Elliot Lee's avatar
Elliot Lee committed
396

397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
  list = GTK_BOX (widget)->children;
  while (list)
    {
      GtkBoxChild *child_info = list->data;
      if (child_info->widget == child)
	{
	  child_info->is_secondary = is_secondary;
	  break;
	}

      list = list->next;
    }

  gtk_widget_child_notify (child, "secondary");

412 413
  if (gtk_widget_get_visible (GTK_WIDGET (widget))
      && gtk_widget_get_visible (child))
414 415
    gtk_widget_queue_resize (child);
}
Elliot Lee's avatar
Elliot Lee committed
416 417 418 419 420

/* Ask children how much space they require and round up 
   to match minimum size and internal padding.
   Returns the size each single child should have. */
void
421
_gtk_button_box_child_requisition (GtkWidget *widget,
422 423 424 425
                                   int       *nvis_children,
				   int       *nvis_secondaries,
                                   int       *width,
                                   int       *height)
Elliot Lee's avatar
Elliot Lee committed
426
{
427
  GtkButtonBoxPriv *priv;
Elliot Lee's avatar
Elliot Lee committed
428 429 430 431
  GtkButtonBox *bbox;
  GtkBoxChild *child;
  GList *children;
  gint nchildren;
432
  gint nsecondaries;
Elliot Lee's avatar
Elliot Lee committed
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
  gint needed_width;
  gint needed_height;
  GtkRequisition child_requisition;
  gint ipad_w;
  gint ipad_h;
  gint width_default;
  gint height_default;
  gint ipad_x_default;
  gint ipad_y_default;
  
  gint child_min_width;
  gint child_min_height;
  gint ipad_x;
  gint ipad_y;
  
  g_return_if_fail (GTK_IS_BUTTON_BOX (widget));

  bbox = GTK_BUTTON_BOX (widget);
451
  priv = bbox->priv;
Elliot Lee's avatar
Elliot Lee committed
452

Havoc Pennington's avatar
Havoc Pennington committed
453
  gtk_widget_style_get (widget,
454 455 456 457 458
                        "child-min-width", &width_default,
                        "child-min-height", &height_default,
                        "child-internal-pad-x", &ipad_x_default,
                        "child-internal-pad-y", &ipad_y_default, 
			NULL);
459 460 461 462 463 464 465 466 467

  child_min_width = priv->child_min_width != GTK_BUTTONBOX_DEFAULT
                    ? priv->child_min_width : width_default;
  child_min_height = priv->child_min_height != GTK_BUTTONBOX_DEFAULT
                     ? priv->child_min_height : height_default;
  ipad_x = priv->child_ipad_x != GTK_BUTTONBOX_DEFAULT
           ? priv->child_ipad_x : ipad_x_default;
  ipad_y = priv->child_ipad_y != GTK_BUTTONBOX_DEFAULT
           ? priv->child_ipad_y : ipad_y_default;
Elliot Lee's avatar
Elliot Lee committed
468 469

  nchildren = 0;
470
  nsecondaries = 0;
Elliot Lee's avatar
Elliot Lee committed
471 472 473 474 475 476 477 478 479 480 481
  children = GTK_BOX(bbox)->children;
  needed_width = child_min_width;
  needed_height = child_min_height;  
  ipad_w = ipad_x * 2;
  ipad_h = ipad_y * 2;
  
  while (children)
    {
      child = children->data;
      children = children->next;

482
      if (gtk_widget_get_visible (child->widget))
Elliot Lee's avatar
Elliot Lee committed
483 484 485
	{
	  nchildren += 1;
	  gtk_widget_size_request (child->widget, &child_requisition);
486

Elliot Lee's avatar
Elliot Lee committed
487
	  if (child_requisition.width + ipad_w > needed_width)
488
	    needed_width = child_requisition.width + ipad_w;
Elliot Lee's avatar
Elliot Lee committed
489
	  if (child_requisition.height + ipad_h > needed_height)
490 491 492
	    needed_height = child_requisition.height + ipad_h;
	  if (child->is_secondary)
	    nsecondaries++;
Elliot Lee's avatar
Elliot Lee committed
493 494
	}
    }
495 496 497 498 499 500 501 502 503

  if (nvis_children)
    *nvis_children = nchildren;
  if (nvis_secondaries)
    *nvis_secondaries = nsecondaries;
  if (width)
    *width = needed_width;
  if (height)
    *height = needed_height;
Elliot Lee's avatar
Elliot Lee committed
504
}
505

506 507 508 509 510 511 512 513 514 515 516 517
/* this is a kludge function to support the deprecated
 * gtk_[vh]button_box_set_layout_default() just in case anyone is still
 * using it (why?)
 */
static GtkButtonBoxStyle
gtk_button_box_kludge_get_layout_default (GtkButtonBox *widget)
{
  GtkOrientation orientation;

  orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));

  if (orientation == GTK_ORIENTATION_HORIZONTAL)
518
    return _gtk_hbutton_box_get_layout_default ();
519
  else
520
    return _gtk_vbutton_box_get_layout_default ();
521 522 523 524 525 526
}

static void
gtk_button_box_size_request (GtkWidget      *widget,
                             GtkRequisition *requisition)
{
527
  GtkButtonBoxPriv *priv;
528 529 530 531 532 533 534 535 536 537 538
  GtkBox *box;
  GtkButtonBox *bbox;
  gint nvis_children;
  gint child_width;
  gint child_height;
  gint spacing;
  GtkButtonBoxStyle layout;
  GtkOrientation orientation;

  box = GTK_BOX (widget);
  bbox = GTK_BUTTON_BOX (widget);
539
  priv = bbox->priv;
540 541 542

  orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
  spacing = box->spacing;
543 544
  layout = priv->layout_style != GTK_BUTTONBOX_DEFAULT_STYLE
           ? priv->layout_style : gtk_button_box_kludge_get_layout_default (GTK_BUTTON_BOX (widget));
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586

  _gtk_button_box_child_requisition (widget,
                                     &nvis_children,
				     NULL,
                                     &child_width,
                                     &child_height);

  if (nvis_children == 0)
    {
      requisition->width = 0;
      requisition->height = 0;
    }
  else
    {
      switch (layout)
        {
          case GTK_BUTTONBOX_SPREAD:
            if (orientation == GTK_ORIENTATION_HORIZONTAL)
              requisition->width =
                      nvis_children*child_width + ((nvis_children+1)*spacing);
            else
              requisition->height =
                      nvis_children*child_height + ((nvis_children+1)*spacing);

            break;
          case GTK_BUTTONBOX_EDGE:
          case GTK_BUTTONBOX_START:
          case GTK_BUTTONBOX_END:
          case GTK_BUTTONBOX_CENTER:
            if (orientation == GTK_ORIENTATION_HORIZONTAL)
              requisition->width =
                      nvis_children*child_width + ((nvis_children-1)*spacing);
            else
              requisition->height =
                      nvis_children*child_height + ((nvis_children-1)*spacing);

            break;
          default:
            g_assert_not_reached ();
            break;
        }

587 588 589 590
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
        requisition->height = child_height;
      else
        requisition->width = child_width;
591 592 593 594 595 596 597 598 599 600
    }

  requisition->width += GTK_CONTAINER (box)->border_width * 2;
  requisition->height += GTK_CONTAINER (box)->border_width * 2;
}

static void
gtk_button_box_size_allocate (GtkWidget     *widget,
                              GtkAllocation *allocation)
{
601
  GtkButtonBoxPriv *priv;
602 603 604 605 606 607 608 609 610 611 612 613 614
  GtkBox *base_box;
  GtkButtonBox *box;
  GtkBoxChild *child;
  GList *children;
  GtkAllocation child_allocation;
  gint nvis_children;
  gint n_secondaries;
  gint child_width;
  gint child_height;
  gint x = 0;
  gint y = 0;
  gint secondary_x = 0;
  gint secondary_y = 0;
615 616
  gint width = 0;
  gint height = 0;
617 618 619 620 621 622 623 624
  gint childspace;
  gint childspacing = 0;
  GtkButtonBoxStyle layout;
  gint spacing;
  GtkOrientation orientation;

  base_box = GTK_BOX (widget);
  box = GTK_BUTTON_BOX (widget);
625 626 627
  priv = box->priv;

  orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
628
  spacing = base_box->spacing;
629 630
  layout = priv->layout_style != GTK_BUTTONBOX_DEFAULT_STYLE
           ? priv->layout_style : gtk_button_box_kludge_get_layout_default (GTK_BUTTON_BOX (widget));
631 632 633 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 662 663 664 665 666 667 668 669 670 671 672
  _gtk_button_box_child_requisition (widget,
                                     &nvis_children,
                                     &n_secondaries,
                                     &child_width,
                                     &child_height);
  widget->allocation = *allocation;

  if (orientation == GTK_ORIENTATION_HORIZONTAL)
    width = allocation->width - GTK_CONTAINER (box)->border_width*2;
  else
    height = allocation->height - GTK_CONTAINER (box)->border_width*2;

  switch (layout)
    {
      case GTK_BUTTONBOX_SPREAD:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            childspacing = (width - (nvis_children * child_width))
                    / (nvis_children + 1);
            x = allocation->x + GTK_CONTAINER (box)->border_width
                    + childspacing;
            secondary_x = x + ((nvis_children - n_secondaries)
                            * (child_width + childspacing));
          }
        else
          {
            childspacing = (height - (nvis_children * child_height))
                    / (nvis_children + 1);
            y = allocation->y + GTK_CONTAINER (box)->border_width
                    + childspacing;
            secondary_y = y + ((nvis_children - n_secondaries)
                            * (child_height + childspacing));
          }

        break;

      case GTK_BUTTONBOX_EDGE:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            if (nvis_children >= 2)
673 674
              {
                childspacing = (width - (nvis_children * child_width))
675
                      / (nvis_children - 1);
676 677 678 679 680 681 682 683 684
                x = allocation->x + GTK_CONTAINER (box)->border_width;
                secondary_x = x + ((nvis_children - n_secondaries)
                                   * (child_width + childspacing));
              }
            else
              {
                /* one or zero children, just center */
                childspacing = width;
                x = secondary_x = allocation->x
685
                      + (allocation->width - child_width) / 2;
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 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
          }
        else
          {
            if (nvis_children >= 2)
              {
                childspacing = (height - (nvis_children*child_height))
                        / (nvis_children-1);
                y = allocation->y + GTK_CONTAINER (box)->border_width;
                secondary_y = y + ((nvis_children - n_secondaries)
                                * (child_height + childspacing));
              }
            else
              {
                /* one or zero children, just center */
                childspacing = height;
                y = secondary_y = allocation->y
                        + (allocation->height - child_height) / 2;
              }
          }

        break;

      case GTK_BUTTONBOX_START:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            childspacing = spacing;
            x = allocation->x + GTK_CONTAINER (box)->border_width;
            secondary_x = allocation->x + allocation->width
              - child_width * n_secondaries
              - spacing * (n_secondaries - 1)
              - GTK_CONTAINER (box)->border_width;
          }
        else
          {
            childspacing = spacing;
            y = allocation->y + GTK_CONTAINER (box)->border_width;
            secondary_y = allocation->y + allocation->height
              - child_height * n_secondaries
              - spacing * (n_secondaries - 1)
              - GTK_CONTAINER (box)->border_width;
          }

        break;

      case GTK_BUTTONBOX_END:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            childspacing = spacing;
            x = allocation->x + allocation->width
              - child_width * (nvis_children - n_secondaries)
              - spacing * (nvis_children - n_secondaries - 1)
              - GTK_CONTAINER (box)->border_width;
            secondary_x = allocation->x + GTK_CONTAINER (box)->border_width;
          }
        else
          {
            childspacing = spacing;
            y = allocation->y + allocation->height
              - child_height * (nvis_children - n_secondaries)
              - spacing * (nvis_children - n_secondaries - 1)
              - GTK_CONTAINER (box)->border_width;
            secondary_y = allocation->y + GTK_CONTAINER (box)->border_width;
          }

        break;

      case GTK_BUTTONBOX_CENTER:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            childspacing = spacing;
            x = allocation->x +
              (allocation->width
               - (child_width * (nvis_children - n_secondaries)
763 764
               + spacing * (nvis_children - n_secondaries - 1))) / 2
              + (n_secondaries * child_width + n_secondaries * spacing) / 2;
765 766 767 768 769 770 771 772
            secondary_x = allocation->x + GTK_CONTAINER (box)->border_width;
          }
        else
          {
            childspacing = spacing;
            y = allocation->y +
              (allocation->height
               - (child_height * (nvis_children - n_secondaries)
773 774
                  + spacing * (nvis_children - n_secondaries - 1))) / 2
              + (n_secondaries * child_height + n_secondaries * spacing) / 2;
775 776 777 778 779 780
            secondary_y = allocation->y + GTK_CONTAINER (box)->border_width;
          }

        break;

      default:
781
        g_assert_not_reached ();
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
        break;
    }

    if (orientation == GTK_ORIENTATION_HORIZONTAL)
      {
        y = allocation->y + (allocation->height - child_height) / 2;
        childspace = child_width + childspacing;
      }
    else
      {
        x = allocation->x + (allocation->width - child_width) / 2;
        childspace = child_height + childspacing;
      }

  children = GTK_BOX (box)->children;

  while (children)
    {
      child = children->data;
      children = children->next;

803
      if (gtk_widget_get_visible (child->widget))
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847
        {
          child_allocation.width = child_width;
          child_allocation.height = child_height;

          if (orientation == GTK_ORIENTATION_HORIZONTAL)
            {
              child_allocation.y = y;

              if (child->is_secondary)
                {
                  child_allocation.x = secondary_x;
                  secondary_x += childspace;
                }
              else
                {
                  child_allocation.x = x;
                  x += childspace;
                }

              if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
                  child_allocation.x = (allocation->x + allocation->width)
                          - (child_allocation.x + child_width - allocation->x);
            }
          else
            {
              child_allocation.x = x;

              if (child->is_secondary)
                {
                  child_allocation.y = secondary_y;
                  secondary_y += childspace;
                }
              else
                {
                  child_allocation.y = y;
                  y += childspace;
                }
            }

          gtk_widget_size_allocate (child->widget, &child_allocation);
        }
    }
}

848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
/**
 * gtk_button_box_new:
 * @orientation: the box' orientation.
 *
 * Creates a new #GtkButtonBox.
 *
 * Return value: a new #GtkButtonBox.
 *
 * Since: 3.0
 */
GtkWidget *
gtk_button_box_new (GtkOrientation orientation)
{
  return g_object_new (GTK_TYPE_BUTTON_BOX,
                       "orientation", orientation,
                       NULL);
}