gtkbbox.c 29.3 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
#include "gtkorientable.h"
58
#include "gtkprivate.h"
Havoc Pennington's avatar
Havoc Pennington committed
59
#include "gtkintl.h"
60

Elliot Lee's avatar
Elliot Lee committed
61

62
struct _GtkButtonBoxPrivate
63 64 65 66
{
  GtkButtonBoxStyle layout_style;
};

67 68
enum {
  PROP_0,
69
  PROP_LAYOUT_STYLE
70
};
Elliot Lee's avatar
Elliot Lee committed
71

72 73 74 75 76
enum {
  CHILD_PROP_0,
  CHILD_PROP_SECONDARY
};

77 78
#define GTK_BOX_SECONDARY_CHILD "gtk-box-secondary-child"

79
static void gtk_button_box_set_property       (GObject           *object,
80 81 82
                                               guint              prop_id,
                                               const GValue      *value,
                                               GParamSpec        *pspec);
83
static void gtk_button_box_get_property       (GObject           *object,
84 85 86
                                               guint              prop_id,
                                               GValue            *value,
                                               GParamSpec        *pspec);
87 88 89 90
static void gtk_button_box_size_request       (GtkWidget         *widget,
                                               GtkRequisition    *requisition);
static void gtk_button_box_size_allocate      (GtkWidget         *widget,
                                               GtkAllocation     *allocation);
91 92
static void gtk_button_box_remove             (GtkContainer      *container,
                                               GtkWidget         *widget);
93
static void gtk_button_box_set_child_property (GtkContainer      *container,
94 95 96 97
                                               GtkWidget         *child,
                                               guint              property_id,
                                               const GValue      *value,
                                               GParamSpec        *pspec);
98
static void gtk_button_box_get_child_property (GtkContainer      *container,
99 100 101 102
                                               GtkWidget         *child,
                                               guint              property_id,
                                               GValue            *value,
                                               GParamSpec        *pspec);
Elliot Lee's avatar
Elliot Lee committed
103

Havoc Pennington's avatar
Havoc Pennington committed
104 105
#define DEFAULT_CHILD_MIN_WIDTH 85
#define DEFAULT_CHILD_MIN_HEIGHT 27
106
#define DEFAULT_CHILD_IPAD_X 4
Havoc Pennington's avatar
Havoc Pennington committed
107
#define DEFAULT_CHILD_IPAD_Y 0
108
#define DEFAULT_LAYOUT_STYLE GTK_BUTTONBOX_EDGE
Elliot Lee's avatar
Elliot Lee committed
109

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

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

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

123 124
  gobject_class->set_property = gtk_button_box_set_property;
  gobject_class->get_property = gtk_button_box_get_property;
125

126 127 128
  widget_class->size_request = gtk_button_box_size_request;
  widget_class->size_allocate = gtk_button_box_size_allocate;

129
  container_class->remove = gtk_button_box_remove;
130 131
  container_class->set_child_property = gtk_button_box_set_child_property;
  container_class->get_child_property = gtk_button_box_get_child_property;
132

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

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

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

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

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

188
  g_type_class_add_private (class, sizeof (GtkButtonBoxPrivate));
Elliot Lee's avatar
Elliot Lee committed
189 190 191 192 193
}

static void
gtk_button_box_init (GtkButtonBox *button_box)
{
194
  GtkButtonBoxPrivate *priv;
195 196 197

  button_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (button_box,
                                                  GTK_TYPE_BUTTON_BOX,
198
                                                  GtkButtonBoxPrivate);
199 200 201
  priv = button_box->priv;

  gtk_box_set_spacing (GTK_BOX (button_box), 0);
202
  priv->layout_style = DEFAULT_LAYOUT_STYLE;
Elliot Lee's avatar
Elliot Lee committed
203 204
}

205
static void
206 207 208 209
gtk_button_box_set_property (GObject      *object,
                             guint         prop_id,
                             const GValue *value,
                             GParamSpec   *pspec)
