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