gtkbbox.c 34.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
/**
 * SECTION:gtkbbox
29
 * @Short_description: A container for arranging buttons
30 31
 * @Title: GtkButtonBox
 *
32 33 34 35
 * A button box should be used to provide a consistent layout of buttons
 * throughout your application. The layout/spacing can be altered by the
 * programmer, or if desired, by the user to alter the 'feel' of a
 * program to a small degree.
36 37 38 39 40 41
 *
 * 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
42 43 44 45
 * same size. GtkButtonBox gives all children the same size, but it does allow
 * 'outliers' to keep their own larger size. To force all children to be
 * strictly the same size without exceptions, you can set the
 * #GtkButtonBox::homogeneous property to %TRUE.
46 47 48 49
 *
 * To excempt individual children from homogeneous sizing regardless of their
 * 'outlier' status, you can set the #GtkButtonBox::non-homogeneous child
 * property.
50 51
 */

52
#include "config.h"
53

Elliot Lee's avatar
Elliot Lee committed
54
#include "gtkbbox.h"
55

56
#include "gtkboxprivate.h"
57
#include "gtkorientable.h"
58
#include "gtktypebuiltins.h"
59
#include "gtkprivate.h"
60 61
#include "gtksizerequest.h"

Havoc Pennington's avatar
Havoc Pennington committed
62
#include "gtkintl.h"
63

Elliot Lee's avatar
Elliot Lee committed
64

65
struct _GtkButtonBoxPrivate
66 67 68 69
{
  GtkButtonBoxStyle layout_style;
};

70 71
enum {
  PROP_0,
72
  PROP_LAYOUT_STYLE
73
};
Elliot Lee's avatar
Elliot Lee committed
74

75 76
enum {
  CHILD_PROP_0,
77 78
  CHILD_PROP_SECONDARY,
  CHILD_PROP_NONHOMOGENEOUS
79 80
};

81
#define GTK_BOX_SECONDARY_CHILD "gtk-box-secondary-child"
82
#define GTK_BOX_NON_HOMOGENEOUS "gtk-box-non-homogeneous"
83

84
static void gtk_button_box_set_property       (GObject           *object,
85 86 87
                                               guint              prop_id,
                                               const GValue      *value,
                                               GParamSpec        *pspec);
88
static void gtk_button_box_get_property       (GObject           *object,
89 90 91
                                               guint              prop_id,
                                               GValue            *value,
                                               GParamSpec        *pspec);
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
static void gtk_button_box_get_preferred_width            (GtkWidget *widget,
                                                           gint      *minimum,
                                                           gint      *natural);
static void gtk_button_box_get_preferred_height           (GtkWidget *widget,
                                                           gint      *minimum,
                                                           gint      *natural);
static void gtk_button_box_get_preferred_width_for_height (GtkWidget *widget,
                                                           gint       height,
                                                           gint      *minimum,
                                                           gint      *natural);
static void gtk_button_box_get_preferred_height_for_width (GtkWidget *widget,
                                                           gint       width,
                                                           gint      *minimum,
                                                           gint      *natural);

107 108
static void gtk_button_box_size_allocate      (GtkWidget         *widget,
                                               GtkAllocation     *allocation);
109 110
static void gtk_button_box_remove             (GtkContainer      *container,
                                               GtkWidget         *widget);
111
static void gtk_button_box_set_child_property (GtkContainer      *container,
112 113 114 115
                                               GtkWidget         *child,
                                               guint              property_id,
                                               const GValue      *value,
                                               GParamSpec        *pspec);
116
static void gtk_button_box_get_child_property (GtkContainer      *container,
117 118 119 120
                                               GtkWidget         *child,
                                               guint              property_id,
                                               GValue            *value,
                                               GParamSpec        *pspec);
Elliot Lee's avatar
Elliot Lee committed
121

Havoc Pennington's avatar
Havoc Pennington committed
122 123
#define DEFAULT_CHILD_MIN_WIDTH 85
#define DEFAULT_CHILD_MIN_HEIGHT 27
124
#define DEFAULT_CHILD_IPAD_X 4
Havoc Pennington's avatar
Havoc Pennington committed
125
#define DEFAULT_CHILD_IPAD_Y 0
126
#define DEFAULT_LAYOUT_STYLE GTK_BUTTONBOX_EDGE
Elliot Lee's avatar
Elliot Lee committed
127