210
{
211
  switch (prop_id)
212 213 214
    {
    case PROP_LAYOUT_STYLE:
      gtk_button_box_set_layout (GTK_BUTTON_BOX (object),
215
                                 g_value_get_enum (value));
216 217 218 219 220 221 222 223
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
224 225 226 227
gtk_button_box_get_property (GObject    *object,
                             guint       prop_id,
                             GValue     *value,
                             GParamSpec *pspec)
228
{
229
  GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (object)->priv;
230

231 232 233
  switch (prop_id)
    {
    case PROP_LAYOUT_STYLE:
234
      g_value_set_enum (value, priv->layout_style);
235 236 237 238 239 240 241
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

242
static void
243 244 245 246 247
gtk_button_box_set_child_property (GtkContainer *container,
                                   GtkWidget    *child,
                                   guint         property_id,
                                   const GValue *value,
                                   GParamSpec   *pspec)
248 249 250 251 252
{
  switch (property_id)
    {
    case CHILD_PROP_SECONDARY:
      gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), child,
253
                                          g_value_get_boolean (value));
254 255 256 257 258 259 260 261 262
      break;
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      break;
    }
}

static void
gtk_button_box_get_child_property (GtkContainer *container,
263 264 265 266
                                   GtkWidget    *child,
                                   guint         property_id,
                                   GValue       *value,
                                   GParamSpec   *pspec)
267 268 269 270
{
  switch (property_id)
    {
    case CHILD_PROP_SECONDARY:
271 272 273
      g_value_set_boolean (value,
                           gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (container),
                                                               child));
274 275 276 277 278 279
      break;
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      break;
    }
}
280

281 282 283 284 285
static void
gtk_button_box_remove (GtkContainer *container,
                       GtkWidget    *widget)
{
  /* clear is_secondary flag in case the widget
286 287
   * is added to another container
   */
288 289 290 291 292 293 294
  gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container),
                                      widget,
                                      FALSE);

  GTK_CONTAINER_CLASS (gtk_button_box_parent_class)->remove (container, widget);
}

295 296 297 298 299 300 301
/**
 * gtk_button_box_set_layout:
 * @widget: a #GtkButtonBox
 * @layout_style: the new layout style
 *
 * Changes the way buttons are arranged in their container.
 */
302
void
303
gtk_button_box_set_layout (GtkButtonBox      *widget,
304
                           GtkButtonBoxStyle  layout_style)
Elliot Lee's avatar
Elliot Lee committed
305
{
306
  GtkButtonBoxPrivate *priv;
307

308
  g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
309

310 311 312
  priv = widget->priv;

  if (priv->layout_style != layout_style)
313
    {
314
      priv->layout_style = layout_style;
315
      g_object_notify (G_OBJECT (widget), "layout-style");
316 317
      gtk_widget_queue_resize (GTK_WIDGET (widget));
    }
Elliot Lee's avatar
Elliot Lee committed
318 319
}

320 321 322 323 324 325
/**
 * gtk_button_box_get_layout:
 * @widget: a #GtkButtonBox
 *
 * Retrieves the method being used to arrange the buttons in a button box.
 *
326
 * Returns: the method used to lay out buttons in @widget.
327
 */
328
GtkButtonBoxStyle
329
gtk_button_box_get_layout (GtkButtonBox *widget)
Elliot Lee's avatar
Elliot Lee committed
330
{
331 332
  g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), DEFAULT_LAYOUT_STYLE);

333
  return widget->priv->layout_style;
Elliot Lee's avatar
Elliot Lee committed
334 335
}

Matthias Clasen's avatar
Matthias Clasen committed
336 337 338
/**
 * gtk_button_box_get_child_secondary:
 * @widget: a #GtkButtonBox
339 340
 * @child: a child of @widget
 *
Matthias Clasen's avatar
Matthias Clasen committed
341 342 343 344 345 346
 * 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
 **/
347
gboolean
Matthias Clasen's avatar
Matthias Clasen committed
348
gtk_button_box_get_child_secondary (GtkButtonBox *widget,
349
                                    GtkWidget    *child)
Matthias Clasen's avatar
Matthias Clasen committed
350
{
351 352
  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
353

354
  return (g_object_get_data (G_OBJECT (child), GTK_BOX_SECONDARY_CHILD) != NULL);
Matthias Clasen's avatar
Matthias Clasen committed
355 356
}

