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