128
G_DEFINE_TYPE (GtkButtonBox, gtk_button_box, GTK_TYPE_BOX)
Elliot Lee's avatar
Elliot Lee committed
129 130 131 132 133

static void
gtk_button_box_class_init (GtkButtonBoxClass *class)
{
  GtkWidgetClass *widget_class;
134
  GObjectClass *gobject_class;
135
  GtkContainerClass *container_class;
Elliot Lee's avatar
Elliot Lee committed
136

137
  gobject_class = G_OBJECT_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
138
  widget_class = (GtkWidgetClass*) class;
139
  container_class = (GtkContainerClass*) class;
140

141 142
  gobject_class->set_property = gtk_button_box_set_property;
  gobject_class->get_property = gtk_button_box_get_property;
143

144 145
  widget_class->get_preferred_width = gtk_button_box_get_preferred_width;
  widget_class->get_preferred_height = gtk_button_box_get_preferred_height;
146 147
  widget_class->get_preferred_width_for_height = gtk_button_box_get_preferred_width_for_height;
  widget_class->get_preferred_height_for_width = gtk_button_box_get_preferred_height_for_width;
148 149
  widget_class->size_allocate = gtk_button_box_size_allocate;

150
  container_class->remove = gtk_button_box_remove;
151 152
  container_class->set_child_property = gtk_button_box_set_child_property;
  container_class->get_child_property = gtk_button_box_get_child_property;
153
  gtk_container_class_handle_border_width (container_class);
154

155 156 157
  /* FIXME we need to override the "spacing" property on GtkBox once
   * libgobject allows that.
   */
Havoc Pennington's avatar
Havoc Pennington committed
158
  gtk_widget_class_install_style_property (widget_class,
159 160 161 162 163
                                           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
164
                                                             DEFAULT_CHILD_MIN_WIDTH,
165
                                                             GTK_PARAM_READABLE));
Havoc Pennington's avatar
Havoc Pennington committed
166 167

  gtk_widget_class_install_style_property (widget_class,
168 169 170 171 172
                                           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
173
                                                             DEFAULT_CHILD_MIN_HEIGHT,
174
                                                             GTK_PARAM_READABLE));
Havoc Pennington's avatar
Havoc Pennington committed
175 176

  gtk_widget_class_install_style_property (widget_class,
177 178 179 180 181
                                           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
182
                                                             DEFAULT_CHILD_IPAD_X,
183
                                                             GTK_PARAM_READABLE));
Havoc Pennington's avatar
Havoc Pennington committed
184 185

  gtk_widget_class_install_style_property (widget_class,
186 187 188 189 190
                                           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
191
                                                             DEFAULT_CHILD_IPAD_Y,
192
                                                             GTK_PARAM_READABLE));
193 194
  g_object_class_install_property (gobject_class,
                                   PROP_LAYOUT_STYLE,
195
                                   g_param_spec_enum ("layout-style",
196
                                                      P_("Layout style"),
197
                                                      P_("How to lay out the buttons in the box. Possible values are: spread, edge, start and end"),
198 199
                                                      GTK_TYPE_BUTTON_BOX_STYLE,
                                                      DEFAULT_LAYOUT_STYLE,
200
                                                      GTK_PARAM_READWRITE));
201 202

  gtk_container_class_install_child_property (container_class,
203 204 205 206 207 208
                                              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));
209

210 211 212 213 214 215 216 217
  gtk_container_class_install_child_property (container_class,
                                              CHILD_PROP_NONHOMOGENEOUS,
                                              g_param_spec_boolean ("non-homogeneous",
                                                                    P_("Non-Homogeneous"),
                                                                    P_("If TRUE, the child will not be subject to homogeneous sizing"),
                                                                    FALSE,
                                                                    GTK_PARAM_READWRITE));

218
  g_type_class_add_private (class, sizeof (GtkButtonBoxPrivate));
Elliot Lee's avatar
Elliot Lee committed
219 220 221 222 223
}

static void
gtk_button_box_init (GtkButtonBox *button_box)
{
224
  GtkButtonBoxPrivate *priv;
225 226 227

  button_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (button_box,
                                                  GTK_TYPE_BUTTON_BOX,
228
                                                  GtkButtonBoxPrivate);
229 230 231
  priv = button_box->priv;

  gtk_box_set_spacing (GTK_BOX (button_box), 0);
232
  priv->layout_style = DEFAULT_LAYOUT_STYLE;
Elliot Lee's avatar
Elliot Lee committed
233 234
}

