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