357 358 359 360 361 362 363 364 365 366 367 368
/**
 * 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
369
 * %GTK_BUTTONBOX_EDGE, and before the other children if the style
370
 * is %GTK_BUTTONBOX_END. For horizontal button boxes, the definition
Matthias Clasen's avatar
Matthias Clasen committed
371 372 373
 * 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
374 375 376
 * the other end of the button box from the main children. For the
 * other styles, they appear immediately next to the main children.
 **/
377 378 379 380
void
gtk_button_box_set_child_secondary (GtkButtonBox *widget,
                                    GtkWidget    *child,
                                    gboolean      is_secondary)
381 382 383
{
  g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
  g_return_if_fail (GTK_IS_WIDGET (child));
384
  g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (widget));
Elliot Lee's avatar
Elliot Lee committed
385

386 387 388
  g_object_set_data (G_OBJECT (child),
                     GTK_BOX_SECONDARY_CHILD,
                     is_secondary ? GINT_TO_POINTER (1) : NULL);
389 390
  gtk_widget_child_notify (child, "secondary");

391 392
  if (gtk_widget_get_visible (GTK_WIDGET (widget)) &&
      gtk_widget_get_visible (child))
393 394
    gtk_widget_queue_resize (child);
}
Elliot Lee's avatar
Elliot Lee committed
395

396
/* Ask children how much space they require and round up
397 398 399
 * to match minimum size and internal padding.
 * Returns the size each single child should have.
 */
400
static void
401 402 403 404 405
gtk_button_box_child_requisition (GtkWidget  *widget,
                                  gint       *nvis_children,
                                  gint       *nvis_secondaries,
                                  gint      **widths,
                                  gint      **heights)
Elliot Lee's avatar
Elliot Lee committed
406
{
407
  GtkButtonBoxPrivate *priv;
Elliot Lee's avatar
Elliot Lee committed
408
  GtkButtonBox *bbox;
409
  GList *children, *list;
Elliot Lee's avatar
Elliot Lee committed
410
  gint nchildren;
411
  gint nsecondaries;
Elliot Lee's avatar
Elliot Lee committed
412 413
  gint needed_width;
  gint needed_height;
414
  gint avg_w, avg_h;
Elliot Lee's avatar
Elliot Lee committed
415 416 417 418 419 420 421
  GtkRequisition child_requisition;
  gint ipad_w;
  gint ipad_h;
  gint child_min_width;
  gint child_min_height;
  gint ipad_x;
  gint ipad_y;
422 423
  gboolean homogeneous;
  gint i;
424

Elliot Lee's avatar
Elliot Lee committed
425 426 427
  g_return_if_fail (GTK_IS_BUTTON_BOX (widget));

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

430 431
  homogeneous = gtk_box_get_homogeneous (GTK_BOX (widget));

Havoc Pennington's avatar
Havoc Pennington committed
432
  gtk_widget_style_get (widget,
433 434 435 436
                        "child-min-width", &child_min_width,
                        "child-min-height", &child_min_height,
                        "child-internal-pad-x", &ipad_x,
                        "child-internal-pad-y", &ipad_y,
437 438
                        NULL);

Elliot Lee's avatar
Elliot Lee committed
439
  nchildren = 0;
440
  nsecondaries = 0;
441
  list = children = _gtk_box_get_children (GTK_BOX (bbox));
Elliot Lee's avatar
Elliot Lee committed
442
  needed_width = child_min_width;
443
  needed_height = child_min_height;
Elliot Lee's avatar
Elliot Lee committed
444 445
  ipad_w = ipad_x * 2;
  ipad_h = ipad_y * 2;
446 447

  avg_w = avg_h = 0;
Elliot Lee's avatar
Elliot Lee committed
448 449
  while (children)
    {
450 451
      GtkWidget *child;

Elliot Lee's avatar
Elliot Lee committed
452 453 454
      child = children->data;
      children = children->next;

455
      if (gtk_widget_get_visible (child))
456 457 458
        {
          nchildren += 1;
          gtk_widget_size_request (child, &child_requisition);
459 460 461 462
          avg_w += child_requisition.width + ipad_w;
          avg_h += child_requisition.height + ipad_h;
        }
    }
463 464
  avg_w /= MAX (nchildren, 1);
  avg_h /= MAX (nchildren, 1);
465 466 467 468 469 470 471 472 473 474

  *widths = g_new (gint, nchildren);
  *heights = g_new (gint, nchildren);

  i = 0;
  children = list;
  while (children)
    {
      GtkWidget *child;
      gboolean is_secondary;
475

476 477 478 479 480 481
      child = children->data;
      children = children->next;

      if (gtk_widget_get_visible (child))
        {
          is_secondary = gtk_button_box_get_child_secondary (bbox, child);
482 483
          if (is_secondary)
            nsecondaries++;
484 485 486

          gtk_widget_get_child_requisition (child, &child_requisition);

487
          if (homogeneous || (child_requisition.width + ipad_w < avg_w * 1.5))
488 489 490 491 492 493 494 495 496 497
            {
              (*widths)[i] = -1;
              if (child_requisition.width + ipad_w > needed_width)
                needed_width = child_requisition.width + ipad_w;
            }
          else
            {
              (*widths)[i] = child_requisition.width + ipad_w;
            }

498
          if (homogeneous || (child_requisition.height + ipad_h < avg_h * 1.5))
499 500 501 502 503 504 505 506 507 508 509
            {
              (*heights)[i] = -1;
              if (child_requisition.height + ipad_h > needed_height)
                needed_height = child_requisition.height + ipad_h;
            }
          else
            {
              (*heights)[i] = child_requisition.height + ipad_h;
            }

          i++;
510
        }
Elliot Lee's avatar
Elliot Lee committed
511
    }
512

513 514
  g_list_free (list);

515 516 517 518 519 520 521 522
  for (i = 0; i < nchildren; i++)
    {
      if ((*widths)[i] == -1)
        (*widths)[i] = needed_width;
      if ((*heights)[i] == -1)
        (*heights)[i] = needed_height;
    }

523 524
  if (nvis_children)
    *nvis_children = nchildren;
525

526 527
  if (nvis_secondaries)
    *nvis_secondaries = nsecondaries;
Elliot Lee's avatar
Elliot Lee committed
528
}
529