235
static void
236 237 238 239
gtk_button_box_set_property (GObject      *object,
                             guint         prop_id,
                             const GValue *value,
                             GParamSpec   *pspec)
240
{
241
  switch (prop_id)
242 243 244
    {
    case PROP_LAYOUT_STYLE:
      gtk_button_box_set_layout (GTK_BUTTON_BOX (object),
245
                                 g_value_get_enum (value));
246 247 248 249 250 251 252 253
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
254 255 256 257
gtk_button_box_get_property (GObject    *object,
                             guint       prop_id,
                             GValue     *value,
                             GParamSpec *pspec)
258
{
259
  GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (object)->priv;
260

261 262 263
  switch (prop_id)
    {
    case PROP_LAYOUT_STYLE:
264
      g_value_set_enum (value, priv->layout_style);
265 266 267 268 269 270 271
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

272
static void
273 274 275 276 277
gtk_button_box_set_child_property (GtkContainer *container,
                                   GtkWidget    *child,
                                   guint         property_id,
                                   const GValue *value,
                                   GParamSpec   *pspec)
278 279 280 281 282
{
  switch (property_id)
    {
    case CHILD_PROP_SECONDARY:
      gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), child,
283
                                          g_value_get_boolean (value));
284
      break;
285 286 287 288
    case CHILD_PROP_NONHOMOGENEOUS:
      gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (container), child,
                                                g_value_get_boolean (value));
      break;
289 290 291 292 293 294 295 296
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      break;
    }
}

static void
gtk_button_box_get_child_property (GtkContainer *container,
297 298 299 300
                                   GtkWidget    *child,
                                   guint         property_id,
                                   GValue       *value,
                                   GParamSpec   *pspec)
301 302 303 304
{
  switch (property_id)
    {
    case CHILD_PROP_SECONDARY:
305 306 307
      g_value_set_boolean (value,
                           gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (container),
                                                               child));
308
      break;
309 310 311 312 313
    case CHILD_PROP_NONHOMOGENEOUS:
      g_value_set_boolean (value,
                           gtk_button_box_get_child_non_homogeneous (GTK_BUTTON_BOX (container),
                                                                     child));
      break;
314 315 316 317 318
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      break;
    }
}
319

320 321 322 323
static void
gtk_button_box_remove (GtkContainer *container,
                       GtkWidget    *widget)
{
324
  /* clear is_secondary and nonhomogeneous flag in case the widget
325 326
   * is added to another container
   */
327 328
  gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), widget, FALSE);
  gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (container), widget, FALSE);
329 330 331 332

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

333 334 335 336 337 338 339
/**
 * gtk_button_box_set_layout:
 * @widget: a #GtkButtonBox
 * @layout_style: the new layout style
 *
 * Changes the way buttons are arranged in their container.
 */
340
void
341
gtk_button_box_set_layout (GtkButtonBox      *widget,
342
                           GtkButtonBoxStyle  layout_style)
Elliot Lee's avatar
Elliot Lee committed
343
{
344
  GtkButtonBoxPrivate *priv;
345

346
  g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
347

348 349 350
  priv = widget->priv;

  if (priv->layout_style != layout_style)
351
    {
352
      priv->layout_style = layout_style;
353
      g_object_notify (G_OBJECT (widget), "layout-style");
354 355
      gtk_widget_queue_resize (GTK_WIDGET (widget));
    }
Elliot Lee's avatar
Elliot Lee committed
356 357
}

358 359 360 361 362 363
/**
 * gtk_button_box_get_layout:
 * @widget: a #GtkButtonBox
 *
 * Retrieves the method being used to arrange the buttons in a button box.
 *
364
 * Returns: the method used to lay out buttons in @widget.
365
 */
366
GtkButtonBoxStyle
367
gtk_button_box_get_layout (GtkButtonBox *widget)
Elliot Lee's avatar
Elliot Lee committed
368
{
369 370
  g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), DEFAULT_LAYOUT_STYLE);

371
  return widget->priv->layout_style;
Elliot Lee's avatar
Elliot Lee committed
372 373
}

Matthias Clasen's avatar
Matthias Clasen committed
374 375 376
/**
 * gtk_button_box_get_child_secondary:
 * @widget: a #GtkButtonBox
377 378
 * @child: a child of @widget
 *
Matthias Clasen's avatar
Matthias Clasen committed
379 380 381 382 383 384
 * 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
 **/
