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, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23  */
24 
25 /**
26  * SECTION:gtkbbox
27  * @Short_description: A container for arranging buttons
28  * @Title: GtkButtonBox
29  *
30  * A button box should be used to provide a consistent layout of buttons
31  * throughout your application. The layout/spacing can be altered by the
32  * programmer, or if desired, by the user to alter the “feel” of a
33  * program to a small degree.
34  *
35  * gtk_button_box_get_layout() and gtk_button_box_set_layout() retrieve and
36  * alter the method used to spread the buttons in a button box across the
37  * container, respectively.
38  *
39  * The main purpose of GtkButtonBox is to make sure the children have all the
40  * same size. GtkButtonBox gives all children the same size, but it does allow
41  * 'outliers' to keep their own larger size.
42  *
43  * To exempt individual children from homogeneous sizing regardless of their
44  * 'outlier' status, you can set the non-homogeneous child
45  * property.
46  *
47  * # CSS nodes
48  *
49  * GtkButtonBox uses a single CSS node with name buttonbox.
50  */
51 
52 #include "config.h"
53 
54 #include "gtkbbox.h"
55 
56 #include "gtkboxprivate.h"
57 #include "gtkorientable.h"
58 #include "gtktypebuiltins.h"
59 #include "gtkprivate.h"
60 #include "gtksizerequest.h"
61 #include "gtkwidgetprivate.h"
62 #include "gtkcontainerprivate.h"
63 #include "gtkcsscustomgadgetprivate.h"
64 #include "gtkintl.h"
65 
66 
67 struct _GtkButtonBoxPrivate
68 {
69   GtkButtonBoxStyle layout_style;
70   GtkCssGadget *gadget;
71 };
72 
73 enum {
74   PROP_0,
75   PROP_LAYOUT_STYLE
76 };
77 
78 enum {
79   CHILD_PROP_0,
80   CHILD_PROP_SECONDARY,
81   CHILD_PROP_NONHOMOGENEOUS
82 };
83 
84 #define GTK_BOX_SECONDARY_CHILD "gtk-box-secondary-child"
85 #define GTK_BOX_NON_HOMOGENEOUS "gtk-box-non-homogeneous"
86 
87 static void gtk_button_box_set_property       (GObject           *object,
88                                                guint              prop_id,
89                                                const GValue      *value,
90                                                GParamSpec        *pspec);
91 static void gtk_button_box_get_property       (GObject           *object,
92                                                guint              prop_id,
93                                                GValue            *value,
94                                                GParamSpec        *pspec);
95 static gboolean gtk_button_box_draw           (GtkWidget         *widget,
96                                                cairo_t           *cr);
97 static void gtk_button_box_get_preferred_width            (GtkWidget *widget,
98                                                            gint      *minimum,
99                                                            gint      *natural);
100 static void gtk_button_box_get_preferred_height           (GtkWidget *widget,
101                                                            gint      *minimum,
102                                                            gint      *natural);
103 static void gtk_button_box_get_preferred_width_for_height (GtkWidget *widget,
104                                                            gint       height,
105                                                            gint      *minimum,
106                                                            gint      *natural);
107 static void gtk_button_box_get_preferred_height_for_width (GtkWidget *widget,
108                                                            gint       width,
109                                                            gint      *minimum,
110                                                            gint      *natural);
111 static void gtk_button_box_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
112 									gint       width,
113 									gint      *minimum,
114 									gint      *natural,
115 									gint      *minimum_baseline,
116 									gint      *natural_baseline);
117 
118 static void gtk_button_box_size_allocate      (GtkWidget         *widget,
119                                                GtkAllocation     *allocation);
120 static void gtk_button_box_remove             (GtkContainer      *container,
121                                                GtkWidget         *widget);
122 static void gtk_button_box_set_child_property (GtkContainer      *container,
123                                                GtkWidget         *child,
124                                                guint              property_id,
125                                                const GValue      *value,
126                                                GParamSpec        *pspec);
127 static void gtk_button_box_get_child_property (GtkContainer      *container,
128                                                GtkWidget         *child,
129                                                guint              property_id,
130                                                GValue            *value,
131                                                GParamSpec        *pspec);
132 
133 static void     gtk_button_box_measure         (GtkCssGadget        *gadget,
134                                                 GtkOrientation       orientation,
135                                                 int                  for_size,
136                                                 int                 *minimum,
137                                                 int                 *natural,
138                                                 int                 *minimum_baseline,
139                                                 int                 *natural_baseline,
140                                                 gpointer             unused);
141 static void     gtk_button_box_allocate        (GtkCssGadget        *gadget,
142                                                 const GtkAllocation *allocation,
143                                                 int                  baseline,
144                                                 GtkAllocation       *out_clip,
145                                                 gpointer             unused);
146 static gboolean gtk_button_box_render          (GtkCssGadget        *gadget,
147                                                 cairo_t             *cr,
148                                                 int                  x,
149                                                 int                  y,
150                                                 int                  width,
151                                                 int                  height,
152                                                 gpointer             data);
153 
154 #define DEFAULT_CHILD_MIN_WIDTH 85
155 #define DEFAULT_CHILD_MIN_HEIGHT 27
156 #define DEFAULT_CHILD_IPAD_X 4
157 #define DEFAULT_CHILD_IPAD_Y 0
158 #define DEFAULT_LAYOUT_STYLE GTK_BUTTONBOX_EDGE
159 
G_DEFINE_TYPE_WITH_PRIVATE(GtkButtonBox,gtk_button_box,GTK_TYPE_BOX)160 G_DEFINE_TYPE_WITH_PRIVATE (GtkButtonBox, gtk_button_box, GTK_TYPE_BOX)
161 
162 static void
163 gtk_button_box_add (GtkContainer *container,
164                     GtkWidget    *widget)
165 {
166   gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
167 }
168 
169 static void
gtk_button_box_finalize(GObject * object)170 gtk_button_box_finalize (GObject *object)
171 {
172   GtkButtonBox *button_box = GTK_BUTTON_BOX (object);
173 
174   g_clear_object (&button_box->priv->gadget);
175 
176   G_OBJECT_CLASS (gtk_button_box_parent_class)->finalize (object);
177 }
178 
179 static void
gtk_button_box_class_init(GtkButtonBoxClass * class)180 gtk_button_box_class_init (GtkButtonBoxClass *class)
181 {
182   GtkWidgetClass *widget_class;
183   GObjectClass *gobject_class;
184   GtkContainerClass *container_class;
185 
186   gobject_class = G_OBJECT_CLASS (class);
187   widget_class = (GtkWidgetClass*) class;
188   container_class = (GtkContainerClass*) class;
189 
190   gobject_class->set_property = gtk_button_box_set_property;
191   gobject_class->get_property = gtk_button_box_get_property;
192   gobject_class->finalize = gtk_button_box_finalize;
193 
194   widget_class->get_preferred_width = gtk_button_box_get_preferred_width;
195   widget_class->get_preferred_height = gtk_button_box_get_preferred_height;
196   widget_class->get_preferred_width_for_height = gtk_button_box_get_preferred_width_for_height;
197   widget_class->get_preferred_height_for_width = gtk_button_box_get_preferred_height_for_width;
198   widget_class->get_preferred_height_and_baseline_for_width = gtk_button_box_get_preferred_height_and_baseline_for_width;
199   widget_class->size_allocate = gtk_button_box_size_allocate;
200   widget_class->draw = gtk_button_box_draw;
201 
202   container_class->remove = gtk_button_box_remove;
203   container_class->add = gtk_button_box_add;
204   container_class->set_child_property = gtk_button_box_set_child_property;
205   container_class->get_child_property = gtk_button_box_get_child_property;
206   gtk_container_class_handle_border_width (container_class);
207 
208   /**
209    * GtkButtonBox:child-min-width:
210    *
211    * The minimum width of buttons inside the box.
212    *
213    * Deprecated: 3.20: Use CSS min-width instead.
214    */
215   gtk_widget_class_install_style_property (widget_class,
216                                            g_param_spec_int ("child-min-width",
217                                                              P_("Minimum child width"),
218                                                              P_("Minimum width of buttons inside the box"),
219                                                              0,
220                                                              G_MAXINT,
221                                                              DEFAULT_CHILD_MIN_WIDTH,
222                                                              GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
223 
224   /**
225    * GtkButtonBox:child-min-height:
226    *
227    * The minimum height of buttons inside the box.
228    *
229    * Deprecated: 3.20: Use CSS min-height instead.
230    */
231   gtk_widget_class_install_style_property (widget_class,
232                                            g_param_spec_int ("child-min-height",
233                                                              P_("Minimum child height"),
234                                                              P_("Minimum height of buttons inside the box"),
235                                                              0,
236                                                              G_MAXINT,
237                                                              DEFAULT_CHILD_MIN_HEIGHT,
238                                                              GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
239 
240   /**
241    * GtkButtonBox:child-internal-pad-x:
242    *
243    * The amount to increase a child's size on either side.
244    *
245    * Deprecated: 3.20: Use CSS padding instead.
246    */
247   gtk_widget_class_install_style_property (widget_class,
248                                            g_param_spec_int ("child-internal-pad-x",
249                                                              P_("Child internal width padding"),
250                                                              P_("Amount to increase child's size on either side"),
251                                                              0,
252                                                              G_MAXINT,
253                                                              DEFAULT_CHILD_IPAD_X,
254                                                              GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
255 
256   /**
257    * GtkButtonBox:child-internal-pad-y:
258    *
259    * The amount to increase a child's size on the top and bottom.
260    *
261    * Deprecated: 3.20: Use CSS padding instead.
262    */
263   gtk_widget_class_install_style_property (widget_class,
264                                            g_param_spec_int ("child-internal-pad-y",
265                                                              P_("Child internal height padding"),
266                                                              P_("Amount to increase child's size on the top and bottom"),
267                                                              0,
268                                                              G_MAXINT,
269                                                              DEFAULT_CHILD_IPAD_Y,
270                                                              GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
271 
272   g_object_class_install_property (gobject_class,
273                                    PROP_LAYOUT_STYLE,
274                                    g_param_spec_enum ("layout-style",
275                                                       P_("Layout style"),
276                                                       P_("How to lay out the buttons in the box. Possible values are: spread, edge, start and end"),
277                                                       GTK_TYPE_BUTTON_BOX_STYLE,
278                                                       DEFAULT_LAYOUT_STYLE,
279                                                       GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
280 
281   gtk_container_class_install_child_property (container_class,
282                                               CHILD_PROP_SECONDARY,
283                                               g_param_spec_boolean ("secondary",
284                                                                     P_("Secondary"),
285                                                                     P_("If TRUE, the child appears in a secondary group of children, suitable for, e.g., help buttons"),
286                                                                     FALSE,
287                                                                     GTK_PARAM_READWRITE));
288 
289   gtk_container_class_install_child_property (container_class,
290                                               CHILD_PROP_NONHOMOGENEOUS,
291                                               g_param_spec_boolean ("non-homogeneous",
292                                                                     P_("Non-Homogeneous"),
293                                                                     P_("If TRUE, the child will not be subject to homogeneous sizing"),
294                                                                     FALSE,
295                                                                     GTK_PARAM_READWRITE));
296 
297   gtk_widget_class_set_css_name (widget_class, "buttonbox");
298 }
299 
300 static gboolean
gtk_button_box_render(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer unused)301 gtk_button_box_render (GtkCssGadget *gadget,
302                        cairo_t      *cr,
303                        int           x,
304                        int           y,
305                        int           width,
306                        int           height,
307                        gpointer      unused)
308 {
309   GtkWidget *widget;
310   GList *children, *l;
311 
312   widget = gtk_css_gadget_get_owner (gadget);
313   children = gtk_container_get_children (GTK_CONTAINER (widget));
314 
315   for (l = children; l; l = l->next)
316     {
317       GtkWidget *child = l->data;
318 
319       gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
320     }
321 
322   g_list_free (children);
323 
324   return FALSE;
325 }
326 
327 
328 static gboolean
gtk_button_box_draw(GtkWidget * widget,cairo_t * cr)329 gtk_button_box_draw (GtkWidget *widget,
330                      cairo_t   *cr)
331 {
332   GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (widget)->priv;
333   GtkCssGadget *gadget;
334 
335   if (priv->layout_style == GTK_BUTTONBOX_EXPAND)
336     gadget = gtk_box_get_gadget (GTK_BOX (widget));
337   else
338     gadget = priv->gadget;
339 
340   gtk_css_gadget_draw (gadget, cr);
341 
342   return FALSE;
343 }
344 
345 static void
gtk_button_box_init(GtkButtonBox * button_box)346 gtk_button_box_init (GtkButtonBox *button_box)
347 {
348   button_box->priv = gtk_button_box_get_instance_private (button_box);
349   button_box->priv->layout_style = DEFAULT_LAYOUT_STYLE;
350 
351   gtk_box_set_spacing (GTK_BOX (button_box), 0);
352 
353   button_box->priv->gadget = gtk_css_custom_gadget_new_for_node (gtk_widget_get_css_node (GTK_WIDGET (button_box)),
354                                                          GTK_WIDGET (button_box),
355                                                          gtk_button_box_measure,
356                                                          gtk_button_box_allocate,
357                                                          gtk_button_box_render,
358                                                          NULL,
359                                                          NULL);
360 }
361 
362 static void
gtk_button_box_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)363 gtk_button_box_set_property (GObject      *object,
364                              guint         prop_id,
365                              const GValue *value,
366                              GParamSpec   *pspec)
367 {
368   switch (prop_id)
369     {
370     case PROP_LAYOUT_STYLE:
371       gtk_button_box_set_layout (GTK_BUTTON_BOX (object),
372                                  g_value_get_enum (value));
373       break;
374     default:
375       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
376       break;
377     }
378 }
379 
380 static void
gtk_button_box_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)381 gtk_button_box_get_property (GObject    *object,
382                              guint       prop_id,
383                              GValue     *value,
384                              GParamSpec *pspec)
385 {
386   GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (object)->priv;
387 
388   switch (prop_id)
389     {
390     case PROP_LAYOUT_STYLE:
391       g_value_set_enum (value, priv->layout_style);
392       break;
393     default:
394       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
395       break;
396     }
397 }
398 
399 static void
gtk_button_box_set_child_property(GtkContainer * container,GtkWidget * child,guint property_id,const GValue * value,GParamSpec * pspec)400 gtk_button_box_set_child_property (GtkContainer *container,
401                                    GtkWidget    *child,
402                                    guint         property_id,
403                                    const GValue *value,
404                                    GParamSpec   *pspec)
405 {
406   switch (property_id)
407     {
408     case CHILD_PROP_SECONDARY:
409       gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), child,
410                                           g_value_get_boolean (value));
411       break;
412     case CHILD_PROP_NONHOMOGENEOUS:
413       gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (container), child,
414                                                 g_value_get_boolean (value));
415       break;
416     default:
417       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
418       break;
419     }
420 }
421 
422 static void
gtk_button_box_get_child_property(GtkContainer * container,GtkWidget * child,guint property_id,GValue * value,GParamSpec * pspec)423 gtk_button_box_get_child_property (GtkContainer *container,
424                                    GtkWidget    *child,
425                                    guint         property_id,
426                                    GValue       *value,
427                                    GParamSpec   *pspec)
428 {
429   switch (property_id)
430     {
431     case CHILD_PROP_SECONDARY:
432       g_value_set_boolean (value,
433                            gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (container),
434                                                                child));
435       break;
436     case CHILD_PROP_NONHOMOGENEOUS:
437       g_value_set_boolean (value,
438                            gtk_button_box_get_child_non_homogeneous (GTK_BUTTON_BOX (container),
439                                                                      child));
440       break;
441     default:
442       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
443       break;
444     }
445 }
446 
447 static void
gtk_button_box_remove(GtkContainer * container,GtkWidget * widget)448 gtk_button_box_remove (GtkContainer *container,
449                        GtkWidget    *widget)
450 {
451   /* clear is_secondary and nonhomogeneous flag in case the widget
452    * is added to another container
453    */
454   gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), widget, FALSE);
455   gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (container), widget, FALSE);
456 
457   GTK_CONTAINER_CLASS (gtk_button_box_parent_class)->remove (container, widget);
458 }
459 
460 /**
461  * gtk_button_box_set_layout:
462  * @widget: a #GtkButtonBox
463  * @layout_style: the new layout style
464  *
465  * Changes the way buttons are arranged in their container.
466  */
467 void
gtk_button_box_set_layout(GtkButtonBox * widget,GtkButtonBoxStyle layout_style)468 gtk_button_box_set_layout (GtkButtonBox      *widget,
469                            GtkButtonBoxStyle  layout_style)
470 {
471   GtkButtonBoxPrivate *priv;
472 
473   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
474 
475   priv = widget->priv;
476 
477   if (priv->layout_style != layout_style)
478     {
479       priv->layout_style = layout_style;
480 
481       if (priv->layout_style == GTK_BUTTONBOX_EXPAND)
482         {
483           gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (widget)), "linked");
484           gtk_box_set_spacing (GTK_BOX (widget), 0);
485           gtk_box_set_homogeneous (GTK_BOX (widget), TRUE);
486         }
487       else
488         {
489           gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET (widget)), "linked");
490           gtk_box_set_homogeneous (GTK_BOX (widget), FALSE);
491         }
492 
493       g_object_notify (G_OBJECT (widget), "layout-style");
494       gtk_widget_queue_resize (GTK_WIDGET (widget));
495     }
496 }
497 
498 /**
499  * gtk_button_box_get_layout:
500  * @widget: a #GtkButtonBox
501  *
502  * Retrieves the method being used to arrange the buttons in a button box.
503  *
504  * Returns: the method used to lay out buttons in @widget.
505  */
506 GtkButtonBoxStyle
gtk_button_box_get_layout(GtkButtonBox * widget)507 gtk_button_box_get_layout (GtkButtonBox *widget)
508 {
509   g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), DEFAULT_LAYOUT_STYLE);
510 
511   return widget->priv->layout_style;
512 }
513 
514 /**
515  * gtk_button_box_get_child_secondary:
516  * @widget: a #GtkButtonBox
517  * @child: a child of @widget
518  *
519  * Returns whether @child should appear in a secondary group of children.
520  *
521  * Returns: whether @child should appear in a secondary group of children.
522  *
523  * Since: 2.4
524  **/
525 gboolean
gtk_button_box_get_child_secondary(GtkButtonBox * widget,GtkWidget * child)526 gtk_button_box_get_child_secondary (GtkButtonBox *widget,
527                                     GtkWidget    *child)
528 {
529   g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE);
530   g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
531 
532   return (g_object_get_data (G_OBJECT (child), GTK_BOX_SECONDARY_CHILD) != NULL);
533 }
534 
535 /**
536  * gtk_button_box_set_child_secondary:
537  * @widget: a #GtkButtonBox
538  * @child: a child of @widget
539  * @is_secondary: if %TRUE, the @child appears in a secondary group of the
540  *                button box.
541  *
542  * Sets whether @child should appear in a secondary group of children.
543  * A typical use of a secondary child is the help button in a dialog.
544  *
545  * This group appears after the other children if the style
546  * is %GTK_BUTTONBOX_START, %GTK_BUTTONBOX_SPREAD or
547  * %GTK_BUTTONBOX_EDGE, and before the other children if the style
548  * is %GTK_BUTTONBOX_END. For horizontal button boxes, the definition
549  * of before/after depends on direction of the widget (see
550  * gtk_widget_set_direction()). If the style is %GTK_BUTTONBOX_START
551  * or %GTK_BUTTONBOX_END, then the secondary children are aligned at
552  * the other end of the button box from the main children. For the
553  * other styles, they appear immediately next to the main children.
554  **/
555 void
gtk_button_box_set_child_secondary(GtkButtonBox * widget,GtkWidget * child,gboolean is_secondary)556 gtk_button_box_set_child_secondary (GtkButtonBox *widget,
557                                     GtkWidget    *child,
558                                     gboolean      is_secondary)
559 {
560   GtkButtonBox *bbox;
561 
562   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
563   g_return_if_fail (GTK_IS_WIDGET (child));
564   g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (widget));
565 
566   bbox = GTK_BUTTON_BOX (widget);
567 
568   g_object_set_data (G_OBJECT (child),
569                      GTK_BOX_SECONDARY_CHILD,
570                      is_secondary ? GINT_TO_POINTER (1) : NULL);
571   gtk_widget_child_notify (child, "secondary");
572 
573   if (bbox->priv->layout_style == GTK_BUTTONBOX_EXPAND)
574     {
575       gtk_box_reorder_child (GTK_BOX (bbox), child, is_secondary ? 0 : -1);
576     }
577 
578   if (gtk_widget_get_visible (GTK_WIDGET (widget)) &&
579       gtk_widget_get_visible (child))
580     gtk_widget_queue_resize (child);
581 }
582 
583 /* Ask children how much space they require and round up
584  * to match minimum size and internal padding.
585  * Returns the size each single child should have.
586  */
587 static void
gtk_button_box_child_requisition(GtkWidget * widget,gint * nvis_children,gint * nvis_secondaries,gint ** widths,gint ** heights,gint ** baselines,gint * baseline,gint * baseline_height)588 gtk_button_box_child_requisition (GtkWidget  *widget,
589                                   gint       *nvis_children,
590                                   gint       *nvis_secondaries,
591                                   gint      **widths,
592                                   gint      **heights,
593                                   gint      **baselines,
594 				  gint       *baseline,
595 				  gint       *baseline_height)
596 {
597   GtkButtonBox *bbox;
598   GList *children, *list;
599   gint nchildren;
600   gint nsecondaries;
601   gint needed_width;
602   gint needed_height;
603   gint needed_above, needed_below;
604   gint avg_w, avg_h;
605   GtkRequisition child_requisition;
606   gint ipad_w;
607   gint ipad_h;
608   gint child_min_width;
609   gint child_min_height;
610   gint ipad_x;
611   gint ipad_y;
612   gboolean homogeneous;
613   gint i;
614   gint max_above, max_below, child_baseline;
615   GtkOrientation orientation;
616   gboolean have_baseline;
617 
618   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
619 
620   bbox = GTK_BUTTON_BOX (widget);
621 
622   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
623   homogeneous = gtk_box_get_homogeneous (GTK_BOX (widget));
624 
625   gtk_widget_style_get (widget,
626                         "child-min-width", &child_min_width,
627                         "child-min-height", &child_min_height,
628                         "child-internal-pad-x", &ipad_x,
629                         "child-internal-pad-y", &ipad_y,
630                         NULL);
631 
632   nchildren = 0;
633   nsecondaries = 0;
634   list = children = _gtk_box_get_children (GTK_BOX (bbox));
635   needed_width = child_min_width;
636   needed_height = child_min_height;
637   needed_above = 0;
638   needed_below = 0;
639   ipad_w = ipad_x * 2;
640   ipad_h = ipad_y * 2;
641 
642   have_baseline = FALSE;
643   max_above = max_below = 0;
644   avg_w = avg_h = 0;
645   for (children = list; children != NULL; children = children->next)
646     {
647       GtkWidget *child;
648 
649       child = children->data;
650 
651       if (gtk_widget_get_visible (child))
652         {
653           nchildren += 1;
654           _gtk_widget_get_preferred_size_and_baseline (child,
655                                                        &child_requisition, NULL, &child_baseline, NULL);
656 	  if (orientation == GTK_ORIENTATION_HORIZONTAL &&
657 	      gtk_widget_get_valign_with_baseline (child) == GTK_ALIGN_BASELINE &&
658 	      child_baseline != -1)
659 	    {
660 	      have_baseline = TRUE;
661 	      max_above = MAX (max_above, child_baseline + ipad_y);
662 	      max_below = MAX (max_below , child_requisition.height + ipad_h - (child_baseline + ipad_y));
663 	    }
664           avg_w += child_requisition.width + ipad_w;
665           avg_h += child_requisition.height + ipad_h;
666         }
667     }
668   avg_w /= MAX (nchildren, 1);
669   avg_h /= MAX (nchildren, 1);
670 
671   if (baseline)
672     *baseline = have_baseline ? max_above : -1;
673   if (baseline_height)
674     *baseline_height = max_above + max_below;
675 
676   *widths = g_new (gint, nchildren);
677   *heights = g_new (gint, nchildren);
678   *baselines = g_new (gint, nchildren);
679 
680   i = 0;
681   children = list;
682   while (children)
683     {
684       GtkWidget *child;
685       gboolean is_secondary;
686       gboolean non_homogeneous;
687 
688       child = children->data;
689       children = children->next;
690 
691       if (gtk_widget_get_visible (child))
692         {
693           is_secondary = gtk_button_box_get_child_secondary (bbox, child);
694           non_homogeneous = gtk_button_box_get_child_non_homogeneous (bbox, child);
695 
696           if (is_secondary)
697             nsecondaries++;
698 
699           _gtk_widget_get_preferred_size_and_baseline (child,
700                                                        &child_requisition, NULL, &child_baseline, NULL);
701 
702           if (homogeneous ||
703               (!non_homogeneous && (child_requisition.width + ipad_w < avg_w * 1.5)))
704             {
705               (*widths)[i] = -1;
706               if (child_requisition.width + ipad_w > needed_width)
707                 needed_width = child_requisition.width + ipad_w;
708             }
709           else
710             {
711               (*widths)[i] = child_requisition.width + ipad_w;
712             }
713 
714 	  (*baselines)[i] = -1;
715 
716           if (homogeneous ||
717               (!non_homogeneous && (child_requisition.height + ipad_h < avg_h * 1.5)))
718             {
719               (*heights)[i] = -1;
720 
721 	      if (orientation == GTK_ORIENTATION_HORIZONTAL &&
722 		  gtk_widget_get_valign_with_baseline (child) == GTK_ALIGN_BASELINE &&
723 		  child_baseline != -1)
724 		{
725 		  (*baselines)[i] = child_baseline + ipad_y;
726 
727 		  if (child_baseline + ipad_y > needed_above)
728 		    needed_above = child_baseline + ipad_y;
729 		  if (child_requisition.height - child_baseline + ipad_y > needed_below)
730 		    needed_below = child_requisition.height - child_baseline + ipad_y;
731 		}
732 	      else
733 		{
734 		  if (child_requisition.height + ipad_h > needed_height)
735 		    needed_height = child_requisition.height + ipad_h;
736 		}
737             }
738           else
739             {
740               (*heights)[i] = child_requisition.height + ipad_h;
741 
742 	      if (orientation == GTK_ORIENTATION_HORIZONTAL &&
743 		  gtk_widget_get_valign_with_baseline (child) == GTK_ALIGN_BASELINE &&
744 		  child_baseline != -1)
745 		(*baselines)[i] = child_baseline + ipad_y;
746             }
747 
748           i++;
749         }
750     }
751 
752   g_list_free (list);
753 
754   needed_height = MAX (needed_height, needed_above + needed_below);
755 
756   for (i = 0; i < nchildren; i++)
757     {
758       if ((*widths)[i] == -1)
759         (*widths)[i] = needed_width;
760       if ((*heights)[i] == -1)
761 	{
762 	  (*heights)[i] = needed_height;
763 	  if ((*baselines)[i] != -1)
764 	    (*baselines)[i] = needed_above;
765 	}
766     }
767 
768   if (nvis_children)
769     *nvis_children = nchildren;
770 
771   if (nvis_secondaries)
772     *nvis_secondaries = nsecondaries;
773 }
774 
775 static void
gtk_button_box_size_request(GtkWidget * widget,GtkRequisition * requisition,gint * baseline)776 gtk_button_box_size_request (GtkWidget      *widget,
777                              GtkRequisition *requisition,
778 			     gint           *baseline)
779 {
780   GtkButtonBoxPrivate *priv;
781   GtkButtonBox *bbox;
782   gint nvis_children;
783   gint max_size, max_above, max_below;
784   gint total_size;
785   gint spacing;
786   GtkOrientation orientation;
787   gint *widths;
788   gint *heights;
789   gint *baselines;
790   gint i;
791 
792   if (baseline)
793     *baseline = -1;
794 
795   bbox = GTK_BUTTON_BOX (widget);
796   priv = bbox->priv;
797 
798   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
799   spacing = gtk_box_get_spacing (GTK_BOX (widget));
800 
801   gtk_button_box_child_requisition (widget,
802                                     &nvis_children,
803                                     NULL,
804                                     &widths, &heights, &baselines, baseline, NULL);
805 
806   max_size = max_above = max_below = 0;
807   total_size = 0;
808   for (i = 0; i < nvis_children; i++)
809     {
810       if (orientation == GTK_ORIENTATION_HORIZONTAL)
811         {
812           total_size += widths[i];
813 	  if (baselines[i] == -1)
814 	    max_size = MAX (max_size, heights[i]);
815 	  else
816 	    {
817 	      max_above = MAX (max_above, baselines[i]);
818 	      max_below = MAX (max_below, heights[i] - baselines[i]);
819 	    }
820         }
821       else
822         {
823           total_size += heights[i];
824           max_size = MAX (max_size, widths[i]);
825         }
826     }
827   g_free (widths);
828   g_free (heights);
829   g_free (baselines);
830 
831   max_size = MAX (max_size, max_above + max_below);
832 
833   switch (gtk_box_get_baseline_position (GTK_BOX (widget)))
834     {
835     case GTK_BASELINE_POSITION_TOP:
836       break;
837     case GTK_BASELINE_POSITION_CENTER:
838       if (baseline != NULL && *baseline != -1)
839 	*baseline += (max_size - (max_above + max_below)) / 2;
840       break;
841     case GTK_BASELINE_POSITION_BOTTOM:
842       if (baseline != NULL && *baseline != -1)
843 	*baseline += max_size - (max_above + max_below);
844       break;
845     }
846 
847   if (nvis_children == 0)
848     {
849       requisition->width = 0;
850       requisition->height = 0;
851     }
852   else
853     {
854       switch (priv->layout_style)
855         {
856           case GTK_BUTTONBOX_SPREAD:
857             if (orientation == GTK_ORIENTATION_HORIZONTAL)
858               requisition->width = total_size + ((nvis_children + 1)*spacing);
859             else
860               requisition->height = total_size + ((nvis_children + 1)*spacing);
861 
862             break;
863           case GTK_BUTTONBOX_EDGE:
864           case GTK_BUTTONBOX_START:
865           case GTK_BUTTONBOX_END:
866           case GTK_BUTTONBOX_CENTER:
867           case GTK_BUTTONBOX_EXPAND:
868             if (orientation == GTK_ORIENTATION_HORIZONTAL)
869               requisition->width = total_size + ((nvis_children - 1)*spacing);
870             else
871               requisition->height = total_size + ((nvis_children - 1)*spacing);
872 
873             break;
874           default:
875             g_assert_not_reached ();
876             break;
877         }
878 
879       if (orientation == GTK_ORIENTATION_HORIZONTAL)
880         requisition->height = max_size;
881       else
882         requisition->width = max_size;
883     }
884 }
885 
886 static void
gtk_button_box_measure(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline,gpointer unused)887 gtk_button_box_measure (GtkCssGadget   *gadget,
888                         GtkOrientation  orientation,
889                         int             for_size,
890                         int            *minimum,
891                         int            *natural,
892                         int            *minimum_baseline,
893                         int            *natural_baseline,
894                         gpointer        unused)
895 {
896   GtkWidget *widget;
897   GtkRequisition requisition;
898   int baseline;
899   int *pb;
900 
901   if (minimum_baseline || natural_baseline)
902     pb = &baseline;
903   else
904     pb = NULL;
905 
906   widget = gtk_css_gadget_get_owner (gadget);
907   gtk_button_box_size_request (widget, &requisition, pb);
908 
909   if (orientation == GTK_ORIENTATION_HORIZONTAL)
910     *minimum = *natural = requisition.width;
911   else
912     *minimum = *natural = requisition.height;
913 
914   if (minimum_baseline)
915     *minimum_baseline = baseline;
916   if (natural_baseline)
917     *natural_baseline = baseline;
918 }
919 
920 static void
gtk_button_box_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)921 gtk_button_box_get_preferred_width (GtkWidget *widget,
922                                     gint      *minimum,
923                                     gint      *natural)
924 {
925   GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (widget)->priv;
926   GtkCssGadget *gadget;
927 
928   if (priv->layout_style == GTK_BUTTONBOX_EXPAND)
929     gadget = gtk_box_get_gadget (GTK_BOX (widget));
930   else
931     gadget = priv->gadget;
932 
933   gtk_css_gadget_get_preferred_size (gadget,
934                                      GTK_ORIENTATION_HORIZONTAL,
935                                      -1,
936                                      minimum, natural,
937                                      NULL, NULL);
938 }
939 
940 static void
gtk_button_box_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)941 gtk_button_box_get_preferred_height (GtkWidget *widget,
942                                      gint      *minimum,
943                                      gint      *natural)
944 {
945   GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (widget)->priv;
946   GtkCssGadget *gadget;
947 
948   if (priv->layout_style == GTK_BUTTONBOX_EXPAND)
949     gadget = gtk_box_get_gadget (GTK_BOX (widget));
950   else
951     gadget = priv->gadget;
952 
953   gtk_css_gadget_get_preferred_size (gadget,
954                                      GTK_ORIENTATION_VERTICAL,
955                                      -1,
956                                      minimum, natural,
957                                      NULL, NULL);
958 }
959 
960 static void
gtk_button_box_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum,gint * natural)961 gtk_button_box_get_preferred_width_for_height (GtkWidget *widget,
962                                                gint       height,
963                                                gint      *minimum,
964                                                gint      *natural)
965 {
966   GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (widget)->priv;
967   GtkCssGadget *gadget;
968 
969   if (priv->layout_style == GTK_BUTTONBOX_EXPAND)
970     gadget = gtk_box_get_gadget (GTK_BOX (widget));
971   else
972     gadget = priv->gadget;
973 
974   gtk_css_gadget_get_preferred_size (gadget,
975                                      GTK_ORIENTATION_HORIZONTAL,
976                                      height,
977                                      minimum, natural,
978                                      NULL, NULL);
979 }
980 
981 static void
gtk_button_box_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum,gint * natural)982 gtk_button_box_get_preferred_height_for_width (GtkWidget *widget,
983                                                gint       width,
984                                                gint      *minimum,
985                                                gint      *natural)
986 {
987   GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (widget)->priv;
988   GtkCssGadget *gadget;
989 
990   if (priv->layout_style == GTK_BUTTONBOX_EXPAND)
991     gadget = gtk_box_get_gadget (GTK_BOX (widget));
992   else
993     gadget = priv->gadget;
994 
995   gtk_css_gadget_get_preferred_size (gadget,
996                                      GTK_ORIENTATION_VERTICAL,
997                                      width,
998                                      minimum, natural,
999                                      NULL, NULL);
1000 }
1001 
1002 static void
gtk_button_box_get_preferred_height_and_baseline_for_width(GtkWidget * widget,gint width,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)1003 gtk_button_box_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
1004 							    gint       width,
1005 							    gint      *minimum,
1006 							    gint      *natural,
1007 							    gint      *minimum_baseline,
1008 							    gint      *natural_baseline)
1009 {
1010   GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (widget)->priv;
1011   GtkCssGadget *gadget;
1012 
1013   if (priv->layout_style == GTK_BUTTONBOX_EXPAND)
1014     gadget = gtk_box_get_gadget (GTK_BOX (widget));
1015   else
1016     gadget = priv->gadget;
1017 
1018   gtk_css_gadget_get_preferred_size (gadget,
1019                                      GTK_ORIENTATION_VERTICAL,
1020                                      width,
1021                                      minimum, natural,
1022                                      minimum_baseline, natural_baseline);
1023 }
1024 
1025 static void
gtk_button_box_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1026 gtk_button_box_size_allocate (GtkWidget     *widget,
1027                               GtkAllocation *allocation)
1028 {
1029   GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (widget)->priv;
1030   GtkCssGadget *gadget;
1031   GdkRectangle clip;
1032 
1033   if (priv->layout_style == GTK_BUTTONBOX_EXPAND)
1034     gadget = gtk_box_get_gadget (GTK_BOX (widget));
1035   else
1036     gadget = priv->gadget;
1037 
1038   gtk_widget_set_allocation (widget, allocation);
1039 
1040   gtk_css_gadget_allocate (gadget,
1041                            allocation,
1042                            gtk_widget_get_allocated_baseline (widget),
1043                            &clip);
1044 
1045   gtk_widget_set_clip (widget, &clip);
1046 }
1047 
1048 static void
gtk_button_box_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer unused)1049 gtk_button_box_allocate (GtkCssGadget        *gadget,
1050                          const GtkAllocation *allocation,
1051                          int                  baseline,
1052                          GtkAllocation       *out_clip,
1053                          gpointer             unused)
1054 {
1055   GtkWidget *widget;
1056   GtkButtonBoxPrivate *priv;
1057   GtkButtonBox *bbox;
1058   GList *children, *list;
1059   GtkAllocation child_allocation;
1060   gint nvis_children;
1061   gint n_primaries;
1062   gint n_secondaries;
1063   gint x = 0;
1064   gint y = 0;
1065   gint secondary_x = 0;
1066   gint secondary_y = 0;
1067   gint width = 0;
1068   gint height = 0;
1069   gint childspacing = 0;
1070   gint spacing;
1071   GtkOrientation orientation;
1072   gint ipad_x, ipad_y;
1073   gint *widths;
1074   gint *heights;
1075   gint *baselines;
1076   gint *sizes;
1077   gint primary_size;
1078   gint secondary_size;
1079   gint total_size;
1080   gint baseline_height;
1081   gint child_baseline;
1082   gint i;
1083 
1084   widget = gtk_css_gadget_get_owner (gadget);
1085   bbox = GTK_BUTTON_BOX (widget);
1086   priv = bbox->priv;
1087 
1088   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
1089   spacing = gtk_box_get_spacing (GTK_BOX (widget));
1090 
1091   gtk_widget_style_get (widget,
1092                         "child-internal-pad-x", &ipad_x,
1093                         "child-internal-pad-y", &ipad_y,
1094                         NULL);
1095   gtk_button_box_child_requisition (widget,
1096                                     &nvis_children,
1097                                     &n_secondaries,
1098                                     &widths, &heights, &baselines, &baseline, &baseline_height);
1099 
1100   if (baseline != -1)
1101     {
1102       /* TODO: modify baseline based on baseline_pos && allocated_baseline*/
1103       switch (gtk_box_get_baseline_position (GTK_BOX (widget)))
1104 	{
1105 	case GTK_BASELINE_POSITION_TOP:
1106           /* keep baseline as is */
1107 	  break;
1108 	case GTK_BASELINE_POSITION_CENTER:
1109 	  baseline = baseline + (allocation->height - baseline_height) / 2;
1110 	  break;
1111 	case GTK_BASELINE_POSITION_BOTTOM:
1112 	  baseline = allocation->height - (baseline_height - baseline);
1113 	  break;
1114 	}
1115     }
1116 
1117   n_primaries = nvis_children - n_secondaries;
1118   primary_size = 0;
1119   secondary_size = 0;
1120   if (orientation == GTK_ORIENTATION_HORIZONTAL)
1121     sizes = widths;
1122   else
1123     sizes = heights;
1124 
1125   i = 0;
1126   list = children = _gtk_box_get_children (GTK_BOX (widget));
1127   while (children)
1128     {
1129       GtkWidget *child;
1130 
1131       child = children->data;
1132       children = children->next;
1133 
1134       if (gtk_widget_get_visible (child))
1135         {
1136           if (gtk_button_box_get_child_secondary (bbox, child))
1137             secondary_size += sizes[i];
1138           else
1139             primary_size += sizes[i];
1140           i++;
1141         }
1142     }
1143   total_size = primary_size + secondary_size;
1144 
1145   if (orientation == GTK_ORIENTATION_HORIZONTAL)
1146     width = allocation->width;
1147   else
1148     height = allocation->height;
1149 
1150   switch (priv->layout_style)
1151     {
1152       case GTK_BUTTONBOX_SPREAD:
1153 
1154         if (orientation == GTK_ORIENTATION_HORIZONTAL)
1155           {
1156             childspacing = (width - total_size) / (nvis_children + 1);
1157             x = allocation->x + childspacing;
1158             secondary_x = x + primary_size + n_primaries * childspacing;
1159           }
1160         else
1161           {
1162             childspacing = (height - total_size) / (nvis_children + 1);
1163             y = allocation->y + childspacing;
1164             secondary_y = y + primary_size + n_primaries * childspacing;
1165           }
1166 
1167         break;
1168 
1169       case GTK_BUTTONBOX_EDGE:
1170 
1171         if (orientation == GTK_ORIENTATION_HORIZONTAL)
1172           {
1173             if (nvis_children >= 2)
1174               {
1175                 childspacing = (width - total_size) / (nvis_children - 1);
1176                 x = allocation->x;
1177                 secondary_x = x + primary_size + n_primaries * childspacing;
1178               }
1179             else if (nvis_children == 1)
1180               {
1181                 /* one child, just center */
1182                 childspacing = width;
1183                 x = secondary_x = allocation->x
1184                                   + (allocation->width - widths[0]) / 2;
1185               }
1186             else
1187               {
1188                 /* zero children, meh */
1189                 childspacing = width;
1190                 x = secondary_x = allocation->x + allocation->width / 2;
1191               }
1192           }
1193         else
1194           {
1195             if (nvis_children >= 2)
1196               {
1197                 childspacing = (height - total_size) / (nvis_children - 1);
1198                 y = allocation->y;
1199                 secondary_y = y + primary_size + n_primaries * childspacing;
1200               }
1201             else if (nvis_children == 1)
1202               {
1203                 /* one child, just center */
1204                 childspacing = height;
1205                 y = secondary_y = allocation->y
1206                                      + (allocation->height - heights[0]) / 2;
1207               }
1208             else
1209               {
1210                 /* zero children, meh */
1211                 childspacing = height;
1212                 y = secondary_y = allocation->y + allocation->height / 2;
1213               }
1214           }
1215 
1216         break;
1217 
1218       case GTK_BUTTONBOX_START:
1219 
1220         if (orientation == GTK_ORIENTATION_HORIZONTAL)
1221           {
1222             childspacing = spacing;
1223             x = allocation->x;
1224             secondary_x = allocation->x + allocation->width
1225               - secondary_size - spacing * (n_secondaries - 1);
1226           }
1227         else
1228           {
1229             childspacing = spacing;
1230             y = allocation->y;
1231             secondary_y = allocation->y + allocation->height
1232               - secondary_size - spacing * (n_secondaries - 1);
1233           }
1234 
1235         break;
1236 
1237       case GTK_BUTTONBOX_END:
1238 
1239         if (orientation == GTK_ORIENTATION_HORIZONTAL)
1240           {
1241             childspacing = spacing;
1242             x = allocation->x + allocation->width
1243               - primary_size - spacing * (n_primaries - 1);
1244             secondary_x = allocation->x;
1245           }
1246         else
1247           {
1248             childspacing = spacing;
1249             y = allocation->y + allocation->height
1250               - primary_size - spacing * (n_primaries - 1);
1251             secondary_y = allocation->y;
1252           }
1253 
1254         break;
1255 
1256       case GTK_BUTTONBOX_CENTER:
1257 
1258         if (orientation == GTK_ORIENTATION_HORIZONTAL)
1259           {
1260             childspacing = spacing;
1261             x = allocation->x +
1262               (allocation->width
1263                - (primary_size + spacing * (n_primaries - 1))) / 2
1264               + (secondary_size + n_secondaries * spacing) / 2;
1265             secondary_x = allocation->x;
1266           }
1267         else
1268           {
1269             childspacing = spacing;
1270             y = allocation->y +
1271               (allocation->height
1272                - (primary_size + spacing * (n_primaries - 1))) / 2
1273               + (secondary_size + n_secondaries * spacing) / 2;
1274             secondary_y = allocation->y;
1275           }
1276 
1277         break;
1278 
1279       default:
1280         g_assert_not_reached ();
1281         break;
1282     }
1283 
1284   children = list;
1285   i = 0;
1286   while (children)
1287     {
1288       GtkWidget *child;
1289 
1290       child = children->data;
1291       children = children->next;
1292 
1293       if (gtk_widget_get_visible (child))
1294         {
1295           child_allocation.width = widths[i];
1296           child_allocation.height = heights[i];
1297 	  child_baseline = -1;
1298 
1299           if (orientation == GTK_ORIENTATION_HORIZONTAL)
1300             {
1301 	      if (baselines[i] != -1)
1302 		{
1303 		  child_allocation.y = allocation->y + baseline - baselines[i];
1304 		  child_baseline = baselines[i];
1305 		}
1306 	      else
1307 		child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2;
1308 
1309               if (gtk_button_box_get_child_secondary (bbox, child))
1310                 {
1311                   child_allocation.x = secondary_x;
1312                   secondary_x += child_allocation.width + childspacing;
1313                 }
1314               else
1315                 {
1316                   child_allocation.x = x;
1317                   x += child_allocation.width + childspacing;
1318                 }
1319 
1320               if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1321                   child_allocation.x = (allocation->x + allocation->width)
1322                           - (child_allocation.x + child_allocation.width - allocation->x);
1323             }
1324           else
1325             {
1326               child_allocation.x = allocation->x + (allocation->width - child_allocation.width) / 2;
1327 
1328               if (gtk_button_box_get_child_secondary (bbox, child))
1329                 {
1330                   child_allocation.y = secondary_y;
1331                   secondary_y += child_allocation.height + childspacing;
1332                 }
1333               else
1334                 {
1335                   child_allocation.y = y;
1336                   y += child_allocation.height + childspacing;
1337                 }
1338             }
1339 
1340           gtk_widget_size_allocate_with_baseline (child, &child_allocation, child_baseline);
1341           i++;
1342         }
1343     }
1344 
1345   g_list_free (list);
1346   g_free (widths);
1347   g_free (heights);
1348   g_free (baselines);
1349 
1350   gtk_container_get_children_clip (GTK_CONTAINER (widget), out_clip);
1351 }
1352 
1353 /**
1354  * gtk_button_box_new:
1355  * @orientation: the box's orientation.
1356  *
1357  * Creates a new #GtkButtonBox.
1358  *
1359  * Returns: a new #GtkButtonBox.
1360  *
1361  * Since: 3.0
1362  */
1363 GtkWidget *
gtk_button_box_new(GtkOrientation orientation)1364 gtk_button_box_new (GtkOrientation orientation)
1365 {
1366   return g_object_new (GTK_TYPE_BUTTON_BOX,
1367                        "orientation", orientation,
1368                        NULL);
1369 }
1370 
1371 /**
1372  * gtk_button_box_get_child_non_homogeneous:
1373  * @widget: a #GtkButtonBox
1374  * @child: a child of @widget
1375  *
1376  * Returns whether the child is exempted from homogenous
1377  * sizing.
1378  *
1379  * Returns: %TRUE if the child is not subject to homogenous sizing
1380  *
1381  * Since: 3.2
1382  */
1383 gboolean
gtk_button_box_get_child_non_homogeneous(GtkButtonBox * widget,GtkWidget * child)1384 gtk_button_box_get_child_non_homogeneous (GtkButtonBox *widget,
1385                                           GtkWidget    *child)
1386 {
1387   g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE);
1388   g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
1389 
1390   return (g_object_get_data (G_OBJECT (child), GTK_BOX_NON_HOMOGENEOUS) != NULL);
1391 }
1392 
1393 /**
1394  * gtk_button_box_set_child_non_homogeneous:
1395  * @widget: a #GtkButtonBox
1396  * @child: a child of @widget
1397  * @non_homogeneous: the new value
1398  *
1399  * Sets whether the child is exempted from homogeous sizing.
1400  *
1401  * Since: 3.2
1402  */
1403 void
gtk_button_box_set_child_non_homogeneous(GtkButtonBox * widget,GtkWidget * child,gboolean non_homogeneous)1404 gtk_button_box_set_child_non_homogeneous (GtkButtonBox *widget,
1405                                           GtkWidget    *child,
1406                                           gboolean      non_homogeneous)
1407 {
1408   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
1409   g_return_if_fail (GTK_IS_WIDGET (child));
1410   g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (widget));
1411 
1412   g_object_set_data (G_OBJECT (child),
1413                      GTK_BOX_NON_HOMOGENEOUS,
1414                      non_homogeneous ? GINT_TO_POINTER (1) : NULL);
1415   gtk_widget_child_notify (child, "non-homogeneous");
1416 
1417   if (gtk_widget_get_visible (GTK_WIDGET (widget)) &&
1418       gtk_widget_get_visible (child))
1419     gtk_widget_queue_resize (child);
1420 }
1421