530 531 532 533
static void
gtk_button_box_size_request (GtkWidget      *widget,
                             GtkRequisition *requisition)
{
534
  GtkButtonBoxPrivate *priv;
535 536
  GtkButtonBox *bbox;
  gint nvis_children;
537 538
  gint max_size;
  gint total_size;
539
  gint spacing;
540
  guint border_width;
541
  GtkOrientation orientation;
542 543 544
  gint *widths;
  gint *heights;
  gint i;
545 546

  bbox = GTK_BUTTON_BOX (widget);
547
  priv = bbox->priv;
548 549

  orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
550
  spacing = gtk_box_get_spacing (GTK_BOX (widget));
551

552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
  gtk_button_box_child_requisition (widget,
                                    &nvis_children,
                                    NULL,
                                    &widths, &heights);

  max_size = 0;
  total_size = 0;
  for (i = 0; i < nvis_children; i++)
    {
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
        {
          total_size += widths[i];
          max_size = MAX (max_size, heights[i]);
        }
      else
        {
          total_size += heights[i];
          max_size = MAX (max_size, widths[i]);
        }
    }
  g_free (widths);
  g_free (heights);
574 575 576 577 578 579 580 581

  if (nvis_children == 0)
    {
      requisition->width = 0;
      requisition->height = 0;
    }
  else
    {
582
      switch (priv->layout_style)
583 584 585
        {
          case GTK_BUTTONBOX_SPREAD:
            if (orientation == GTK_ORIENTATION_HORIZONTAL)
586
              requisition->width = total_size + ((nvis_children + 1)*spacing);
587
            else
588
              requisition->height = total_size + ((nvis_children + 1)*spacing);
589 590 591 592 593 594 595

            break;
          case GTK_BUTTONBOX_EDGE:
          case GTK_BUTTONBOX_START:
          case GTK_BUTTONBOX_END:
          case GTK_BUTTONBOX_CENTER:
            if (orientation == GTK_ORIENTATION_HORIZONTAL)
596
              requisition->width = total_size + ((nvis_children - 1)*spacing);
597
            else
598
              requisition->height = total_size + ((nvis_children - 1)*spacing);
599 600 601 602 603 604 605

            break;
          default:
            g_assert_not_reached ();
            break;
        }

606
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
607
        requisition->height = max_size;
608
      else
609
        requisition->width = max_size;
610 611
    }

612
  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
613 614
  requisition->width += border_width * 2;
  requisition->height += border_width * 2;
615 616 617 618 619 620
}