385
gboolean
Matthias Clasen's avatar
Matthias Clasen committed
386
gtk_button_box_get_child_secondary (GtkButtonBox *widget,
387
                                    GtkWidget    *child)
Matthias Clasen's avatar
Matthias Clasen committed
388
{
389 390
  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
391

392
  return (g_object_get_data (G_OBJECT (child), GTK_BOX_SECONDARY_CHILD) != NULL);
Matthias Clasen's avatar
Matthias Clasen committed
393 394
}

395 396 397 398 399 400 401 402 403 404 405 406
/**
 * 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
407
 * %GTK_BUTTONBOX_EDGE, and before the other children if the style
408
 * is %GTK_BUTTONBOX_END. For horizontal button boxes, the definition
Matthias Clasen's avatar
Matthias Clasen committed
409 410 411
 * 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
412 413 414
 * the other end of the button box from the main children. For the
 * other styles, they appear immediately next to the main children.
 **/
415 416 417 418
void
gtk_button_box_set_child_secondary (GtkButtonBox *widget,
                                    GtkWidget    *child,
                                    gboolean      is_secondary)
419 420 421
{
  g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
  g_return_if_fail (GTK_IS_WIDGET (child));
422
  g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (widget));
Elliot Lee's avatar
Elliot Lee committed
423

424 425 426
  g_object_set_data (G_OBJECT (child),
                     GTK_BOX_SECONDARY_CHILD,
                     is_secondary ? GINT_TO_POINTER (1) : NULL);
427 428
  gtk_widget_child_notify (child, "secondary");

429 430
  if (gtk_widget_get_visible (GTK_WIDGET (widget)) &&
      gtk_widget_get_visible (child))
431 432
    gtk_widget_queue_resize (child);
}
Elliot Lee's avatar
Elliot Lee committed
433

434
/* Ask children how much space they require and round up
435 436 437
 * to match minimum size and internal padding.
 * Returns the size each single child should have.
 */
438
static void
439 440 441 442 443
gtk_button_box_child_requisition (GtkWidget  *widget,
                                  gint       *nvis_children,
                                  gint       *nvis_secondaries,
                                  gint      **widths,
                                  gint      **heights)
