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