static void
gtk_button_box_size_allocate (GtkWidget     *widget,
                              GtkAllocation *allocation)
{
621
  GtkButtonBoxPrivate *priv;
622
  GtkButtonBox *bbox;
623
  GList *children, *list;
624 625
  GtkAllocation child_allocation;
  gint nvis_children;
626
  gint n_primaries;
627 628 629 630 631
  gint n_secondaries;
  gint x = 0;
  gint y = 0;
  gint secondary_x = 0;
  gint secondary_y = 0;
632 633
  gint width = 0;
  gint height = 0;
634 635
  gint childspacing = 0;
  gint spacing;
636
  guint border_width;
637
  GtkOrientation orientation;
638 639 640 641 642 643 644 645
  gint ipad_x, ipad_y;
  gint *widths;
  gint *heights;
  gint *sizes;
  gint primary_size;
  gint secondary_size;
  gint total_size;
  gint i;
646

647 648
  bbox = GTK_BUTTON_BOX (widget);
  priv = bbox->priv;
649

650
  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
651
  orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
652
  spacing = gtk_box_get_spacing (GTK_BOX (widget));
653

654 655 656 657 658 659 660 661 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
  gtk_widget_style_get (widget,
                        "child-internal-pad-x", &ipad_x,
                        "child-internal-pad-y", &ipad_y,
                        NULL);
  gtk_button_box_child_requisition (widget,
                                    &nvis_children,
                                    &n_secondaries,
                                    &widths, &heights);

  n_primaries = nvis_children - n_secondaries;
  primary_size = 0;
  secondary_size = 0;
  if (orientation == GTK_ORIENTATION_HORIZONTAL)
    sizes = widths;
  else
    sizes = heights;

  i = 0;
  list = children = _gtk_box_get_children (GTK_BOX (widget));
  while (children)
    {
      GtkWidget *child;

      child = children->data;
      children = children->next;

      if (gtk_widget_get_visible (child))
        {
          if (gtk_button_box_get_child_secondary (bbox, child))
            secondary_size += sizes[i];
          else
            primary_size += sizes[i];
          i++;
        }
    }
  total_size = primary_size + secondary_size;

691
  gtk_widget_set_allocation (widget, allocation);
692 693

  if (orientation == GTK_ORIENTATION_HORIZONTAL)
694
    width = allocation->width - border_width*2;
695
  else
696
    height = allocation->height - border_width*2;
697

698
  switch (priv->layout_style)
699 700 701 702 703
    {
      case GTK_BUTTONBOX_SPREAD:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
704
            childspacing = (width - total_size) / (nvis_children + 1);
705
            x = allocation->x + border_width + childspacing;
706
            secondary_x = x + primary_size + n_primaries * childspacing;
707 708 709
          }
        else
          {
710
            childspacing = (height - total_size) / (nvis_children + 1);
711
            y = allocation->y + border_width + childspacing;
712
            secondary_y = y + primary_size + n_primaries * childspacing;
713 714 715 716 717 718 719 720 721
          }

        break;

      case GTK_BUTTONBOX_EDGE:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            if (nvis_children >= 2)
722
              {
723
                childspacing = (width - total_size) / (nvis_children - 1);
724
                x = allocation->x + border_width;
725
                secondary_x = x + primary_size + n_primaries * childspacing;
726 727 728 729 730 731
              }
            else
              {
                /* one or zero children, just center */
                childspacing = width;
                x = secondary_x = allocation->x
732
                                  + (allocation->width - widths[0]) / 2;
733
              }
734 735 736 737 738
          }
        else
          {
            if (nvis_children >= 2)
              {
739
                childspacing = (height - total_size) / (nvis_children - 1);
740
                y = allocation->y + border_width;
741
                secondary_y = y + primary_size + n_primaries * childspacing;
742 743 744 745 746 747
              }
            else
              {
                /* one or zero children, just center */
                childspacing = height;
                y = secondary_y = allocation->y
748
                        + (allocation->height - heights[0]) / 2;
749 750 751 752 753 754 755 756 757 758
              }
          }

        break;

      case GTK_BUTTONBOX_START:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            childspacing = spacing;
