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