Elliot Lee's avatar
Elliot Lee committed
444 445
{
  GtkButtonBox *bbox;
446
  GList *children, *list;
Elliot Lee's avatar
Elliot Lee committed
447
  gint nchildren;
448
  gint nsecondaries;
Elliot Lee's avatar
Elliot Lee committed
449 450
  gint needed_width;
  gint needed_height;
451
  gint avg_w, avg_h;
Elliot Lee's avatar
Elliot Lee committed
452 453 454 455 456 457 458
  GtkRequisition child_requisition;
  gint ipad_w;
  gint ipad_h;
  gint child_min_width;
  gint child_min_height;
  gint ipad_x;
  gint ipad_y;
459 460
  gboolean homogeneous;
  gint i;
461

Elliot Lee's avatar
Elliot Lee committed
462 463 464 465
  g_return_if_fail (GTK_IS_BUTTON_BOX (widget));

  bbox = GTK_BUTTON_BOX (widget);

466 467
  homogeneous = gtk_box_get_homogeneous (GTK_BOX (widget));

Havoc Pennington's avatar
Havoc Pennington committed
468
  gtk_widget_style_get (widget,
469 470 471 472
                        "child-min-width", &child_min_width,
                        "child-min-height", &child_min_height,
                        "child-internal-pad-x", &ipad_x,
                        "child-internal-pad-y", &ipad_y,
473 474
                        NULL);

Elliot Lee's avatar
Elliot Lee committed
475
  nchildren = 0;
476
  nsecondaries = 0;
477
  list = children = _gtk_box_get_children (GTK_BOX (bbox));
Elliot Lee's avatar
Elliot Lee committed
478
  needed_width = child_min_width;
479
  needed_height = child_min_height;
Elliot Lee's avatar
Elliot Lee committed
480 481
  ipad_w = ipad_x * 2;
  ipad_h = ipad_y * 2;
482 483

  avg_w = avg_h = 0;
Elliot Lee's avatar
Elliot Lee committed
484 485
  while (children)
    {
486 487
      GtkWidget *child;

Elliot Lee's avatar
Elliot Lee committed
488 489 490
      child = children->data;
      children = children->next;

491
      if (gtk_widget_get_visible (child))
492 493
        {
          nchildren += 1;
494 495
          gtk_widget_get_preferred_size (child,
                                         &child_requisition, NULL);
496 497 498 499
          avg_w += child_requisition.width + ipad_w;
          avg_h += child_requisition.height + ipad_h;
        }
    }
500 501
  avg_w /= MAX (nchildren, 1);
  avg_h /= MAX (nchildren, 1);
502 503 504 505 506 507 508 509 510 511

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

  i = 0;
  children = list;
  while (children)
    {
      GtkWidget *child;
      gboolean is_secondary;
512
      gboolean non_homogeneous;
513

514 515 516 517 518 519
      child = children->data;
      children = children->next;

      if (gtk_widget_get_visible (child))
        {
          is_secondary = gtk_button_box_get_child_secondary (bbox, child);
520 521
          non_homogeneous = gtk_button_box_get_child_non_homogeneous (bbox, child);

522 523
          if (is_secondary)
            nsecondaries++;
524

525
          gtk_widget_get_preferred_size (child, &child_requisition, NULL);
526

527 528
          if (homogeneous ||
              (!non_homogeneous && (child_requisition.width + ipad_w < avg_w * 1.5)))
529 530 531 532 533 534 535 536 537 538
            {
              (*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;
            }

539 540
          if (homogeneous ||
              (!non_homogeneous && (child_requisition.height + ipad_h < avg_h * 1.5)))
541 542 543 544 545 546 547 548 549 550 551
            {
              (*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++;
552
        }
Elliot Lee's avatar
Elliot Lee committed
553
    }
554

555 556
  g_list_free (list);

557 558 559 560 561 562 563 564
  for (i = 0; i < nchildren; i++)
    {
      if ((*widths)[i] == -1)
        (*widths)[i] = needed_width;
      if ((*heights)[i] == -1)
        (*heights)[i] = needed_height;
    }

565 566
  if (nvis_children)
    *nvis_children = nchildren;
567

568 569
  if (nvis_secondaries)
    *nvis_secondaries = nsecondaries;
Elliot Lee's avatar
Elliot Lee committed
570
}
571

572 573 574 575
static void
gtk_button_box_size_request (GtkWidget      *widget,
                             GtkRequisition *requisition)
{
576
  GtkButtonBoxPrivate *priv;
577 578
  GtkButtonBox *bbox;
  gint nvis_children;
579 580
  gint max_size;
  gint total_size;
581 582
  gint spacing;
  GtkOrientation orientation;
583 584 585
  gint *widths;
  gint *heights;
  gint i;
586 587

  bbox = GTK_BUTTON_BOX (widget);
588
  priv = bbox->priv;
589 590

  orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
591
  spacing = gtk_box_get_spacing (GTK_BOX (widget));
592

593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
  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);
615 616 617 618 619 620 621 622

  if (nvis_children == 0)
    {
      requisition->width = 0;
      requisition->height = 0;
    }
  else
    {
623
      switch (priv->layout_style)
624 625 626
        {
          case GTK_BUTTONBOX_SPREAD:
            if (orientation == GTK_ORIENTATION_HORIZONTAL)
627
              requisition->width = total_size + ((nvis_children + 1)*spacing);
628
            else
629
              requisition->height = total_size + ((nvis_children + 1)*spacing);
630 631 632 633 634 635 636

            break;
          case GTK_BUTTONBOX_EDGE:
          case GTK_BUTTONBOX_START:
          case GTK_BUTTONBOX_END:
          case GTK_BUTTONBOX_CENTER:
            if (orientation == GTK_ORIENTATION_HORIZONTAL)
637
              requisition->width = total_size + ((nvis_children - 1)*spacing);
638
            else
639
              requisition->height = total_size + ((nvis_children - 1)*spacing);
640 641 642 643 644 645 646

            break;
          default:
            g_assert_not_reached ();
            break;
        }

647
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
648
        requisition->height = max_size;
649
      else
650
        requisition->width = max_size;
651 652 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
static void
gtk_button_box_get_preferred_width (GtkWidget *widget,
                                    gint      *minimum,
                                    gint      *natural)
{
  GtkRequisition requisition;

  gtk_button_box_size_request (widget, &requisition);

  *minimum = *natural = requisition.width;
}

static void
gtk_button_box_get_preferred_height (GtkWidget *widget,
                                     gint      *minimum,
                                     gint      *natural)
{
  GtkRequisition requisition;

  gtk_button_box_size_request (widget, &requisition);

  *minimum = *natural = requisition.height;
}

678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
static void
gtk_button_box_get_preferred_width_for_height (GtkWidget *widget,
                                               gint       height,
                                               gint      *minimum,
                                               gint      *natural)
{
  gtk_button_box_get_preferred_width (widget, minimum, natural);
}

static void
gtk_button_box_get_preferred_height_for_width (GtkWidget *widget,
                                               gint       width,
                                               gint      *minimum,
                                               gint      *natural)
{
  gtk_button_box_get_preferred_height (widget, minimum, natural);
}

696 697 698 699
static void
gtk_button_box_size_allocate (GtkWidget     *widget,
                              GtkAllocation *allocation)
{
700
  GtkButtonBoxPrivate *priv;
701
  GtkButtonBox *bbox;
702
  GList *children, *list;
703 704
  GtkAllocation child_allocation;
  gint nvis_children;
705
  gint n_primaries;
706 707 708 709 710
  gint n_secondaries;
  gint x = 0;
  gint y = 0;
  gint secondary_x = 0;
  gint secondary_y = 0;
711 712
  gint width = 0;
  gint height = 0;
713 714 715
  gint childspacing = 0;
  gint spacing;
  GtkOrientation orientation;
716 717 718 719 720 721 722 723
  gint ipad_x, ipad_y;
  gint *widths;
  gint *heights;
  gint *sizes;
  gint primary_size;
  gint secondary_size;
  gint total_size;
  gint i;
724

725 726
  bbox = GTK_BUTTON_BOX (widget);
  priv = bbox->priv;
727 728

  orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
729
  spacing = gtk_box_get_spacing (GTK_BOX (widget));
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 763 764 765 766 767
  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;

768
  gtk_widget_set_allocation (widget, allocation);
769 770

  if (orientation == GTK_ORIENTATION_HORIZONTAL)
771
    width = allocation->width;
772
  else
773
    height = allocation->height;
774

775
  switch (priv->layout_style)
776 777 778 779 780
    {
      case GTK_BUTTONBOX_SPREAD:

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

        break;

      case GTK_BUTTONBOX_EDGE:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            if (nvis_children >= 2)
799
              {
800
                childspacing = (width - total_size) / (nvis_children - 1);
801
                x = allocation->x;
802
                secondary_x = x + primary_size + n_primaries * childspacing;
803
              }
804
            else if (nvis_children == 1)
805
              {
806
                /* one child, just center */
807 808
                childspacing = width;
                x = secondary_x = allocation->x
809
                                  + (allocation->width - widths[0]) / 2;
810
              }
811 812 813 814 815 816
            else
              {
                /* zero children, meh */
                childspacing = width;
                x = secondary_x = allocation->x + allocation->width / 2;
              }
817 818 819 820 821
          }
        else
          {
            if (nvis_children >= 2)
              {
822
                childspacing = (height - total_size) / (nvis_children - 1);
823
                y = allocation->y;
824
                secondary_y = y + primary_size + n_primaries * childspacing;
825
              }
826
            else if (nvis_children == 1)
827
              {
828
                /* one child, just center */
829 830
                childspacing = height;
                y = secondary_y = allocation->y
831 832 833 834 835 836 837
                                     + (allocation->height - heights[0]) / 2;
              }
            else
              {
                /* zero children, meh */
                childspacing = height;
                y = secondary_y = allocation->y + allocation->height / 2;
838 839 840 841 842 843 844 845 846 847
              }
          }

        break;

      case GTK_BUTTONBOX_START:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            childspacing = spacing;
848
            x = allocation->x;
849
            secondary_x = allocation->x + allocation->width
850
              - secondary_size - spacing * (n_secondaries - 1);
851 852 853 854
          }
        else
          {
            childspacing = spacing;
855
            y = allocation->y;
856
            secondary_y = allocation->y + allocation->height
857
              - secondary_size - spacing * (n_secondaries - 1);
858 859 860 861 862 863 864 865 866 867
          }

        break;

      case GTK_BUTTONBOX_END:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            childspacing = spacing;
            x = allocation->x + allocation->width
868 869
              - primary_size - spacing * (n_primaries - 1);
            secondary_x = allocation->x;
870 871 872 873 874
          }
        else
          {
            childspacing = spacing;
            y = allocation->y + allocation->height
875 876
              - primary_size - spacing * (n_primaries - 1);
            secondary_y = allocation->y;
877 878 879 880 881 882 883 884 885 886 887
          }

        break;

      case GTK_BUTTONBOX_CENTER:

        if (orientation == GTK_ORIENTATION_HORIZONTAL)
          {
            childspacing = spacing;
            x = allocation->x +
              (allocation->width
888 889
               - (primary_size + spacing * (n_primaries - 1))) / 2
              + (secondary_size + n_secondaries * spacing) / 2;
890
            secondary_x = allocation->x;
891 892 893 894 895 896
          }
        else
          {
            childspacing = spacing;
            y = allocation->y +
              (allocation->height
897 898
               - (primary_size + spacing * (n_primaries - 1))) / 2
              + (secondary_size + n_secondaries * spacing) / 2;
899
            secondary_y = allocation->y;
900 901 902 903 904
          }

        break;

      default:
905
        g_assert_not_reached ();
906 907 908
        break;
    }

909 910
  children = list;
  i = 0;
911 912
  while (children)
    {
913 914
      GtkWidget *child;

915 916 917
      child = children->data;
      children = children->next;

918
      if (gtk_widget_get_visible (child))
919
        {
920 921
          child_allocation.width = widths[i];
          child_allocation.height = heights[i];
922 923 924

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

927
              if (gtk_button_box_get_child_secondary (bbox, child))
928 929
                {
                  child_allocation.x = secondary_x;
930
                  secondary_x += child_allocation.width + childspacing;
931 932 933 934
                }
              else
                {
                  child_allocation.x = x;
935
                  x += child_allocation.width + childspacing;
936 937 938 939
                }

              if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
                  child_allocation.x = (allocation->x + allocation->width)
940
                          - (child_allocation.x + child_allocation.width - allocation->x);
941 942 943
            }
          else
            {
944
              child_allocation.x = allocation->x + (allocation->width - child_allocation.width) / 2;
945

946
              if (gtk_button_box_get_child_secondary (bbox, child))
947 948
                {
                  child_allocation.y = secondary_y;
949
                  secondary_y += child_allocation.height + childspacing;
950 951 952 953
                }
              else
                {
                  child_allocation.y = y;
954
                  y += child_allocation.height + childspacing;
955 956 957
                }
            }

958
          gtk_widget_size_allocate (child, &child_allocation);
959
          i++;
960 961
        }
    }
962 963

  g_list_free (list);
964 965
  g_free (widths);
  g_free (heights);
966 967
}

