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

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

25 26
/**
 * SECTION:gtkbbox
27
 * @Short_description: A container for arranging buttons
28 29
 * @Title: GtkButtonBox
 *
30 31 32 33
 * 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.
34 35 36 37 38 39
 *
 * 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
40 41 42
 * 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
43
 * #GtkButtonBox:homogeneous property to %TRUE.
44 45
 *
 * To excempt individual children from homogeneous sizing regardless of their
46
 * 'outlier' status, you can set the #GtkButtonBox:non-homogeneous child
47
 * property.
48 49
 */

50
#include "config.h"
51

Elliot Lee's avatar
Elliot Lee committed
52
#include "gtkbbox.h"
53

54
#include "gtkboxprivate.h"
55
#include "gtkorientable.h"
56
#include "gtktypebuiltins.h"
57
#include "gtkprivate.h"
58 59
#include "gtksizerequest.h"

Havoc Pennington's avatar
Havoc Pennington committed
60
#include "gtkintl.h"
61

Elliot Lee's avatar
Elliot Lee committed
62

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

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

73 74
enum {
  CHILD_PROP_0,
75 76
  CHILD_PROP_SECONDARY,
  CHILD_PROP_NONHOMOGENEOUS
77 78
};

79
#define GTK_BOX_SECONDARY_CHILD "gtk-box-secondary-child"
80
#define GTK_BOX_NON_HOMOGENEOUS "gtk-box-non-homogeneous"
81

82
static void gtk_button_box_set_property       (GObject           *object,
83 84 85
                                               guint              prop_id,
                                               const GValue      *value,
                                               GParamSpec        *pspec);
86
static void gtk_button_box_get_property       (GObject           *object,
87 88 89
                                               guint              prop_id,
                                               GValue            *value,
                                               GParamSpec        *pspec);
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
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);

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

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

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

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

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

139 140
  gobject_class->set_property = gtk_button_box_set_property;
  gobject_class->get_property = gtk_button_box_get_property;
141

142 143
  widget_class->get_preferred_width = gtk_button_box_get_preferred_width;
  widget_class->get_preferred_height = gtk_button_box_get_preferred_height;
144 145
  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;
146 147
  widget_class->size_allocate = gtk_button_box_size_allocate;

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

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

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

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

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

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

208 209 210 211 212 213 214 215
  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));

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

static void
gtk_button_box_init (GtkButtonBox *button_box)
{
222
  GtkButtonBoxPrivate *priv;
223 224 225

  button_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (button_box,
                                                  GTK_TYPE_BUTTON_BOX,
226
                                                  GtkButtonBoxPrivate);
227 228 229
  priv = button_box->priv;

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

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

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

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

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

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

318 319 320 321
static void
gtk_button_box_remove (GtkContainer *container,
                       GtkWidget    *widget)
{
322
  /* clear is_secondary and nonhomogeneous flag in case the widget
323 324
   * is added to another container
   */
325 326
  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);
327 328 329 330

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

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

344
  g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
345

346 347 348
  priv = widget->priv;

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

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

369
  return widget->priv->layout_style;
Elliot Lee's avatar
Elliot Lee committed
370 371
}

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

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

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

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

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

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

Elliot Lee's avatar
Elliot Lee committed
460 461 462 463
  g_return_if_fail (GTK_IS_BUTTON_BOX (widget));

  bbox = GTK_BUTTON_BOX (widget);

464 465
  homogeneous = gtk_box_get_homogeneous (GTK_BOX (widget));

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

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

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

Elliot Lee's avatar
Elliot Lee committed
486 487 488
      child = children->data;
      children = children->next;

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

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

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

512 513 514 515 516 517
      child = children->data;
      children = children->next;

      if (gtk_widget_get_visible (child))
        {
          is_secondary = gtk_button_box_get_child_secondary (bbox, child);
518 519
          non_homogeneous = gtk_button_box_get_child_non_homogeneous (bbox, child);

520 521
          if (is_secondary)
            nsecondaries++;
522

523
          gtk_widget_get_preferred_size (child, &child_requisition, NULL);
524

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

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

553 554
  g_list_free (list);

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

563 564
  if (nvis_children)
    *nvis_children = nchildren;
565

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

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

  bbox = GTK_BUTTON_BOX (widget);
586
  priv = bbox->priv;
587 588

  orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
589
  spacing = gtk_box_get_spacing (GTK_BOX (widget));
590

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

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

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

            break;
          default:
            g_assert_not_reached ();
            break;
        }

645
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
646
        requisition->height = max_size;
647
      else
648
        requisition->width = max_size;
649 650 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
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;
}

676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
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);
}

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

723 724
  bbox = GTK_BUTTON_BOX (widget);
  priv = bbox->priv;
725 726

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

766
  gtk_widget_set_allocation (widget, allocation);
767 768

  if (orientation == GTK_ORIENTATION_HORIZONTAL)
769
    width = allocation->width;
770
  else
771
    height = allocation->height;
772

773
  switch (priv->layout_style)
774 775 776 777 778
    {
      case GTK_BUTTONBOX_SPREAD:

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

        break;

      case GTK_BUTTONBOX_EDGE:

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

        break;

      case GTK_BUTTONBOX_START:

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

        break;

      case GTK_BUTTONBOX_END:

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

        break;

      case GTK_BUTTONBOX_CENTER:

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

        break;

      default:
903
        g_assert_not_reached ();
904 905 906
        break;
    }

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

913 914 915
      child = children->data;
      children = children->next;

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

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

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

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

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

956
          gtk_widget_size_allocate (child, &child_allocation);
957
          i++;
958 959
        }
    }
960 961

  g_list_free (list);
962 963
  g_free (widths);
  g_free (heights);
964 965
}

966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982
/**
 * 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);
}
983 984 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

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