759
            x = allocation->x + border_width;
760
            secondary_x = allocation->x + allocation->width
761
              - secondary_size - spacing * (n_secondaries - 1) - border_width;
762 763 764 765
          }
        else
          {
            childspacing = spacing;
766
            y = allocation->y + border_width;
767
            secondary_y = allocation->y + allocation->height
768
              - secondary_size - spacing * (n_secondaries - 1) - border_width;
769 770 771 772 773 774 775 776 777 778
          }

        break;

      case GTK_BUTTONBOX_END:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            childspacing = spacing;
            x = allocation->x + allocation->width
779
              - primary_size - spacing * (n_primaries - 1) - border_width;
780
            secondary_x = allocation->x + border_width;
781 782 783 784 785
          }
        else
          {
            childspacing = spacing;
            y = allocation->y + allocation->height
786
              - primary_size - spacing * (n_primaries - 1) - border_width;
787
            secondary_y = allocation->y + border_width;
788 789 790 791 792 793 794 795 796 797 798
          }

        break;

      case GTK_BUTTONBOX_CENTER:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            childspacing = spacing;
            x = allocation->x +
              (allocation->width
799 800
               - (primary_size + spacing * (n_primaries - 1))) / 2
              + (secondary_size + n_secondaries * spacing) / 2;
801
            secondary_x = allocation->x + border_width;
802 803 804 805 806 807
          }
        else
          {
            childspacing = spacing;
            y = allocation->y +
              (allocation->height
808 809
               - (primary_size + spacing * (n_primaries - 1))) / 2
              + (secondary_size + n_secondaries * spacing) / 2;
810
            secondary_y = allocation->y + border_width;
811 812 813 814 815
          }

        break;

      default:
816
        g_assert_not_reached ();
817 818 819
        break;
    }

820 821
  children = list;
  i = 0;
822 823
  while (children)
    {
824 825
      GtkWidget *child;

826 827 828
      child = children->data;
      children = children->next;

829
      if (gtk_widget_get_visible (child))
830
        {
831 832
          child_allocation.width = widths[i];
          child_allocation.height = heights[i];
833 834 835

          if (orientation == GTK_ORIENTATION_HORIZONTAL)
            {
836
              child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2;
837

838
              if (gtk_button_box_get_child_secondary (bbox, child))
839 840
                {
                  child_allocation.x = secondary_x;
841
                  secondary_x += child_allocation.width + childspacing;
842 843 844 845
                }
              else
                {
                  child_allocation.x = x;
846
                  x += child_allocation.width + childspacing;
847 848 849 850
                }

              if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
                  child_allocation.x = (allocation->x + allocation->width)
851
                          - (child_allocation.x + child_allocation.width - allocation->x);
852 853 854
            }
          else
            {
855
              child_allocation.x = allocation->x + (allocation->width - child_allocation.width) / 2;
856

857
              if (gtk_button_box_get_child_secondary (bbox, child))
858 859
                {
                  child_allocation.y = secondary_y;
860
                  secondary_y += child_allocation.height + childspacing;
861 862 863 864
                }
              else
                {
                  child_allocation.y = y;
865
                  y += child_allocation.height + childspacing;
866 867 868
                }
            }

869
          gtk_widget_size_allocate (child, &child_allocation);
870
          i++;
871 872
        }
    }
873 874

  g_list_free (list);
875 876
  g_free (widths);
  g_free (heights);
877 878
}

879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
/**
 * 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);
}