968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
/**
 * 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);
}
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035

/**
 * gtk_button_box_get_child_non_homogeneous:
 * @widget: a #GtkButtonBox
 * @child: a child of @widget
 *
 * Returns whether the child is exempted from homogenous
 * sizing.
 *
 * Returns: %TRUE if the child is not subject to homogenous sizing
 *
 * Since: 3.2
 */
gboolean
gtk_button_box_get_child_non_homogeneous (GtkButtonBox *widget,
                                          GtkWidget    *child)
{
  g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE);
  g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);

  return (g_object_get_data (G_OBJECT (child), GTK_BOX_NON_HOMOGENEOUS) != NULL);
}

/**
 * gtk_button_box_set_child_non_homogeneous:
 * @widget: a #GtkButtonBox
 * @child: a child of @widget
 * @non_homogeneous: the new value
 *
 * Sets whether the child is exempted from homogeous sizing.
 *
 * Since: 3.2
 */
void
gtk_button_box_set_child_non_homogeneous (GtkButtonBox *widget,
                                          GtkWidget    *child,
                                          gboolean      non_homogeneous)
{
  g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
  g_return_if_fail (GTK_IS_WIDGET (child));
  g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (widget));

  g_object_set_data (G_OBJECT (child),
                     GTK_BOX_NON_HOMOGENEOUS,
                     non_homogeneous ? GINT_TO_POINTER (1) : NULL);
  gtk_widget_child_notify (child, "non-homogeneous");

  if (gtk_widget_get_visible (GTK_WIDGET (widget)) &&
      gtk_widget_get_visible (child))
    gtk_widget_queue_resize (child);
}