]> Pileus Git - ~andy/gtk/blob - gtk/gtkbbox.c
6056e43a8892e460a213467d5bd9f47506aa3e87
[~andy/gtk] / gtk / gtkbbox.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * 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: Base class for GtkHButtonBox and GtkVButtonBox
30  * @Title: GtkButtonBox
31  * @See_also: #GtkVButtonBox, #GtkHButtonBox
32  *
33  * The primary purpose of this class is to keep track of the various
34  * properties of #GtkHButtonBox and #GtkVButtonBox widgets.
35  *
36  * gtk_button_box_get_layout() and gtk_button_box_set_layout() retrieve and
37  * alter the method used to spread the buttons in a button box across the
38  * container, respectively.
39  *
40  * The main purpose of GtkButtonBox is to make sure the children have all the
41  * same size. GtkButtonBox gives all children the same size, but it does allow
42  * 'outliers' to keep their own larger size. To force all children to be
43  * strictly the same size without exceptions, you can set the
44  * #GtkButtonBox::homogeneous property to %TRUE.
45  */
46
47 #include "config.h"
48 #include "gtkbbox.h"
49 #include "gtkorientable.h"
50 #include "gtkprivate.h"
51 #include "gtkintl.h"
52
53
54 struct _GtkButtonBoxPrivate
55 {
56   GtkButtonBoxStyle layout_style;
57 };
58
59 enum {
60   PROP_0,
61   PROP_LAYOUT_STYLE
62 };
63
64 enum {
65   CHILD_PROP_0,
66   CHILD_PROP_SECONDARY
67 };
68
69 #define GTK_BOX_SECONDARY_CHILD "gtk-box-secondary-child"
70
71 static void gtk_button_box_set_property       (GObject           *object,
72                                                guint              prop_id,
73                                                const GValue      *value,
74                                                GParamSpec        *pspec);
75 static void gtk_button_box_get_property       (GObject           *object,
76                                                guint              prop_id,
77                                                GValue            *value,
78                                                GParamSpec        *pspec);
79 static void gtk_button_box_size_request       (GtkWidget         *widget,
80                                                GtkRequisition    *requisition);
81 static void gtk_button_box_size_allocate      (GtkWidget         *widget,
82                                                GtkAllocation     *allocation);
83 static void gtk_button_box_remove             (GtkContainer      *container,
84                                                GtkWidget         *widget);
85 static void gtk_button_box_set_child_property (GtkContainer      *container,
86                                                GtkWidget         *child,
87                                                guint              property_id,
88                                                const GValue      *value,
89                                                GParamSpec        *pspec);
90 static void gtk_button_box_get_child_property (GtkContainer      *container,
91                                                GtkWidget         *child,
92                                                guint              property_id,
93                                                GValue            *value,
94                                                GParamSpec        *pspec);
95
96 #define DEFAULT_CHILD_MIN_WIDTH 85
97 #define DEFAULT_CHILD_MIN_HEIGHT 27
98 #define DEFAULT_CHILD_IPAD_X 4
99 #define DEFAULT_CHILD_IPAD_Y 0
100 #define DEFAULT_LAYOUT_STYLE GTK_BUTTONBOX_EDGE
101
102 G_DEFINE_TYPE (GtkButtonBox, gtk_button_box, GTK_TYPE_BOX)
103
104 static void
105 gtk_button_box_class_init (GtkButtonBoxClass *class)
106 {
107   GtkWidgetClass *widget_class;
108   GObjectClass *gobject_class;
109   GtkContainerClass *container_class;
110
111   gobject_class = G_OBJECT_CLASS (class);
112   widget_class = (GtkWidgetClass*) class;
113   container_class = (GtkContainerClass*) class;
114
115   gobject_class->set_property = gtk_button_box_set_property;
116   gobject_class->get_property = gtk_button_box_get_property;
117
118   widget_class->size_request = gtk_button_box_size_request;
119   widget_class->size_allocate = gtk_button_box_size_allocate;
120
121   container_class->remove = gtk_button_box_remove;
122   container_class->set_child_property = gtk_button_box_set_child_property;
123   container_class->get_child_property = gtk_button_box_get_child_property;
124
125   /* FIXME we need to override the "spacing" property on GtkBox once
126    * libgobject allows that.
127    */
128   gtk_widget_class_install_style_property (widget_class,
129                                            g_param_spec_int ("child-min-width",
130                                                              P_("Minimum child width"),
131                                                              P_("Minimum width of buttons inside the box"),
132                                                              0,
133                                                              G_MAXINT,
134                                                              DEFAULT_CHILD_MIN_WIDTH,
135                                                              GTK_PARAM_READABLE));
136
137   gtk_widget_class_install_style_property (widget_class,
138                                            g_param_spec_int ("child-min-height",
139                                                              P_("Minimum child height"),
140                                                              P_("Minimum height of buttons inside the box"),
141                                                              0,
142                                                              G_MAXINT,
143                                                              DEFAULT_CHILD_MIN_HEIGHT,
144                                                              GTK_PARAM_READABLE));
145
146   gtk_widget_class_install_style_property (widget_class,
147                                            g_param_spec_int ("child-internal-pad-x",
148                                                              P_("Child internal width padding"),
149                                                              P_("Amount to increase child's size on either side"),
150                                                              0,
151                                                              G_MAXINT,
152                                                              DEFAULT_CHILD_IPAD_X,
153                                                              GTK_PARAM_READABLE));
154
155   gtk_widget_class_install_style_property (widget_class,
156                                            g_param_spec_int ("child-internal-pad-y",
157                                                              P_("Child internal height padding"),
158                                                              P_("Amount to increase child's size on the top and bottom"),
159                                                              0,
160                                                              G_MAXINT,
161                                                              DEFAULT_CHILD_IPAD_Y,
162                                                              GTK_PARAM_READABLE));
163   g_object_class_install_property (gobject_class,
164                                    PROP_LAYOUT_STYLE,
165                                    g_param_spec_enum ("layout-style",
166                                                       P_("Layout style"),
167                                                       P_("How to lay out the buttons in the box. Possible values are: spread, edge, start and end"),
168                                                       GTK_TYPE_BUTTON_BOX_STYLE,
169                                                       DEFAULT_LAYOUT_STYLE,
170                                                       GTK_PARAM_READWRITE));
171
172   gtk_container_class_install_child_property (container_class,
173                                               CHILD_PROP_SECONDARY,
174                                               g_param_spec_boolean ("secondary", 
175                                                                     P_("Secondary"),
176                                                                     P_("If TRUE, the child appears in a secondary group of children, suitable for, e.g., help buttons"),
177                                                                     FALSE,
178                                                                     GTK_PARAM_READWRITE));
179
180   g_type_class_add_private (class, sizeof (GtkButtonBoxPrivate));
181 }
182
183 static void
184 gtk_button_box_init (GtkButtonBox *button_box)
185 {
186   GtkButtonBoxPrivate *priv;
187
188   button_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (button_box,
189                                                   GTK_TYPE_BUTTON_BOX,
190                                                   GtkButtonBoxPrivate);
191   priv = button_box->priv;
192
193   gtk_box_set_spacing (GTK_BOX (button_box), 0);
194   priv->layout_style = DEFAULT_LAYOUT_STYLE;
195 }
196
197 static void
198 gtk_button_box_set_property (GObject      *object,
199                              guint         prop_id,
200                              const GValue *value,
201                              GParamSpec   *pspec)
202 {
203   switch (prop_id)
204     {
205     case PROP_LAYOUT_STYLE:
206       gtk_button_box_set_layout (GTK_BUTTON_BOX (object),
207                                  g_value_get_enum (value));
208       break;
209     default:
210       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
211       break;
212     }
213 }
214
215 static void
216 gtk_button_box_get_property (GObject    *object,
217                              guint       prop_id,
218                              GValue     *value,
219                              GParamSpec *pspec)
220 {
221   GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (object)->priv;
222
223   switch (prop_id)
224     {
225     case PROP_LAYOUT_STYLE:
226       g_value_set_enum (value, priv->layout_style);
227       break;
228     default:
229       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
230       break;
231     }
232 }
233
234 static void
235 gtk_button_box_set_child_property (GtkContainer *container,
236                                    GtkWidget    *child,
237                                    guint         property_id,
238                                    const GValue *value,
239                                    GParamSpec   *pspec)
240 {
241   switch (property_id)
242     {
243     case CHILD_PROP_SECONDARY:
244       gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), child,
245                                           g_value_get_boolean (value));
246       break;
247     default:
248       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
249       break;
250     }
251 }
252
253 static void
254 gtk_button_box_get_child_property (GtkContainer *container,
255                                    GtkWidget    *child,
256                                    guint         property_id,
257                                    GValue       *value,
258                                    GParamSpec   *pspec)
259 {
260   switch (property_id)
261     {
262     case CHILD_PROP_SECONDARY:
263       g_value_set_boolean (value,
264                            gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (container),
265                                                                child));
266       break;
267     default:
268       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
269       break;
270     }
271 }
272
273 static void
274 gtk_button_box_remove (GtkContainer *container,
275                        GtkWidget    *widget)
276 {
277   /* clear is_secondary flag in case the widget
278    * is added to another container
279    */
280   gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container),
281                                       widget,
282                                       FALSE);
283
284   GTK_CONTAINER_CLASS (gtk_button_box_parent_class)->remove (container, widget);
285 }
286
287 /**
288  * gtk_button_box_set_layout:
289  * @widget: a #GtkButtonBox
290  * @layout_style: the new layout style
291  *
292  * Changes the way buttons are arranged in their container.
293  */
294 void
295 gtk_button_box_set_layout (GtkButtonBox      *widget,
296                            GtkButtonBoxStyle  layout_style)
297 {
298   GtkButtonBoxPrivate *priv;
299
300   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
301
302   priv = widget->priv;
303
304   if (priv->layout_style != layout_style)
305     {
306       priv->layout_style = layout_style;
307       g_object_notify (G_OBJECT (widget), "layout-style");
308       gtk_widget_queue_resize (GTK_WIDGET (widget));
309     }
310 }
311
312 /**
313  * gtk_button_box_get_layout:
314  * @widget: a #GtkButtonBox
315  *
316  * Retrieves the method being used to arrange the buttons in a button box.
317  *
318  * Returns: the method used to lay out buttons in @widget.
319  */
320 GtkButtonBoxStyle
321 gtk_button_box_get_layout (GtkButtonBox *widget)
322 {
323   g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), DEFAULT_LAYOUT_STYLE);
324
325   return widget->priv->layout_style;
326 }
327
328 /**
329  * gtk_button_box_get_child_secondary:
330  * @widget: a #GtkButtonBox
331  * @child: a child of @widget
332  *
333  * Returns whether @child should appear in a secondary group of children.
334  *
335  * Return value: whether @child should appear in a secondary group of children.
336  *
337  * Since: 2.4
338  **/
339 gboolean
340 gtk_button_box_get_child_secondary (GtkButtonBox *widget,
341                                     GtkWidget    *child)
342 {
343   g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE);
344   g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
345
346   return (g_object_get_data (G_OBJECT (child), GTK_BOX_SECONDARY_CHILD) != NULL);
347 }
348
349 /**
350  * gtk_button_box_set_child_secondary
351  * @widget: a #GtkButtonBox
352  * @child: a child of @widget
353  * @is_secondary: if %TRUE, the @child appears in a secondary group of the
354  *                button box.
355  *
356  * Sets whether @child should appear in a secondary group of children.
357  * A typical use of a secondary child is the help button in a dialog.
358  *
359  * This group appears after the other children if the style
360  * is %GTK_BUTTONBOX_START, %GTK_BUTTONBOX_SPREAD or
361  * %GTK_BUTTONBOX_EDGE, and before the other children if the style
362  * is %GTK_BUTTONBOX_END. For horizontal button boxes, the definition
363  * of before/after depends on direction of the widget (see
364  * gtk_widget_set_direction()). If the style is %GTK_BUTTONBOX_START
365  * or %GTK_BUTTONBOX_END, then the secondary children are aligned at
366  * the other end of the button box from the main children. For the
367  * other styles, they appear immediately next to the main children.
368  **/
369 void
370 gtk_button_box_set_child_secondary (GtkButtonBox *widget,
371                                     GtkWidget    *child,
372                                     gboolean      is_secondary)
373 {
374   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
375   g_return_if_fail (GTK_IS_WIDGET (child));
376   g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (widget));
377
378   g_object_set_data (G_OBJECT (child),
379                      GTK_BOX_SECONDARY_CHILD,
380                      is_secondary ? GINT_TO_POINTER (1) : NULL);
381   gtk_widget_child_notify (child, "secondary");
382
383   if (gtk_widget_get_visible (GTK_WIDGET (widget)) &&
384       gtk_widget_get_visible (child))
385     gtk_widget_queue_resize (child);
386 }
387
388 /* Ask children how much space they require and round up
389  * to match minimum size and internal padding.
390  * Returns the size each single child should have.
391  */
392 static void
393 gtk_button_box_child_requisition (GtkWidget  *widget,
394                                   gint       *nvis_children,
395                                   gint       *nvis_secondaries,
396                                   gint      **widths,
397                                   gint      **heights)
398 {
399   GtkButtonBoxPrivate *priv;
400   GtkButtonBox *bbox;
401   GList *children, *list;
402   gint nchildren;
403   gint nsecondaries;
404   gint needed_width;
405   gint needed_height;
406   gint avg_w, avg_h;
407   GtkRequisition child_requisition;
408   gint ipad_w;
409   gint ipad_h;
410   gint child_min_width;
411   gint child_min_height;
412   gint ipad_x;
413   gint ipad_y;
414   gboolean homogeneous;
415   gint i;
416
417   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
418
419   bbox = GTK_BUTTON_BOX (widget);
420   priv = bbox->priv;
421
422   homogeneous = gtk_box_get_homogeneous (GTK_BOX (widget));
423
424   gtk_widget_style_get (widget,
425                         "child-min-width", &child_min_width,
426                         "child-min-height", &child_min_height,
427                         "child-internal-pad-x", &ipad_x,
428                         "child-internal-pad-y", &ipad_y,
429                         NULL);
430
431   nchildren = 0;
432   nsecondaries = 0;
433   list = children = _gtk_box_get_children (GTK_BOX (bbox));
434   needed_width = child_min_width;
435   needed_height = child_min_height;
436   ipad_w = ipad_x * 2;
437   ipad_h = ipad_y * 2;
438
439   avg_w = avg_h = 0;
440   while (children)
441     {
442       GtkWidget *child;
443
444       child = children->data;
445       children = children->next;
446
447       if (gtk_widget_get_visible (child))
448         {
449           nchildren += 1;
450           gtk_widget_size_request (child, &child_requisition);
451           avg_w += child_requisition.width + ipad_w;
452           avg_h += child_requisition.height + ipad_h;
453         }
454     }
455   avg_w /= MAX (nchildren, 1);
456   avg_h /= MAX (nchildren, 1);
457
458   *widths = g_new (gint, nchildren);
459   *heights = g_new (gint, nchildren);
460
461   i = 0;
462   children = list;
463   while (children)
464     {
465       GtkWidget *child;
466       gboolean is_secondary;
467
468       child = children->data;
469       children = children->next;
470
471       if (gtk_widget_get_visible (child))
472         {
473           is_secondary = gtk_button_box_get_child_secondary (bbox, child);
474           if (is_secondary)
475             nsecondaries++;
476
477           gtk_widget_get_child_requisition (child, &child_requisition);
478
479           if (homogeneous || (child_requisition.width + ipad_w < avg_w * 1.5))
480             {
481               (*widths)[i] = -1;
482               if (child_requisition.width + ipad_w > needed_width)
483                 needed_width = child_requisition.width + ipad_w;
484             }
485           else
486             {
487               (*widths)[i] = child_requisition.width + ipad_w;
488             }
489
490           if (homogeneous || (child_requisition.height + ipad_h < avg_h * 1.5))
491             {
492               (*heights)[i] = -1;
493               if (child_requisition.height + ipad_h > needed_height)
494                 needed_height = child_requisition.height + ipad_h;
495             }
496           else
497             {
498               (*heights)[i] = child_requisition.height + ipad_h;
499             }
500
501           i++;
502         }
503     }
504
505   g_list_free (list);
506
507   for (i = 0; i < nchildren; i++)
508     {
509       if ((*widths)[i] == -1)
510         (*widths)[i] = needed_width;
511       if ((*heights)[i] == -1)
512         (*heights)[i] = needed_height;
513     }
514
515   if (nvis_children)
516     *nvis_children = nchildren;
517
518   if (nvis_secondaries)
519     *nvis_secondaries = nsecondaries;
520 }
521
522 static void
523 gtk_button_box_size_request (GtkWidget      *widget,
524                              GtkRequisition *requisition)
525 {
526   GtkButtonBoxPrivate *priv;
527   GtkButtonBox *bbox;
528   gint nvis_children;
529   gint max_size;
530   gint total_size;
531   gint spacing;
532   guint border_width;
533   GtkOrientation orientation;
534   gint *widths;
535   gint *heights;
536   gint i;
537
538   bbox = GTK_BUTTON_BOX (widget);
539   priv = bbox->priv;
540
541   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
542   spacing = gtk_box_get_spacing (GTK_BOX (widget));
543
544   gtk_button_box_child_requisition (widget,
545                                     &nvis_children,
546                                     NULL,
547                                     &widths, &heights);
548
549   max_size = 0;
550   total_size = 0;
551   for (i = 0; i < nvis_children; i++)
552     {
553       if (orientation == GTK_ORIENTATION_HORIZONTAL)
554         {
555           total_size += widths[i];
556           max_size = MAX (max_size, heights[i]);
557         }
558       else
559         {
560           total_size += heights[i];
561           max_size = MAX (max_size, widths[i]);
562         }
563     }
564   g_free (widths);
565   g_free (heights);
566
567   if (nvis_children == 0)
568     {
569       requisition->width = 0;
570       requisition->height = 0;
571     }
572   else
573     {
574       switch (priv->layout_style)
575         {
576           case GTK_BUTTONBOX_SPREAD:
577             if (orientation == GTK_ORIENTATION_HORIZONTAL)
578               requisition->width = total_size + ((nvis_children + 1)*spacing);
579             else
580               requisition->height = total_size + ((nvis_children + 1)*spacing);
581
582             break;
583           case GTK_BUTTONBOX_EDGE:
584           case GTK_BUTTONBOX_START:
585           case GTK_BUTTONBOX_END:
586           case GTK_BUTTONBOX_CENTER:
587             if (orientation == GTK_ORIENTATION_HORIZONTAL)
588               requisition->width = total_size + ((nvis_children - 1)*spacing);
589             else
590               requisition->height = total_size + ((nvis_children - 1)*spacing);
591
592             break;
593           default:
594             g_assert_not_reached ();
595             break;
596         }
597
598       if (orientation == GTK_ORIENTATION_HORIZONTAL)
599         requisition->height = max_size;
600       else
601         requisition->width = max_size;
602     }
603
604   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
605   requisition->width += border_width * 2;
606   requisition->height += border_width * 2;
607 }
608
609 static void
610 gtk_button_box_size_allocate (GtkWidget     *widget,
611                               GtkAllocation *allocation)
612 {
613   GtkButtonBoxPrivate *priv;
614   GtkButtonBox *bbox;
615   GList *children, *list;
616   GtkAllocation child_allocation;
617   gint nvis_children;
618   gint n_primaries;
619   gint n_secondaries;
620   gint x = 0;
621   gint y = 0;
622   gint secondary_x = 0;
623   gint secondary_y = 0;
624   gint width = 0;
625   gint height = 0;
626   gint childspacing = 0;
627   gint spacing;
628   guint border_width;
629   GtkOrientation orientation;
630   gint ipad_x, ipad_y;
631   gint *widths;
632   gint *heights;
633   gint *sizes;
634   gint primary_size;
635   gint secondary_size;
636   gint total_size;
637   gint i;
638
639   bbox = GTK_BUTTON_BOX (widget);
640   priv = bbox->priv;
641
642   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
643   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
644   spacing = gtk_box_get_spacing (GTK_BOX (widget));
645
646   gtk_widget_style_get (widget,
647                         "child-internal-pad-x", &ipad_x,
648                         "child-internal-pad-y", &ipad_y,
649                         NULL);
650   gtk_button_box_child_requisition (widget,
651                                     &nvis_children,
652                                     &n_secondaries,
653                                     &widths, &heights);
654
655   n_primaries = nvis_children - n_secondaries;
656   primary_size = 0;
657   secondary_size = 0;
658   if (orientation == GTK_ORIENTATION_HORIZONTAL)
659     sizes = widths;
660   else
661     sizes = heights;
662
663   i = 0;
664   list = children = _gtk_box_get_children (GTK_BOX (widget));
665   while (children)
666     {
667       GtkWidget *child;
668
669       child = children->data;
670       children = children->next;
671
672       if (gtk_widget_get_visible (child))
673         {
674           if (gtk_button_box_get_child_secondary (bbox, child))
675             secondary_size += sizes[i];
676           else
677             primary_size += sizes[i];
678           i++;
679         }
680     }
681   total_size = primary_size + secondary_size;
682
683   gtk_widget_set_allocation (widget, allocation);
684
685   if (orientation == GTK_ORIENTATION_HORIZONTAL)
686     width = allocation->width - border_width*2;
687   else
688     height = allocation->height - border_width*2;
689
690   switch (priv->layout_style)
691     {
692       case GTK_BUTTONBOX_SPREAD:
693
694         if (orientation == GTK_ORIENTATION_HORIZONTAL)
695           {
696             childspacing = (width - total_size) / (nvis_children + 1);
697             x = allocation->x + border_width + childspacing;
698             secondary_x = x + primary_size + n_primaries * childspacing;
699           }
700         else
701           {
702             childspacing = (height - total_size) / (nvis_children + 1);
703             y = allocation->y + border_width + childspacing;
704             secondary_y = y + primary_size + n_primaries * childspacing;
705           }
706
707         break;
708
709       case GTK_BUTTONBOX_EDGE:
710
711         if (orientation == GTK_ORIENTATION_HORIZONTAL)
712           {
713             if (nvis_children >= 2)
714               {
715                 childspacing = (width - total_size) / (nvis_children - 1);
716                 x = allocation->x + border_width;
717                 secondary_x = x + primary_size + n_primaries * childspacing;
718               }
719             else
720               {
721                 /* one or zero children, just center */
722                 childspacing = width;
723                 x = secondary_x = allocation->x
724                                   + (allocation->width - widths[0]) / 2;
725               }
726           }
727         else
728           {
729             if (nvis_children >= 2)
730               {
731                 childspacing = (height - total_size) / (nvis_children - 1);
732                 y = allocation->y + border_width;
733                 secondary_y = y + primary_size + n_primaries * childspacing;
734               }
735             else
736               {
737                 /* one or zero children, just center */
738                 childspacing = height;
739                 y = secondary_y = allocation->y
740                         + (allocation->height - heights[0]) / 2;
741               }
742           }
743
744         break;
745
746       case GTK_BUTTONBOX_START:
747
748         if (orientation == GTK_ORIENTATION_HORIZONTAL)
749           {
750             childspacing = spacing;
751             x = allocation->x + border_width;
752             secondary_x = allocation->x + allocation->width
753               - secondary_size - spacing * (n_secondaries - 1) - border_width;
754           }
755         else
756           {
757             childspacing = spacing;
758             y = allocation->y + border_width;
759             secondary_y = allocation->y + allocation->height
760               - secondary_size - spacing * (n_secondaries - 1) - border_width;
761           }
762
763         break;
764
765       case GTK_BUTTONBOX_END:
766
767         if (orientation == GTK_ORIENTATION_HORIZONTAL)
768           {
769             childspacing = spacing;
770             x = allocation->x + allocation->width
771               - primary_size - spacing * (n_primaries - 1) - border_width;
772             secondary_x = allocation->x + border_width;
773           }
774         else
775           {
776             childspacing = spacing;
777             y = allocation->y + allocation->height
778               - primary_size - spacing * (n_primaries - 1) - border_width;
779             secondary_y = allocation->y + border_width;
780           }
781
782         break;
783
784       case GTK_BUTTONBOX_CENTER:
785
786         if (orientation == GTK_ORIENTATION_HORIZONTAL)
787           {
788             childspacing = spacing;
789             x = allocation->x +
790               (allocation->width
791                - (primary_size + spacing * (n_primaries - 1))) / 2
792               + (secondary_size + n_secondaries * spacing) / 2;
793             secondary_x = allocation->x + border_width;
794           }
795         else
796           {
797             childspacing = spacing;
798             y = allocation->y +
799               (allocation->height
800                - (primary_size + spacing * (n_primaries - 1))) / 2
801               + (secondary_size + n_secondaries * spacing) / 2;
802             secondary_y = allocation->y + border_width;
803           }
804
805         break;
806
807       default:
808         g_assert_not_reached ();
809         break;
810     }
811
812   children = list;
813   i = 0;
814   while (children)
815     {
816       GtkWidget *child;
817
818       child = children->data;
819       children = children->next;
820
821       if (gtk_widget_get_visible (child))
822         {
823           child_allocation.width = widths[i];
824           child_allocation.height = heights[i];
825
826           if (orientation == GTK_ORIENTATION_HORIZONTAL)
827             {
828               child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2;
829
830               if (gtk_button_box_get_child_secondary (bbox, child))
831                 {
832                   child_allocation.x = secondary_x;
833                   secondary_x += child_allocation.width + childspacing;
834                 }
835               else
836                 {
837                   child_allocation.x = x;
838                   x += child_allocation.width + childspacing;
839                 }
840
841               if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
842                   child_allocation.x = (allocation->x + allocation->width)
843                           - (child_allocation.x + child_allocation.width - allocation->x);
844             }
845           else
846             {
847               child_allocation.x = allocation->x + (allocation->width - child_allocation.width) / 2;
848
849               if (gtk_button_box_get_child_secondary (bbox, child))
850                 {
851                   child_allocation.y = secondary_y;
852                   secondary_y += child_allocation.height + childspacing;
853                 }
854               else
855                 {
856                   child_allocation.y = y;
857                   y += child_allocation.height + childspacing;
858                 }
859             }
860
861           gtk_widget_size_allocate (child, &child_allocation);
862           i++;
863         }
864     }
865
866   g_list_free (list);
867   g_free (widths);
868   g_free (heights);
869 }
870
871 /**
872  * gtk_button_box_new:
873  * @orientation: the box' orientation.
874  *
875  * Creates a new #GtkButtonBox.
876  *
877  * Return value: a new #GtkButtonBox.
878  *
879  * Since: 3.0
880  */
881 GtkWidget *
882 gtk_button_box_new (GtkOrientation orientation)
883 {
884   return g_object_new (GTK_TYPE_BUTTON_BOX,
885                        "orientation", orientation,
886                        NULL);
887 }