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:gtkbox
27  * @Short_description: A container for packing widgets in a single row or column
28  * @Title: GtkBox
29  * @See_also: #GtkGrid
30  *
31  * The GtkBox widget arranges child widgets into a single row or column,
32  * depending upon the value of its #GtkOrientable:orientation property. Within
33  * the other dimension, all children are allocated the same size. Of course,
34  * the #GtkWidget:halign and #GtkWidget:valign properties can be used on
35  * the children to influence their allocation.
36  *
37  * GtkBox uses a notion of packing. Packing refers
38  * to adding widgets with reference to a particular position in a
39  * #GtkContainer. For a GtkBox, there are two reference positions: the
40  * start and the end of the box.
41  * For a vertical #GtkBox, the start is defined as the top of the box and
42  * the end is defined as the bottom. For a horizontal #GtkBox the start
43  * is defined as the left side and the end is defined as the right side.
44  *
45  * Use repeated calls to gtk_box_pack_start() to pack widgets into a
46  * GtkBox from start to end. Use gtk_box_pack_end() to add widgets from
47  * end to start. You may intersperse these calls and add widgets from
48  * both ends of the same GtkBox.
49  *
50  * Because GtkBox is a #GtkContainer, you may also use gtk_container_add()
51  * to insert widgets into the box, and they will be packed with the default
52  * values for expand and fill child properties. Use gtk_container_remove()
53  * to remove widgets from the GtkBox.
54  *
55  * Use gtk_box_set_homogeneous() to specify whether or not all children
56  * of the GtkBox are forced to get the same amount of space.
57  *
58  * Use gtk_box_set_spacing() to determine how much space will be
59  * minimally placed between all children in the GtkBox. Note that
60  * spacing is added between the children, while
61  * padding added by gtk_box_pack_start() or gtk_box_pack_end() is added
62  * on either side of the widget it belongs to.
63  *
64  * Use gtk_box_reorder_child() to move a GtkBox child to a different
65  * place in the box.
66  *
67  * Use gtk_box_set_child_packing() to reset the expand,
68  * fill and padding child properties.
69  * Use gtk_box_query_child_packing() to query these fields.
70  *
71  * # CSS nodes
72  *
73  * GtkBox uses a single CSS node with name box.
74  *
75  * In horizontal orientation, the nodes of the children are always arranged
76  * from left to right. So :first-child will always select the leftmost child,
77  * regardless of text direction.
78  */
79 
80 #include "config.h"
81 
82 #include "gtkbox.h"
83 #include "gtkboxprivate.h"
84 #include "gtkcontainerprivate.h"
85 #include "gtkcsscustomgadgetprivate.h"
86 #include "gtkcssnodeprivate.h"
87 #include "gtkintl.h"
88 #include "gtkorientable.h"
89 #include "gtkorientableprivate.h"
90 #include "gtkprivate.h"
91 #include "gtktypebuiltins.h"
92 #include "gtksizerequest.h"
93 #include "gtkwidgetpath.h"
94 #include "gtkwidgetprivate.h"
95 #include "a11y/gtkcontaineraccessible.h"
96 
97 
98 enum {
99   PROP_0,
100   PROP_SPACING,
101   PROP_HOMOGENEOUS,
102   PROP_BASELINE_POSITION,
103 
104   /* orientable */
105   PROP_ORIENTATION,
106   LAST_PROP = PROP_ORIENTATION
107 };
108 
109 enum {
110   CHILD_PROP_0,
111   CHILD_PROP_EXPAND,
112   CHILD_PROP_FILL,
113   CHILD_PROP_PADDING,
114   CHILD_PROP_PACK_TYPE,
115   CHILD_PROP_POSITION,
116   LAST_CHILD_PROP
117 };
118 
119 typedef struct _GtkBoxChild        GtkBoxChild;
120 
121 struct _GtkBoxPrivate
122 {
123   GList          *children;
124   GtkBoxChild    *center;
125   GtkCssGadget   *gadget;
126 
127   GtkOrientation  orientation;
128   gint16          spacing;
129 
130   guint           default_expand : 1;
131   guint           homogeneous    : 1;
132   guint           spacing_set    : 1;
133   guint           baseline_pos   : 2;
134 };
135 
136 static GParamSpec *props[LAST_PROP] = { NULL, };
137 static GParamSpec *child_props[LAST_CHILD_PROP] = { NULL, };
138 
139 /*
140  * GtkBoxChild:
141  * @widget: the child widget, packed into the GtkBox.
142  * @padding: the number of extra pixels to put between this child and its
143  *  neighbors, set when packed, zero by default.
144  * @expand: flag indicates whether extra space should be given to this child.
145  *  Any extra space given to the parent GtkBox is divided up among all children
146  *  with this attribute set to %TRUE; set when packed, %TRUE by default.
147  * @fill: flag indicates whether any extra space given to this child due to its
148  *  @expand attribute being set is actually allocated to the child, rather than
149  *  being used as padding around the widget; set when packed, %TRUE by default.
150  * @pack: one of #GtkPackType indicating whether the child is packed with
151  *  reference to the start (top/left) or end (bottom/right) of the GtkBox.
152  */
153 struct _GtkBoxChild
154 {
155   GtkWidget *widget;
156 
157   guint16    padding;
158 
159   guint      expand : 1;
160   guint      fill   : 1;
161   guint      pack   : 1;
162 };
163 
164 static void gtk_box_size_allocate         (GtkWidget              *widget,
165                                            GtkAllocation          *allocation);
166 static gboolean gtk_box_draw           (GtkWidget        *widget,
167                                         cairo_t          *cr);
168 
169 static void gtk_box_direction_changed  (GtkWidget        *widget,
170                                         GtkTextDirection  previous_direction);
171 
172 static void gtk_box_set_property       (GObject        *object,
173                                         guint           prop_id,
174                                         const GValue   *value,
175                                         GParamSpec     *pspec);
176 static void gtk_box_get_property       (GObject        *object,
177                                         guint           prop_id,
178                                         GValue         *value,
179                                         GParamSpec     *pspec);
180 static void gtk_box_add                (GtkContainer   *container,
181                                         GtkWidget      *widget);
182 static void gtk_box_remove             (GtkContainer   *container,
183                                         GtkWidget      *widget);
184 static void gtk_box_forall             (GtkContainer   *container,
185                                         gboolean        include_internals,
186                                         GtkCallback     callback,
187                                         gpointer        callback_data);
188 static void gtk_box_set_child_property (GtkContainer   *container,
189                                         GtkWidget      *child,
190                                         guint           property_id,
191                                         const GValue   *value,
192                                         GParamSpec     *pspec);
193 static void gtk_box_get_child_property (GtkContainer   *container,
194                                         GtkWidget      *child,
195                                         guint           property_id,
196                                         GValue         *value,
197                                         GParamSpec     *pspec);
198 static GType gtk_box_child_type        (GtkContainer   *container);
199 static GtkWidgetPath * gtk_box_get_path_for_child
200                                        (GtkContainer   *container,
201                                         GtkWidget      *child);
202 
203 
204 static void               gtk_box_get_preferred_width            (GtkWidget           *widget,
205                                                                   gint                *minimum_size,
206                                                                   gint                *natural_size);
207 static void               gtk_box_get_preferred_height           (GtkWidget           *widget,
208                                                                   gint                *minimum_size,
209                                                                   gint                *natural_size);
210 static void               gtk_box_get_preferred_width_for_height (GtkWidget           *widget,
211                                                                   gint                 height,
212                                                                   gint                *minimum_width,
213                                                                   gint                *natural_width);
214 static void               gtk_box_get_preferred_height_for_width (GtkWidget           *widget,
215                                                                   gint                 width,
216                                                                   gint                *minimum_height,
217                                                                   gint                *natural_height);
218 static void  gtk_box_get_preferred_height_and_baseline_for_width (GtkWidget           *widget,
219 								  gint                 width,
220 								  gint                *minimum_height,
221 								  gint                *natural_height,
222 								  gint                *minimum_baseline,
223 								  gint                *natural_baseline);
224 
225 static void               gtk_box_buildable_init                 (GtkBuildableIface  *iface);
226 
G_DEFINE_TYPE_WITH_CODE(GtkBox,gtk_box,GTK_TYPE_CONTAINER,G_ADD_PRIVATE (GtkBox)G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,NULL)G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,gtk_box_buildable_init))227 G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
228                          G_ADD_PRIVATE (GtkBox)
229                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)
230                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_box_buildable_init))
231 
232 static void
233 gtk_box_dispose (GObject *object)
234 {
235   GtkBox *box = GTK_BOX (object);
236   GtkBoxPrivate *priv = box->priv;
237 
238   g_clear_object (&priv->gadget);
239 
240   G_OBJECT_CLASS (gtk_box_parent_class)->dispose (object);
241 }
242 
243 static void
gtk_box_class_init(GtkBoxClass * class)244 gtk_box_class_init (GtkBoxClass *class)
245 {
246   GObjectClass *object_class = G_OBJECT_CLASS (class);
247   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
248   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
249 
250   object_class->set_property = gtk_box_set_property;
251   object_class->get_property = gtk_box_get_property;
252   object_class->dispose = gtk_box_dispose;
253 
254   widget_class->draw                           = gtk_box_draw;
255   widget_class->size_allocate                  = gtk_box_size_allocate;
256   widget_class->get_preferred_width            = gtk_box_get_preferred_width;
257   widget_class->get_preferred_height           = gtk_box_get_preferred_height;
258   widget_class->get_preferred_height_for_width = gtk_box_get_preferred_height_for_width;
259   widget_class->get_preferred_height_and_baseline_for_width = gtk_box_get_preferred_height_and_baseline_for_width;
260   widget_class->get_preferred_width_for_height = gtk_box_get_preferred_width_for_height;
261   widget_class->direction_changed              = gtk_box_direction_changed;
262 
263   container_class->add = gtk_box_add;
264   container_class->remove = gtk_box_remove;
265   container_class->forall = gtk_box_forall;
266   container_class->child_type = gtk_box_child_type;
267   container_class->set_child_property = gtk_box_set_child_property;
268   container_class->get_child_property = gtk_box_get_child_property;
269   container_class->get_path_for_child = gtk_box_get_path_for_child;
270   gtk_container_class_handle_border_width (container_class);
271 
272   g_object_class_override_property (object_class,
273                                     PROP_ORIENTATION,
274                                     "orientation");
275 
276   props[PROP_SPACING] =
277     g_param_spec_int ("spacing",
278                       P_("Spacing"),
279                       P_("The amount of space between children"),
280                       0, G_MAXINT, 0,
281                       GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
282 
283   props[PROP_HOMOGENEOUS] =
284     g_param_spec_boolean ("homogeneous",
285                           P_("Homogeneous"),
286                           P_("Whether the children should all be the same size"),
287                           FALSE,
288                           GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
289 
290   props[PROP_BASELINE_POSITION] =
291     g_param_spec_enum ("baseline-position",
292                        P_("Baseline position"),
293                        P_("The position of the baseline aligned widgets if extra space is available"),
294                        GTK_TYPE_BASELINE_POSITION,
295                        GTK_BASELINE_POSITION_CENTER,
296                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
297 
298   g_object_class_install_properties (object_class, LAST_PROP, props);
299 
300   /**
301    * GtkBox:expand:
302    *
303    * Whether the child should receive extra space when the parent grows.
304    *
305    * Note that the default value for this property is %FALSE for GtkBox,
306    * but #GtkHBox, #GtkVBox and other subclasses use the old default
307    * of %TRUE.
308    *
309    * Note: The #GtkWidget:hexpand or #GtkWidget:vexpand properties are the
310    * preferred way to influence whether the child receives extra space, by
311    * setting the child’s expand property corresponding to the box’s orientation.
312    *
313    * In contrast to #GtkWidget:hexpand, the expand child property does
314    * not cause the box to expand itself.
315    */
316   child_props[CHILD_PROP_EXPAND] =
317       g_param_spec_boolean ("expand",
318                             P_("Expand"),
319                             P_("Whether the child should receive extra space when the parent grows"),
320                             FALSE,
321                             GTK_PARAM_READWRITE);
322 
323   /**
324    * GtkBox:fill:
325    *
326    * Whether the child should fill extra space or use it as padding.
327    *
328    * Note: The #GtkWidget:halign or #GtkWidget:valign properties are the
329    * preferred way to influence whether the child fills available space, by
330    * setting the child’s align property corresponding to the box’s orientation
331    * to %GTK_ALIGN_FILL to fill, or to something else to refrain from filling.
332    */
333   child_props[CHILD_PROP_FILL] =
334       g_param_spec_boolean ("fill",
335                             P_("Fill"),
336                             P_("Whether extra space given to the child should be allocated to the child or used as padding"),
337                             TRUE,
338                             GTK_PARAM_READWRITE);
339 
340   /**
341    * GtkBox:padding:
342    *
343    * Extra space to put between the child and its neighbors, in pixels.
344    *
345    * Note: The CSS padding properties are the preferred way to add space among
346    * widgets, by setting the paddings corresponding to the box’s orientation.
347    */
348   child_props[CHILD_PROP_PADDING] =
349       g_param_spec_uint ("padding",
350                          P_("Padding"),
351                          P_("Extra space to put between the child and its neighbors, in pixels"),
352                          0, G_MAXINT,
353                          0,
354                          GTK_PARAM_READWRITE);
355 
356   child_props[CHILD_PROP_PACK_TYPE] =
357       g_param_spec_enum ("pack-type",
358                          P_("Pack type"),
359                          P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
360                          GTK_TYPE_PACK_TYPE, GTK_PACK_START,
361                          GTK_PARAM_READWRITE);
362 
363   child_props[CHILD_PROP_POSITION] =
364       g_param_spec_int ("position",
365                         P_("Position"),
366                         P_("The index of the child in the parent"),
367                         -1, G_MAXINT,
368                         0,
369                         GTK_PARAM_READWRITE);
370 
371   gtk_container_class_install_child_properties (container_class, LAST_CHILD_PROP, child_props);
372 
373   gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_FILLER);
374   gtk_widget_class_set_css_name (widget_class, "box");
375 }
376 
377 static void
gtk_box_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)378 gtk_box_set_property (GObject      *object,
379                       guint         prop_id,
380                       const GValue *value,
381                       GParamSpec   *pspec)
382 {
383   GtkBox *box = GTK_BOX (object);
384   GtkBoxPrivate *private = box->priv;
385 
386   switch (prop_id)
387     {
388     case PROP_ORIENTATION:
389       {
390         GtkOrientation orientation = g_value_get_enum (value);
391         if (private->orientation != orientation)
392           {
393             private->orientation = orientation;
394             _gtk_orientable_set_style_classes (GTK_ORIENTABLE (box));
395             gtk_widget_queue_resize (GTK_WIDGET (box));
396             g_object_notify (object, "orientation");
397           }
398       }
399       break;
400     case PROP_SPACING:
401       gtk_box_set_spacing (box, g_value_get_int (value));
402       break;
403     case PROP_BASELINE_POSITION:
404       gtk_box_set_baseline_position (box, g_value_get_enum (value));
405       break;
406     case PROP_HOMOGENEOUS:
407       gtk_box_set_homogeneous (box, g_value_get_boolean (value));
408       break;
409     default:
410       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
411       break;
412     }
413 }
414 
415 static void
gtk_box_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)416 gtk_box_get_property (GObject    *object,
417                       guint       prop_id,
418                       GValue     *value,
419                       GParamSpec *pspec)
420 {
421   GtkBox *box = GTK_BOX (object);
422   GtkBoxPrivate *private = box->priv;
423 
424   switch (prop_id)
425     {
426     case PROP_ORIENTATION:
427       g_value_set_enum (value, private->orientation);
428       break;
429     case PROP_SPACING:
430       g_value_set_int (value, private->spacing);
431       break;
432     case PROP_BASELINE_POSITION:
433       g_value_set_enum (value, private->baseline_pos);
434       break;
435     case PROP_HOMOGENEOUS:
436       g_value_set_boolean (value, private->homogeneous);
437       break;
438     default:
439       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
440       break;
441     }
442 }
443 
444 static gboolean
gtk_box_draw_contents(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer unused)445 gtk_box_draw_contents (GtkCssGadget *gadget,
446                        cairo_t      *cr,
447                        int           x,
448                        int           y,
449                        int           width,
450                        int           height,
451                        gpointer      unused)
452 {
453   GTK_WIDGET_CLASS (gtk_box_parent_class)->draw (gtk_css_gadget_get_owner (gadget), cr);
454 
455   return FALSE;
456 }
457 
458 static gboolean
gtk_box_draw(GtkWidget * widget,cairo_t * cr)459 gtk_box_draw (GtkWidget *widget,
460               cairo_t   *cr)
461 {
462   gtk_css_gadget_draw (GTK_BOX (widget)->priv->gadget, cr);
463 
464   return FALSE;
465 }
466 
467 
468 static void
count_expand_children(GtkBox * box,gint * visible_children,gint * expand_children)469 count_expand_children (GtkBox *box,
470                        gint *visible_children,
471                        gint *expand_children)
472 {
473   GtkBoxPrivate  *private = box->priv;
474   GList       *children;
475   GtkBoxChild *child;
476 
477   *visible_children = *expand_children = 0;
478 
479   for (children = private->children; children; children = children->next)
480     {
481       child = children->data;
482 
483       if (_gtk_widget_get_visible (child->widget))
484 	{
485 	  *visible_children += 1;
486 	  if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
487 	    *expand_children += 1;
488 	}
489     }
490 }
491 
492 static void
gtk_box_size_allocate_no_center(GtkWidget * widget,const GtkAllocation * allocation)493 gtk_box_size_allocate_no_center (GtkWidget           *widget,
494                                  const GtkAllocation *allocation)
495 {
496   GtkBox *box = GTK_BOX (widget);
497   GtkBoxPrivate *private = box->priv;
498   GtkBoxChild *child;
499   GList *children;
500   gint nvis_children;
501   gint nexpand_children;
502 
503   GtkTextDirection direction;
504   GtkAllocation child_allocation;
505   GtkRequestedSize *sizes;
506   gint child_minimum_baseline, child_natural_baseline;
507   gint minimum_above, natural_above;
508   gint minimum_below, natural_below;
509   gboolean have_baseline;
510   gint baseline;
511 
512   GtkPackType packing;
513 
514   gint size;
515   gint extra;
516   gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */
517   gint x = 0, y = 0, i;
518   gint child_size;
519 
520 
521   count_expand_children (box, &nvis_children, &nexpand_children);
522 
523   /* If there is no visible child, simply return. */
524   if (nvis_children <= 0)
525     return;
526 
527   direction = gtk_widget_get_direction (widget);
528   sizes = g_newa (GtkRequestedSize, nvis_children);
529   memset (sizes, 0, nvis_children * sizeof (GtkRequestedSize));
530 
531   if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
532     size = allocation->width - (nvis_children - 1) * private->spacing;
533   else
534     size = allocation->height - (nvis_children - 1) * private->spacing;
535 
536   have_baseline = FALSE;
537   minimum_above = natural_above = 0;
538   minimum_below = natural_below = 0;
539 
540   /* Retrieve desired size for visible children. */
541   for (i = 0, children = private->children; children; children = children->next)
542     {
543       child = children->data;
544 
545       if (!_gtk_widget_get_visible (child->widget))
546 	continue;
547 
548       if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
549 	gtk_widget_get_preferred_width_for_height (child->widget,
550                                                    allocation->height,
551                                                    &sizes[i].minimum_size,
552                                                    &sizes[i].natural_size);
553       else
554 	gtk_widget_get_preferred_height_and_baseline_for_width (child->widget,
555 								allocation->width,
556 								&sizes[i].minimum_size,
557 								&sizes[i].natural_size,
558 								NULL, NULL);
559 
560       /* Assert the api is working properly */
561       if (sizes[i].minimum_size < 0)
562 	g_error ("GtkBox child %s minimum %s: %d < 0 for %s %d",
563 		 gtk_widget_get_name (GTK_WIDGET (child->widget)),
564 		 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
565 		 sizes[i].minimum_size,
566 		 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
567 		 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
568 
569       if (sizes[i].natural_size < sizes[i].minimum_size)
570 	g_error ("GtkBox child %s natural %s: %d < minimum %d for %s %d",
571 		 gtk_widget_get_name (GTK_WIDGET (child->widget)),
572 		 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
573 		 sizes[i].natural_size,
574 		 sizes[i].minimum_size,
575 		 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
576 		 (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
577 
578       size -= sizes[i].minimum_size;
579       size -= child->padding * 2;
580 
581       sizes[i].data = child;
582 
583       i++;
584     }
585 
586   if (private->homogeneous)
587     {
588       /* If were homogenous we still need to run the above loop to get the
589        * minimum sizes for children that are not going to fill
590        */
591       if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
592 	size = allocation->width - (nvis_children - 1) * private->spacing;
593       else
594 	size = allocation->height - (nvis_children - 1) * private->spacing;
595 
596       extra = size / nvis_children;
597       n_extra_widgets = size % nvis_children;
598     }
599   else
600     {
601       /* Bring children up to size first */
602       size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
603 
604       /* Calculate space which hasn't distributed yet,
605        * and is available for expanding children.
606        */
607       if (nexpand_children > 0)
608 	{
609 	  extra = size / nexpand_children;
610 	  n_extra_widgets = size % nexpand_children;
611 	}
612       else
613 	extra = 0;
614     }
615 
616   /* Allocate child sizes. */
617   for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
618     {
619       for (i = 0, children = private->children;
620 	   children;
621 	   children = children->next)
622 	{
623 	  child = children->data;
624 
625 	  /* If widget is not visible, skip it. */
626 	  if (!_gtk_widget_get_visible (child->widget))
627 	    continue;
628 
629 	  /* If widget is packed differently skip it, but still increment i,
630 	   * since widget is visible and will be handled in next loop iteration.
631 	   */
632 	  if (child->pack != packing)
633 	    {
634 	      i++;
635 	      continue;
636 	    }
637 
638 	  /* Assign the child's size. */
639 	  if (private->homogeneous)
640 	    {
641 	      child_size = extra;
642 
643 	      if (n_extra_widgets > 0)
644 		{
645 		  child_size++;
646 		  n_extra_widgets--;
647 		}
648 	    }
649 	  else
650 	    {
651 	      child_size = sizes[i].minimum_size + child->padding * 2;
652 
653 	      if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
654 		{
655 		  child_size += extra;
656 
657 		  if (n_extra_widgets > 0)
658 		    {
659 		      child_size++;
660 		      n_extra_widgets--;
661 		    }
662 		}
663 	    }
664 
665 	  sizes[i].natural_size = child_size;
666 
667 	  if (private->orientation == GTK_ORIENTATION_HORIZONTAL &&
668 	      gtk_widget_get_valign_with_baseline (child->widget) == GTK_ALIGN_BASELINE)
669 	    {
670 	      int child_allocation_width;
671 	      int child_minimum_height, child_natural_height;
672 
673 	      if (child->fill)
674 		child_allocation_width = MAX (1, child_size - child->padding * 2);
675 	      else
676 		child_allocation_width = sizes[i].minimum_size;
677 
678 	      child_minimum_baseline = -1;
679 	      child_natural_baseline = -1;
680 	      gtk_widget_get_preferred_height_and_baseline_for_width (child->widget,
681 								      child_allocation_width,
682 								      &child_minimum_height, &child_natural_height,
683 								      &child_minimum_baseline, &child_natural_baseline);
684 
685 	      if (child_minimum_baseline >= 0)
686 		{
687 		  have_baseline = TRUE;
688 		  minimum_below = MAX (minimum_below, child_minimum_height - child_minimum_baseline);
689 		  natural_below = MAX (natural_below, child_natural_height - child_natural_baseline);
690 		  minimum_above = MAX (minimum_above, child_minimum_baseline);
691 		  natural_above = MAX (natural_above, child_natural_baseline);
692 		}
693 	    }
694 
695 	  i++;
696 	}
697     }
698 
699   baseline = gtk_widget_get_allocated_baseline (widget);
700   if (baseline == -1 && have_baseline)
701     {
702       gint height = MAX (1, allocation->height);
703 
704       /* TODO: This is purely based on the minimum baseline, when things fit we should
705 	 use the natural one? */
706 
707       switch (private->baseline_pos)
708 	{
709 	case GTK_BASELINE_POSITION_TOP:
710 	  baseline = minimum_above;
711 	  break;
712 	case GTK_BASELINE_POSITION_CENTER:
713 	  baseline = minimum_above + (height - (minimum_above + minimum_below)) / 2;
714 	  break;
715 	case GTK_BASELINE_POSITION_BOTTOM:
716 	  baseline = height - minimum_below;
717 	  break;
718 	}
719     }
720 
721   /* Allocate child positions. */
722   for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
723     {
724       if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
725 	{
726 	  child_allocation.y = allocation->y;
727 	  child_allocation.height = MAX (1, allocation->height);
728 	  if (packing == GTK_PACK_START)
729 	    x = allocation->x;
730 	  else
731 	    x = allocation->x + allocation->width;
732 	}
733       else
734 	{
735 	  child_allocation.x = allocation->x;
736 	  child_allocation.width = MAX (1, allocation->width);
737 	  if (packing == GTK_PACK_START)
738 	    y = allocation->y;
739 	  else
740 	    y = allocation->y + allocation->height;
741 	}
742 
743       for (i = 0, children = private->children;
744 	   children;
745 	   children = children->next)
746 	{
747 	  child = children->data;
748 
749 	  /* If widget is not visible, skip it. */
750 	  if (!_gtk_widget_get_visible (child->widget))
751 	    continue;
752 
753 	  /* If widget is packed differently skip it, but still increment i,
754 	   * since widget is visible and will be handled in next loop iteration.
755 	   */
756 	  if (child->pack != packing)
757 	    {
758 	      i++;
759 	      continue;
760 	    }
761 
762 	  child_size = sizes[i].natural_size;
763 
764 	  /* Assign the child's position. */
765 	  if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
766 	    {
767 	      if (child->fill)
768 		{
769 		  child_allocation.width = MAX (1, child_size - child->padding * 2);
770 		  child_allocation.x = x + child->padding;
771 		}
772 	      else
773 		{
774 		  child_allocation.width = sizes[i].minimum_size;
775 		  child_allocation.x = x + (child_size - child_allocation.width) / 2;
776 		}
777 
778 	      if (packing == GTK_PACK_START)
779 		{
780 		  x += child_size + private->spacing;
781 		}
782 	      else
783 		{
784 		  x -= child_size + private->spacing;
785 
786 		  child_allocation.x -= child_size;
787 		}
788 
789 	      if (direction == GTK_TEXT_DIR_RTL)
790 		child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
791 
792 	    }
793 	  else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
794 	    {
795 	      if (child->fill)
796 		{
797 		  child_allocation.height = MAX (1, child_size - child->padding * 2);
798 		  child_allocation.y = y + child->padding;
799 		}
800 	      else
801 		{
802 		  child_allocation.height = sizes[i].minimum_size;
803 		  child_allocation.y = y + (child_size - child_allocation.height) / 2;
804 		}
805 
806 	      if (packing == GTK_PACK_START)
807 		{
808 		  y += child_size + private->spacing;
809 		}
810 	      else
811 		{
812 		  y -= child_size + private->spacing;
813 
814 		  child_allocation.y -= child_size;
815 		}
816 	    }
817 	  gtk_widget_size_allocate_with_baseline (child->widget, &child_allocation, baseline);
818 
819 	  i++;
820 	}
821     }
822 
823   _gtk_widget_set_simple_clip (widget, NULL);
824 }
825 
826 static void
gtk_box_size_allocate_with_center(GtkWidget * widget,const GtkAllocation * allocation)827 gtk_box_size_allocate_with_center (GtkWidget           *widget,
828                                    const GtkAllocation *allocation)
829 {
830   GtkBox *box = GTK_BOX (widget);
831   GtkBoxPrivate *priv = box->priv;
832   GtkBoxChild *child;
833   GList *children;
834   gint nvis[2];
835   gint nexp[2];
836   GtkTextDirection direction;
837   GtkAllocation child_allocation;
838   GtkRequestedSize *sizes[2];
839   GtkRequestedSize center_req = {0, 0};
840   gint child_minimum_baseline, child_natural_baseline;
841   gint minimum_above, natural_above;
842   gint minimum_below, natural_below;
843   gboolean have_baseline;
844   gint baseline;
845   gint idx[2];
846   gint center_pos;
847   gint center_size;
848   gint box_size;
849   gint side[2];
850   GtkPackType packing;
851   gint min_size[2];
852   gint nat_size[2];
853   gint extra[2];
854   gint n_extra_widgets[2];
855   gint x = 0, y = 0, i;
856   gint child_size;
857 
858   nvis[0] = nvis[1] = 0;
859   nexp[0] = nexp[1] = 0;
860   for (children = priv->children; children; children = children->next)
861     {
862       child = children->data;
863 
864       if (child != priv->center &&
865           _gtk_widget_get_visible (child->widget))
866         {
867           nvis[child->pack] += 1;
868           if (child->expand || gtk_widget_compute_expand (child->widget, priv->orientation))
869             nexp[child->pack] += 1;
870         }
871     }
872 
873   direction = gtk_widget_get_direction (widget);
874   sizes[0] = g_newa (GtkRequestedSize, nvis[0]);
875   sizes[1] = g_newa (GtkRequestedSize, nvis[1]);
876 
877   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
878     box_size = allocation->width;
879   else
880     box_size = allocation->height;
881 
882   have_baseline = FALSE;
883   minimum_above = natural_above = 0;
884   minimum_below = natural_below = 0;
885 
886   min_size[0] = nat_size[0] = nvis[0] * priv->spacing;
887   min_size[1] = nat_size[1] = nvis[1] * priv->spacing;
888 
889   /* Retrieve desired size for visible children. */
890   idx[0] = idx[1] = 0;
891   for (children = priv->children; children; children = children->next)
892     {
893       GtkRequestedSize *req;
894 
895       child = children->data;
896 
897       if (!_gtk_widget_get_visible (child->widget))
898 	continue;
899 
900       if (child == priv->center)
901         req = &center_req;
902       else
903         req = &(sizes[child->pack][idx[child->pack]]);
904 
905       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
906         gtk_widget_get_preferred_width_for_height (child->widget,
907                                                    allocation->height,
908                                                    &req->minimum_size,
909                                                    &req->natural_size);
910       else
911         gtk_widget_get_preferred_height_and_baseline_for_width (child->widget,
912                                                                 allocation->width,
913                                                                 &req->minimum_size,
914                                                                 &req->natural_size,
915                                                                 NULL, NULL);
916 
917       if (child != priv->center)
918         {
919           min_size[child->pack] += req->minimum_size + 2 * child->padding;
920           nat_size[child->pack] += req->natural_size + 2 * child->padding;
921 
922           idx[child->pack] += 1;
923         }
924 
925       req->data = child;
926     }
927 
928   /* Determine size of center */
929   if (priv->center->expand)
930     center_size = MAX (box_size - 2 * MAX (nat_size[0], nat_size[1]), center_req.minimum_size);
931   else
932     center_size = MAX (MIN (center_req.natural_size, box_size - min_size[0] - min_size[1]), center_req.minimum_size);
933 
934   if (priv->homogeneous)
935     {
936       extra[0] = nvis[0] ? ((box_size - center_size) / 2 - nvis[0] * priv->spacing) / nvis[0] : 0;
937       extra[1] = nvis[1] ? ((box_size - center_size) / 2 - nvis[1] * priv->spacing) / nvis[1] : 0;
938       extra[0] = MIN (extra[0], extra[1]);
939       n_extra_widgets[0] = 0;
940     }
941   else
942     {
943       for (packing = GTK_PACK_START; packing <= GTK_PACK_END; packing++)
944         {
945           gint s;
946           /* Distribute the remainder naturally on each side */
947           s = MIN ((box_size - center_size) / 2 - min_size[packing], box_size - center_size - min_size[0] - min_size[1]);
948           s = gtk_distribute_natural_allocation (MAX (0, s), nvis[packing], sizes[packing]);
949 
950           /* Calculate space which hasn't distributed yet,
951            * and is available for expanding children.
952            */
953           if (nexp[packing] > 0)
954             {
955               extra[packing] = s / nexp[packing];
956               n_extra_widgets[packing] = s % nexp[packing];
957             }
958            else
959 	     extra[packing] = 0;
960         }
961     }
962 
963   /* Allocate child sizes. */
964   for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
965     {
966       for (i = 0, children = priv->children; children; children = children->next)
967         {
968           child = children->data;
969 
970           /* If widget is not visible, skip it. */
971           if (!_gtk_widget_get_visible (child->widget))
972             continue;
973 
974           /* Skip the center widget */
975           if (child == priv->center)
976             continue;
977 
978           /* If widget is packed differently, skip it. */
979           if (child->pack != packing)
980             continue;
981 
982           /* Assign the child's size. */
983           if (priv->homogeneous)
984             {
985               child_size = extra[0];
986 
987               if (n_extra_widgets[0] > 0)
988                 {
989                   child_size++;
990                   n_extra_widgets[0]--;
991                 }
992             }
993           else
994             {
995               child_size = sizes[packing][i].minimum_size + child->padding * 2;
996 
997               if (child->expand || gtk_widget_compute_expand (child->widget, priv->orientation))
998                 {
999                   child_size += extra[packing];
1000 
1001                   if (n_extra_widgets[packing] > 0)
1002                     {
1003                       child_size++;
1004                       n_extra_widgets[packing]--;
1005                     }
1006                 }
1007             }
1008 
1009           sizes[packing][i].natural_size = child_size;
1010 
1011           if (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1012               gtk_widget_get_valign_with_baseline (child->widget) == GTK_ALIGN_BASELINE)
1013             {
1014               gint child_allocation_width;
1015 	      gint child_minimum_height, child_natural_height;
1016 
1017               if (child->fill)
1018                 child_allocation_width = MAX (1, child_size - child->padding * 2);
1019               else
1020                 child_allocation_width = sizes[packing][i].minimum_size;
1021 
1022               child_minimum_baseline = -1;
1023               child_natural_baseline = -1;
1024               gtk_widget_get_preferred_height_and_baseline_for_width (child->widget,
1025                                                                       child_allocation_width,
1026                                                                       &child_minimum_height, &child_natural_height,
1027                                                                       &child_minimum_baseline, &child_natural_baseline);
1028 
1029               if (child_minimum_baseline >= 0)
1030                 {
1031                   have_baseline = TRUE;
1032                   minimum_below = MAX (minimum_below, child_minimum_height - child_minimum_baseline);
1033                   natural_below = MAX (natural_below, child_natural_height - child_natural_baseline);
1034                   minimum_above = MAX (minimum_above, child_minimum_baseline);
1035                   natural_above = MAX (natural_above, child_natural_baseline);
1036                 }
1037             }
1038 
1039           i++;
1040         }
1041     }
1042 
1043   baseline = gtk_widget_get_allocated_baseline (widget);
1044   if (baseline == -1 && have_baseline)
1045     {
1046       gint height = MAX (1, allocation->height);
1047 
1048      /* TODO: This is purely based on the minimum baseline, when things fit we should
1049       * use the natural one?
1050       */
1051       switch (priv->baseline_pos)
1052         {
1053         case GTK_BASELINE_POSITION_TOP:
1054           baseline = minimum_above;
1055           break;
1056         case GTK_BASELINE_POSITION_CENTER:
1057           baseline = minimum_above + (height - (minimum_above + minimum_below)) / 2;
1058           break;
1059         case GTK_BASELINE_POSITION_BOTTOM:
1060           baseline = height - minimum_below;
1061           break;
1062         }
1063     }
1064 
1065   /* Allocate child positions. */
1066   for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
1067     {
1068       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1069         {
1070           child_allocation.y = allocation->y;
1071           child_allocation.height = MAX (1, allocation->height);
1072           if ((packing == GTK_PACK_START && direction == GTK_TEXT_DIR_LTR) ||
1073               (packing == GTK_PACK_END && direction == GTK_TEXT_DIR_RTL))
1074             x = allocation->x;
1075           else
1076             x = allocation->x + allocation->width;
1077         }
1078       else
1079         {
1080           child_allocation.x = allocation->x;
1081           child_allocation.width = MAX (1, allocation->width);
1082           if (packing == GTK_PACK_START)
1083             y = allocation->y;
1084           else
1085             y = allocation->y + allocation->height;
1086         }
1087 
1088       for (i = 0, children = priv->children; children; children = children->next)
1089         {
1090           child = children->data;
1091 
1092           /* If widget is not visible, skip it. */
1093           if (!_gtk_widget_get_visible (child->widget))
1094             continue;
1095 
1096           /* Skip the center widget */
1097           if (child == priv->center)
1098             continue;
1099 
1100           /* If widget is packed differently, skip it. */
1101           if (child->pack != packing)
1102             continue;
1103 
1104           child_size = sizes[packing][i].natural_size;
1105 
1106           /* Assign the child's position. */
1107           if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1108             {
1109               if (child->fill)
1110                 {
1111                   child_allocation.width = MAX (1, child_size - child->padding * 2);
1112                   child_allocation.x = x + child->padding;
1113                 }
1114               else
1115                 {
1116                   child_allocation.width = sizes[packing][i].minimum_size;
1117                   child_allocation.x = x + (child_size - child_allocation.width) / 2;
1118                 }
1119 
1120               if ((packing == GTK_PACK_START && direction == GTK_TEXT_DIR_LTR) ||
1121                   (packing == GTK_PACK_END && direction == GTK_TEXT_DIR_RTL))
1122                 {
1123                   x += child_size + priv->spacing;
1124                 }
1125               else
1126                 {
1127                   x -= child_size + priv->spacing;
1128                   child_allocation.x -= child_size;
1129                 }
1130             }
1131           else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1132             {
1133               if (child->fill)
1134                 {
1135                   child_allocation.height = MAX (1, child_size - child->padding * 2);
1136                   child_allocation.y = y + child->padding;
1137                 }
1138               else
1139                 {
1140                   child_allocation.height = sizes[packing][i].minimum_size;
1141                   child_allocation.y = y + (child_size - child_allocation.height) / 2;
1142                 }
1143 
1144               if (packing == GTK_PACK_START)
1145                 {
1146                   y += child_size + priv->spacing;
1147                 }
1148               else
1149                 {
1150                   y -= child_size + priv->spacing;
1151                   child_allocation.y -= child_size;
1152                 }
1153             }
1154           gtk_widget_size_allocate_with_baseline (child->widget, &child_allocation, baseline);
1155 
1156           i++;
1157         }
1158 
1159       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1160         side[packing] = x;
1161       else
1162         side[packing] = y;
1163     }
1164 
1165   /* Allocate the center widget */
1166   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1167     center_pos = allocation->x + (box_size - center_size) / 2;
1168   else
1169     center_pos = allocation->y + (box_size - center_size) / 2;
1170 
1171   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1172       direction == GTK_TEXT_DIR_RTL)
1173     packing = GTK_PACK_END;
1174   else
1175     packing = GTK_PACK_START;
1176 
1177   if (center_pos < side[packing])
1178     center_pos = side[packing];
1179   else if (center_pos + center_size > side[1 - packing])
1180     center_pos = side[1 - packing] - center_size;
1181 
1182   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1183     {
1184       child_allocation.x = center_pos;
1185       child_allocation.width = center_size;
1186     }
1187   else
1188     {
1189       child_allocation.y = center_pos;
1190       child_allocation.height = center_size;
1191     }
1192   gtk_widget_size_allocate_with_baseline (priv->center->widget, &child_allocation, baseline);
1193 
1194   _gtk_widget_set_simple_clip (widget, NULL);
1195 }
1196 
1197 static void
gtk_box_allocate_contents(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer unused)1198 gtk_box_allocate_contents (GtkCssGadget        *gadget,
1199                            const GtkAllocation *allocation,
1200                            int                  baseline,
1201                            GtkAllocation       *out_clip,
1202                            gpointer             unused)
1203 {
1204   GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
1205   GtkBox *box = GTK_BOX (widget);
1206 
1207   if (box->priv->center &&
1208       _gtk_widget_get_visible (box->priv->center->widget))
1209     gtk_box_size_allocate_with_center (widget, allocation);
1210   else
1211     gtk_box_size_allocate_no_center (widget, allocation);
1212 
1213   gtk_container_get_children_clip (GTK_CONTAINER (box), out_clip);
1214 }
1215 
1216 static void
gtk_box_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1217 gtk_box_size_allocate (GtkWidget     *widget,
1218                        GtkAllocation *allocation)
1219 {
1220   GtkBoxPrivate *priv = GTK_BOX (widget)->priv;
1221   GtkAllocation clip;
1222 
1223   gtk_widget_set_allocation (widget, allocation);
1224 
1225   gtk_css_gadget_allocate (priv->gadget,
1226                            allocation,
1227                            gtk_widget_get_allocated_baseline (widget),
1228                            &clip);
1229 
1230   gtk_widget_set_clip (widget, &clip);
1231 }
1232 
1233 static GType
gtk_box_child_type(GtkContainer * container)1234 gtk_box_child_type (GtkContainer   *container)
1235 {
1236   return GTK_TYPE_WIDGET;
1237 }
1238 
1239 static void
gtk_box_set_child_property(GtkContainer * container,GtkWidget * child,guint property_id,const GValue * value,GParamSpec * pspec)1240 gtk_box_set_child_property (GtkContainer *container,
1241                             GtkWidget    *child,
1242                             guint         property_id,
1243                             const GValue *value,
1244                             GParamSpec   *pspec)
1245 {
1246   gboolean expand = 0;
1247   gboolean fill = 0;
1248   guint padding = 0;
1249   GtkPackType pack_type = 0;
1250 
1251   if (property_id != CHILD_PROP_POSITION)
1252     gtk_box_query_child_packing (GTK_BOX (container),
1253 				 child,
1254 				 &expand,
1255 				 &fill,
1256 				 &padding,
1257 				 &pack_type);
1258   switch (property_id)
1259     {
1260     case CHILD_PROP_EXPAND:
1261       gtk_box_set_child_packing (GTK_BOX (container),
1262 				 child,
1263 				 g_value_get_boolean (value),
1264 				 fill,
1265 				 padding,
1266 				 pack_type);
1267       break;
1268     case CHILD_PROP_FILL:
1269       gtk_box_set_child_packing (GTK_BOX (container),
1270 				 child,
1271 				 expand,
1272 				 g_value_get_boolean (value),
1273 				 padding,
1274 				 pack_type);
1275       break;
1276     case CHILD_PROP_PADDING:
1277       gtk_box_set_child_packing (GTK_BOX (container),
1278 				 child,
1279 				 expand,
1280 				 fill,
1281 				 g_value_get_uint (value),
1282 				 pack_type);
1283       break;
1284     case CHILD_PROP_PACK_TYPE:
1285       gtk_box_set_child_packing (GTK_BOX (container),
1286 				 child,
1287 				 expand,
1288 				 fill,
1289 				 padding,
1290 				 g_value_get_enum (value));
1291       break;
1292     case CHILD_PROP_POSITION:
1293       gtk_box_reorder_child (GTK_BOX (container),
1294 			     child,
1295 			     g_value_get_int (value));
1296       break;
1297     default:
1298       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
1299       break;
1300     }
1301 }
1302 
1303 static void
gtk_box_get_child_property(GtkContainer * container,GtkWidget * child,guint property_id,GValue * value,GParamSpec * pspec)1304 gtk_box_get_child_property (GtkContainer *container,
1305 			    GtkWidget    *child,
1306 			    guint         property_id,
1307 			    GValue       *value,
1308 			    GParamSpec   *pspec)
1309 {
1310   gboolean expand = FALSE;
1311   gboolean fill = FALSE;
1312   guint padding = 0;
1313   GtkPackType pack_type = 0;
1314   GList *list;
1315 
1316   if (property_id != CHILD_PROP_POSITION)
1317     gtk_box_query_child_packing (GTK_BOX (container),
1318 				 child,
1319 				 &expand,
1320 				 &fill,
1321 				 &padding,
1322 				 &pack_type);
1323   switch (property_id)
1324     {
1325       guint i;
1326     case CHILD_PROP_EXPAND:
1327       g_value_set_boolean (value, expand);
1328       break;
1329     case CHILD_PROP_FILL:
1330       g_value_set_boolean (value, fill);
1331       break;
1332     case CHILD_PROP_PADDING:
1333       g_value_set_uint (value, padding);
1334       break;
1335     case CHILD_PROP_PACK_TYPE:
1336       g_value_set_enum (value, pack_type);
1337       break;
1338     case CHILD_PROP_POSITION:
1339       i = 0;
1340       for (list = GTK_BOX (container)->priv->children; list; list = list->next)
1341 	{
1342 	  GtkBoxChild *child_entry;
1343 
1344 	  child_entry = list->data;
1345 	  if (child_entry->widget == child)
1346 	    break;
1347 	  i++;
1348 	}
1349       g_value_set_int (value, list ? i : -1);
1350       break;
1351     default:
1352       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
1353       break;
1354     }
1355 }
1356 
1357 typedef struct _CountingData CountingData;
1358 struct _CountingData {
1359   GtkWidget *widget;
1360   gboolean found;
1361   guint before;
1362   guint after;
1363 };
1364 
1365 static void
count_widget_position(GtkWidget * widget,gpointer data)1366 count_widget_position (GtkWidget *widget,
1367                        gpointer   data)
1368 {
1369   CountingData *count = data;
1370 
1371   if (!_gtk_widget_get_visible (widget))
1372     return;
1373 
1374   if (count->widget == widget)
1375     count->found = TRUE;
1376   else if (count->found)
1377     count->after++;
1378   else
1379     count->before++;
1380 }
1381 
1382 static gint
gtk_box_get_visible_position(GtkBox * box,GtkWidget * child)1383 gtk_box_get_visible_position (GtkBox    *box,
1384                               GtkWidget *child)
1385 {
1386   CountingData count = { child, FALSE, 0, 0 };
1387 
1388   /* foreach iterates in visible order */
1389   gtk_container_foreach (GTK_CONTAINER (box),
1390                          count_widget_position,
1391                          &count);
1392 
1393   /* the child wasn't found, it's likely an internal child of some
1394    * subclass, return -1 to indicate that there is no sibling relation
1395    * to the regular box children
1396    */
1397   if (!count.found)
1398     return -1;
1399 
1400   if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1401       gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
1402     return count.after;
1403   else
1404     return count.before;
1405 }
1406 
1407 static GtkWidgetPath *
gtk_box_get_path_for_child(GtkContainer * container,GtkWidget * child)1408 gtk_box_get_path_for_child (GtkContainer *container,
1409                             GtkWidget    *child)
1410 {
1411   GtkWidgetPath *path, *sibling_path;
1412   GtkBox *box;
1413   GtkBoxPrivate *private;
1414   GList *list, *children;
1415 
1416   box = GTK_BOX (container);
1417   private = box->priv;
1418 
1419   path = _gtk_widget_create_path (GTK_WIDGET (container));
1420 
1421   if (_gtk_widget_get_visible (child))
1422     {
1423       gint position;
1424 
1425       sibling_path = gtk_widget_path_new ();
1426 
1427       /* get_children works in visible order */
1428       children = gtk_container_get_children (container);
1429       if (private->orientation == GTK_ORIENTATION_HORIZONTAL &&
1430           gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
1431         children = g_list_reverse (children);
1432 
1433       for (list = children; list; list = list->next)
1434         {
1435           if (!_gtk_widget_get_visible (list->data))
1436             continue;
1437 
1438           gtk_widget_path_append_for_widget (sibling_path, list->data);
1439         }
1440 
1441       g_list_free (children);
1442 
1443       position = gtk_box_get_visible_position (box, child);
1444 
1445       if (position >= 0)
1446         gtk_widget_path_append_with_siblings (path, sibling_path, position);
1447       else
1448         gtk_widget_path_append_for_widget (path, child);
1449 
1450       gtk_widget_path_unref (sibling_path);
1451     }
1452   else
1453     gtk_widget_path_append_for_widget (path, child);
1454 
1455   return path;
1456 }
1457 
1458 static void
gtk_box_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * type)1459 gtk_box_buildable_add_child (GtkBuildable *buildable,
1460                              GtkBuilder   *builder,
1461                              GObject      *child,
1462                              const gchar  *type)
1463 {
1464   if (type && strcmp (type, "center") == 0)
1465     gtk_box_set_center_widget (GTK_BOX (buildable), GTK_WIDGET (child));
1466   else if (!type)
1467     gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
1468   else
1469     GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_BOX (buildable), type);
1470 }
1471 
1472 static void
gtk_box_buildable_init(GtkBuildableIface * iface)1473 gtk_box_buildable_init (GtkBuildableIface *iface)
1474 {
1475   iface->add_child = gtk_box_buildable_add_child;
1476 }
1477 
1478 static void
gtk_box_update_child_css_position(GtkBox * box,GtkBoxChild * child_info)1479 gtk_box_update_child_css_position (GtkBox      *box,
1480                                    GtkBoxChild *child_info)
1481 {
1482   GtkBoxPrivate *priv = box->priv;
1483   GtkBoxChild *prev;
1484   gboolean reverse;
1485   GList *l;
1486 
1487   prev = NULL;
1488   for (l = priv->children; l->data != child_info; l = l->next)
1489     {
1490       GtkBoxChild *cur = l->data;
1491 
1492       if (cur->pack == child_info->pack)
1493         prev = cur;
1494     }
1495 
1496   reverse = child_info->pack == GTK_PACK_END;
1497   if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1498       gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
1499     reverse = !reverse;
1500 
1501   if (reverse)
1502     gtk_css_node_insert_before (gtk_widget_get_css_node (GTK_WIDGET (box)),
1503                                 gtk_widget_get_css_node (child_info->widget),
1504                                 prev ? gtk_widget_get_css_node (prev->widget) : NULL);
1505   else
1506     gtk_css_node_insert_after (gtk_widget_get_css_node (GTK_WIDGET (box)),
1507                                gtk_widget_get_css_node (child_info->widget),
1508                                prev ? gtk_widget_get_css_node (prev->widget) : NULL);
1509 }
1510 
1511 static void
gtk_box_direction_changed(GtkWidget * widget,GtkTextDirection previous_direction)1512 gtk_box_direction_changed (GtkWidget        *widget,
1513                            GtkTextDirection  previous_direction)
1514 {
1515   GtkBox *box = GTK_BOX (widget);
1516 
1517   if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1518     gtk_css_node_reverse_children (gtk_widget_get_css_node (widget));
1519 }
1520 
1521 static GtkBoxChild *
gtk_box_pack(GtkBox * box,GtkWidget * child,gboolean expand,gboolean fill,guint padding,GtkPackType pack_type)1522 gtk_box_pack (GtkBox      *box,
1523               GtkWidget   *child,
1524               gboolean     expand,
1525               gboolean     fill,
1526               guint        padding,
1527               GtkPackType  pack_type)
1528 {
1529   GtkContainer *container;
1530   GtkBoxPrivate *private;
1531   GtkBoxChild *child_info;
1532 
1533   g_return_val_if_fail (GTK_IS_BOX (box), NULL);
1534   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
1535   g_return_val_if_fail (_gtk_widget_get_parent (child) == NULL, NULL);
1536 
1537   container = GTK_CONTAINER (box);
1538   private = box->priv;
1539 
1540   child_info = g_new (GtkBoxChild, 1);
1541   child_info->widget = child;
1542   child_info->padding = padding;
1543   child_info->expand = expand ? TRUE : FALSE;
1544   child_info->fill = fill ? TRUE : FALSE;
1545   child_info->pack = pack_type;
1546 
1547   private->children = g_list_append (private->children, child_info);
1548   gtk_box_update_child_css_position (box, child_info);
1549 
1550   gtk_widget_freeze_child_notify (child);
1551 
1552   gtk_widget_set_parent (child, GTK_WIDGET (box));
1553 
1554   if (expand)
1555     gtk_container_child_notify_by_pspec (container, child, child_props[CHILD_PROP_EXPAND]);
1556   if (!fill)
1557     gtk_container_child_notify_by_pspec (container, child, child_props[CHILD_PROP_FILL]);
1558   if (padding != 0)
1559     gtk_container_child_notify_by_pspec (container, child, child_props[CHILD_PROP_PADDING]);
1560   if (pack_type != GTK_PACK_START)
1561     gtk_container_child_notify_by_pspec (container, child, child_props[CHILD_PROP_PACK_TYPE]);
1562   gtk_container_child_notify_by_pspec (container, child, child_props[CHILD_PROP_POSITION]);
1563 
1564   gtk_widget_thaw_child_notify (child);
1565 
1566   return child_info;
1567 }
1568 
1569 static void
gtk_box_get_size(GtkWidget * widget,GtkOrientation orientation,gint * minimum_size,gint * natural_size,gint * minimum_baseline,gint * natural_baseline)1570 gtk_box_get_size (GtkWidget      *widget,
1571 		  GtkOrientation  orientation,
1572 		  gint           *minimum_size,
1573 		  gint           *natural_size,
1574 		  gint           *minimum_baseline,
1575 		  gint           *natural_baseline)
1576 {
1577   GtkBox *box;
1578   GtkBoxPrivate *private;
1579   GList *children;
1580   gint nvis_children;
1581   gint minimum, natural;
1582   gint minimum_above, natural_above;
1583   gint minimum_below, natural_below;
1584   gboolean have_baseline;
1585   gint min_baseline, nat_baseline;
1586   gint center_min, center_nat;
1587 
1588   box = GTK_BOX (widget);
1589   private = box->priv;
1590 
1591   have_baseline = FALSE;
1592   minimum = natural = 0;
1593   minimum_above = natural_above = 0;
1594   minimum_below = natural_below = 0;
1595   min_baseline = nat_baseline = -1;
1596 
1597   nvis_children = 0;
1598 
1599   center_min = center_nat = 0;
1600 
1601   for (children = private->children; children; children = children->next)
1602     {
1603       GtkBoxChild *child = children->data;
1604 
1605       if (_gtk_widget_get_visible (child->widget))
1606         {
1607           gint child_minimum, child_natural;
1608           gint child_minimum_baseline = -1, child_natural_baseline = -1;
1609 
1610 	  if (orientation == GTK_ORIENTATION_HORIZONTAL)
1611 	    gtk_widget_get_preferred_width (child->widget,
1612                                             &child_minimum, &child_natural);
1613 	  else
1614 	    gtk_widget_get_preferred_height_and_baseline_for_width (child->widget, -1,
1615 								    &child_minimum, &child_natural,
1616 								    &child_minimum_baseline, &child_natural_baseline);
1617 
1618           if (private->orientation == orientation)
1619 	    {
1620               if (private->homogeneous)
1621                 {
1622                   if (child == private->center)
1623                     {
1624                       center_min = child_minimum + child->padding * 2;
1625                       center_nat = child_natural + child->padding * 2;
1626                     }
1627                   else
1628                     {
1629                       gint largest;
1630 
1631                       largest = child_minimum + child->padding * 2;
1632                       minimum = MAX (minimum, largest);
1633 
1634                       largest = child_natural + child->padding * 2;
1635                       natural = MAX (natural, largest);
1636                     }
1637                 }
1638               else
1639                 {
1640                   minimum += child_minimum + child->padding * 2;
1641                   natural += child_natural + child->padding * 2;
1642                 }
1643 	    }
1644 	  else
1645 	    {
1646 	      if (child_minimum_baseline >= 0)
1647 		{
1648 		  have_baseline = TRUE;
1649 		  minimum_below = MAX (minimum_below, child_minimum - child_minimum_baseline);
1650 		  natural_below = MAX (natural_below, child_natural - child_natural_baseline);
1651 		  minimum_above = MAX (minimum_above, child_minimum_baseline);
1652 		  natural_above = MAX (natural_above, child_natural_baseline);
1653 		}
1654 	      else
1655 		{
1656 		  /* The biggest mins and naturals in the opposing orientation */
1657 		  minimum = MAX (minimum, child_minimum);
1658 		  natural = MAX (natural, child_natural);
1659 		}
1660 	    }
1661 
1662           nvis_children += 1;
1663         }
1664     }
1665 
1666   if (nvis_children > 0 && private->orientation == orientation)
1667     {
1668       if (private->homogeneous)
1669 	{
1670           if (center_min > 0)
1671             {
1672               minimum = minimum * (nvis_children - 1) + center_min;
1673               natural = natural * (nvis_children - 1) + center_nat;
1674             }
1675           else
1676             {
1677 	      minimum *= nvis_children;
1678 	      natural *= nvis_children;
1679             }
1680 	}
1681       minimum += (nvis_children - 1) * private->spacing;
1682       natural += (nvis_children - 1) * private->spacing;
1683     }
1684 
1685   minimum = MAX (minimum, minimum_below + minimum_above);
1686   natural = MAX (natural, natural_below + natural_above);
1687 
1688   if (have_baseline)
1689     {
1690       switch (private->baseline_pos)
1691 	{
1692 	case GTK_BASELINE_POSITION_TOP:
1693 	  min_baseline = minimum_above;
1694 	  nat_baseline = natural_above;
1695 	  break;
1696 	case GTK_BASELINE_POSITION_CENTER:
1697 	  min_baseline = minimum_above + (minimum - (minimum_above + minimum_below)) / 2;
1698 	  nat_baseline = natural_above + (natural - (natural_above + natural_below)) / 2;
1699 	  break;
1700 	case GTK_BASELINE_POSITION_BOTTOM:
1701 	  min_baseline = minimum - minimum_below;
1702 	  nat_baseline = natural - natural_below;
1703 	  break;
1704 	}
1705     }
1706 
1707   if (minimum_size)
1708     *minimum_size = minimum;
1709 
1710   if (natural_size)
1711     *natural_size = natural;
1712 
1713   if (minimum_baseline)
1714     *minimum_baseline = min_baseline;
1715 
1716   if (natural_baseline)
1717     *natural_baseline = nat_baseline;
1718 }
1719 
1720 static void
gtk_box_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)1721 gtk_box_get_preferred_width (GtkWidget *widget,
1722                              gint      *minimum,
1723                              gint      *natural)
1724 {
1725   gtk_css_gadget_get_preferred_size (GTK_BOX (widget)->priv->gadget,
1726                                      GTK_ORIENTATION_HORIZONTAL,
1727                                      -1,
1728                                      minimum, natural,
1729                                      NULL, NULL);
1730 }
1731 
1732 static void
gtk_box_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)1733 gtk_box_get_preferred_height (GtkWidget *widget,
1734                               gint      *minimum,
1735                               gint      *natural)
1736 {
1737   gtk_css_gadget_get_preferred_size (GTK_BOX (widget)->priv->gadget,
1738                                      GTK_ORIENTATION_VERTICAL,
1739                                      -1,
1740                                      minimum, natural,
1741                                      NULL, NULL);
1742 }
1743 
1744 static void
gtk_box_compute_size_for_opposing_orientation(GtkBox * box,gint avail_size,gint * minimum_size,gint * natural_size,gint * minimum_baseline,gint * natural_baseline)1745 gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
1746 					       gint    avail_size,
1747 					       gint   *minimum_size,
1748 					       gint   *natural_size,
1749 					       gint   *minimum_baseline,
1750 					       gint   *natural_baseline)
1751 {
1752   GtkBoxPrivate       *private = box->priv;
1753   GtkBoxChild      *child;
1754   GList            *children;
1755   gint              nvis_children;
1756   gint              nexpand_children;
1757   gint              computed_minimum = 0, computed_natural = 0;
1758   gint              computed_minimum_above = 0, computed_natural_above = 0;
1759   gint              computed_minimum_below = 0, computed_natural_below = 0;
1760   gint              computed_minimum_baseline = -1, computed_natural_baseline = -1;
1761   GtkRequestedSize *sizes;
1762   GtkPackType       packing;
1763   gint              size, extra, i;
1764   gint              child_size, child_minimum, child_natural;
1765   gint              child_minimum_baseline, child_natural_baseline;
1766   gint              n_extra_widgets = 0;
1767   gboolean          have_baseline;
1768 
1769   count_expand_children (box, &nvis_children, &nexpand_children);
1770 
1771   if (nvis_children <= 0)
1772     return;
1773 
1774   sizes = g_newa (GtkRequestedSize, nvis_children);
1775   memset (sizes, 0, nvis_children * sizeof (GtkRequestedSize));
1776   size = avail_size - (nvis_children - 1) * private->spacing;
1777 
1778   /* Retrieve desired size for visible children */
1779   for (i = 0, children = private->children; children; children = children->next)
1780     {
1781       child = children->data;
1782 
1783       if (_gtk_widget_get_visible (child->widget))
1784 	{
1785 	  if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1786 	    gtk_widget_get_preferred_width (child->widget,
1787                                             &sizes[i].minimum_size,
1788                                             &sizes[i].natural_size);
1789 	  else
1790 	    gtk_widget_get_preferred_height (child->widget,
1791                                              &sizes[i].minimum_size,
1792                                              &sizes[i].natural_size);
1793 
1794 	  /* Assert the api is working properly */
1795 	  if (sizes[i].minimum_size < 0)
1796 	    g_error ("GtkBox child %s minimum %s: %d < 0",
1797 		     gtk_widget_get_name (GTK_WIDGET (child->widget)),
1798 		     (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1799 		     sizes[i].minimum_size);
1800 
1801 	  if (sizes[i].natural_size < sizes[i].minimum_size)
1802 	    g_error ("GtkBox child %s natural %s: %d < minimum %d",
1803 		     gtk_widget_get_name (GTK_WIDGET (child->widget)),
1804 		     (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1805 		     sizes[i].natural_size,
1806 		     sizes[i].minimum_size);
1807 
1808 	  size -= sizes[i].minimum_size;
1809 	  size -= child->padding * 2;
1810 
1811 	  sizes[i].data = child;
1812 
1813 	  i += 1;
1814 	}
1815     }
1816 
1817   if (private->homogeneous)
1818     {
1819       /* If were homogenous we still need to run the above loop to get the
1820        * minimum sizes for children that are not going to fill
1821        */
1822       size = avail_size - (nvis_children - 1) * private->spacing;
1823       extra = size / nvis_children;
1824       n_extra_widgets = size % nvis_children;
1825     }
1826   else
1827     {
1828       /* Bring children up to size first */
1829       size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
1830 
1831       /* Calculate space which hasn't distributed yet,
1832        * and is available for expanding children.
1833        */
1834       if (nexpand_children > 0)
1835 	{
1836 	  extra = size / nexpand_children;
1837 	  n_extra_widgets = size % nexpand_children;
1838 	}
1839       else
1840 	extra = 0;
1841     }
1842 
1843   have_baseline = FALSE;
1844   /* Allocate child positions. */
1845   for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
1846     {
1847       for (i = 0, children = private->children;
1848 	   children;
1849 	   children = children->next)
1850 	{
1851 	  child = children->data;
1852 
1853 	  /* If widget is not visible, skip it. */
1854 	  if (!_gtk_widget_get_visible (child->widget))
1855 	    continue;
1856 
1857 	  /* If widget is packed differently skip it, but still increment i,
1858 	   * since widget is visible and will be handled in next loop iteration.
1859 	   */
1860 	  if (child->pack != packing)
1861 	    {
1862 	      i++;
1863 	      continue;
1864 	    }
1865 
1866 	  if (child->pack == packing)
1867 	    {
1868 	      /* Assign the child's size. */
1869 	      if (private->homogeneous)
1870 		{
1871 		  child_size = extra;
1872 
1873 		  if (n_extra_widgets > 0)
1874 		    {
1875 		      child_size++;
1876 		      n_extra_widgets--;
1877 		    }
1878 		}
1879 	      else
1880 		{
1881 		  child_size = sizes[i].minimum_size + child->padding * 2;
1882 
1883 		  if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
1884 		    {
1885 		      child_size += extra;
1886 
1887 		      if (n_extra_widgets > 0)
1888 			{
1889 			  child_size++;
1890 			  n_extra_widgets--;
1891 			}
1892 		    }
1893 		}
1894 
1895 	      if (child->fill)
1896 		{
1897 		  child_size = MAX (1, child_size - child->padding * 2);
1898 		}
1899 	      else
1900 		{
1901 		  child_size = sizes[i].minimum_size;
1902 		}
1903 
1904 
1905 	      child_minimum_baseline = child_natural_baseline = -1;
1906 	      /* Assign the child's position. */
1907 	      if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1908 		gtk_widget_get_preferred_height_and_baseline_for_width (child->widget, child_size,
1909 									&child_minimum, &child_natural,
1910 									&child_minimum_baseline, &child_natural_baseline);
1911 	      else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1912 		gtk_widget_get_preferred_width_for_height (child->widget,
1913                                                            child_size, &child_minimum, &child_natural);
1914 
1915 	      if (child_minimum_baseline >= 0)
1916 		{
1917 		  have_baseline = TRUE;
1918 		  computed_minimum_below = MAX (computed_minimum_below, child_minimum - child_minimum_baseline);
1919 		  computed_natural_below = MAX (computed_natural_below, child_natural - child_natural_baseline);
1920 		  computed_minimum_above = MAX (computed_minimum_above, child_minimum_baseline);
1921 		  computed_natural_above = MAX (computed_natural_above, child_natural_baseline);
1922 		}
1923 	      else
1924 		{
1925 		  computed_minimum = MAX (computed_minimum, child_minimum);
1926 		  computed_natural = MAX (computed_natural, child_natural);
1927 		}
1928 	    }
1929 	  i += 1;
1930 	}
1931     }
1932 
1933   if (have_baseline)
1934     {
1935       computed_minimum = MAX (computed_minimum, computed_minimum_below + computed_minimum_above);
1936       computed_natural = MAX (computed_natural, computed_natural_below + computed_natural_above);
1937       switch (private->baseline_pos)
1938 	{
1939 	case GTK_BASELINE_POSITION_TOP:
1940 	  computed_minimum_baseline = computed_minimum_above;
1941 	  computed_natural_baseline = computed_natural_above;
1942 	  break;
1943 	case GTK_BASELINE_POSITION_CENTER:
1944 	  computed_minimum_baseline = computed_minimum_above + MAX((computed_minimum - (computed_minimum_above + computed_minimum_below)) / 2, 0);
1945 	  computed_natural_baseline = computed_natural_above + MAX((computed_natural - (computed_natural_above + computed_natural_below)) / 2, 0);
1946 	  break;
1947 	case GTK_BASELINE_POSITION_BOTTOM:
1948 	  computed_minimum_baseline = computed_minimum - computed_minimum_below;
1949 	  computed_natural_baseline = computed_natural - computed_natural_below;
1950 	  break;
1951 	}
1952     }
1953 
1954   if (minimum_baseline)
1955     *minimum_baseline = computed_minimum_baseline;
1956   if (natural_baseline)
1957     *natural_baseline = computed_natural_baseline;
1958 
1959   if (minimum_size)
1960     *minimum_size = computed_minimum;
1961   if (natural_size)
1962     *natural_size = MAX (computed_natural, computed_natural_below + computed_natural_above);
1963 }
1964 
1965 static void
gtk_box_compute_size_for_orientation(GtkBox * box,gint avail_size,gint * minimum_size,gint * natural_size)1966 gtk_box_compute_size_for_orientation (GtkBox *box,
1967 				      gint    avail_size,
1968 				      gint   *minimum_size,
1969 				      gint   *natural_size)
1970 {
1971   GtkBoxPrivate    *private = box->priv;
1972   GList         *children;
1973   gint           nvis_children = 0;
1974   gint           required_size = 0, required_natural = 0, child_size, child_natural;
1975   gint           largest_child = 0, largest_natural = 0;
1976 
1977   for (children = private->children; children != NULL;
1978        children = children->next)
1979     {
1980       GtkBoxChild *child = children->data;
1981 
1982       if (_gtk_widget_get_visible (child->widget))
1983         {
1984 
1985           if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1986 	    gtk_widget_get_preferred_width_for_height (child->widget,
1987                                                        avail_size, &child_size, &child_natural);
1988 	  else
1989 	    gtk_widget_get_preferred_height_for_width (child->widget,
1990 						       avail_size, &child_size, &child_natural);
1991 
1992 
1993 	  child_size    += child->padding * 2;
1994 	  child_natural += child->padding * 2;
1995 
1996 	  if (child_size > largest_child)
1997 	    largest_child = child_size;
1998 
1999 	  if (child_natural > largest_natural)
2000 	    largest_natural = child_natural;
2001 
2002 	  required_size    += child_size;
2003 	  required_natural += child_natural;
2004 
2005           nvis_children += 1;
2006         }
2007     }
2008 
2009   if (nvis_children > 0)
2010     {
2011       if (private->homogeneous)
2012 	{
2013 	  required_size    = largest_child   * nvis_children;
2014 	  required_natural = largest_natural * nvis_children;
2015 	}
2016 
2017       required_size     += (nvis_children - 1) * private->spacing;
2018       required_natural  += (nvis_children - 1) * private->spacing;
2019     }
2020 
2021   if (minimum_size)
2022     *minimum_size = required_size;
2023 
2024   if (natural_size)
2025     *natural_size = required_natural;
2026 }
2027 
2028 static void
gtk_box_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum,gint * natural)2029 gtk_box_get_preferred_width_for_height (GtkWidget *widget,
2030                                         gint       height,
2031                                         gint      *minimum,
2032                                         gint      *natural)
2033 {
2034   gtk_css_gadget_get_preferred_size (GTK_BOX (widget)->priv->gadget,
2035                                      GTK_ORIENTATION_HORIZONTAL,
2036                                      height,
2037                                      minimum, natural,
2038                                      NULL, NULL);
2039 }
2040 
2041 static void
gtk_box_get_preferred_height_and_baseline_for_width(GtkWidget * widget,gint width,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)2042 gtk_box_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
2043 						     gint       width,
2044 						     gint      *minimum,
2045 						     gint      *natural,
2046 						     gint      *minimum_baseline,
2047 						     gint      *natural_baseline)
2048 {
2049   gtk_css_gadget_get_preferred_size (GTK_BOX (widget)->priv->gadget,
2050                                      GTK_ORIENTATION_VERTICAL,
2051                                      width,
2052                                      minimum, natural,
2053                                      minimum_baseline, natural_baseline);
2054 }
2055 
2056 static void
gtk_box_get_content_size(GtkCssGadget * gadget,GtkOrientation orientation,gint for_size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline,gpointer unused)2057 gtk_box_get_content_size (GtkCssGadget   *gadget,
2058                           GtkOrientation  orientation,
2059                           gint            for_size,
2060                           gint           *minimum,
2061                           gint           *natural,
2062                           gint           *minimum_baseline,
2063                           gint           *natural_baseline,
2064                           gpointer        unused)
2065 {
2066   GtkWidget     *widget  = gtk_css_gadget_get_owner (gadget);
2067   GtkBox        *box     = GTK_BOX (widget);
2068   GtkBoxPrivate *private = box->priv;
2069 
2070   if (for_size < 0)
2071     gtk_box_get_size (widget, orientation, minimum, natural, minimum_baseline, natural_baseline);
2072   else
2073     {
2074       if (private->orientation != orientation)
2075 	gtk_box_compute_size_for_opposing_orientation (box, for_size, minimum, natural, minimum_baseline, natural_baseline);
2076       else
2077 	{
2078 	  if (minimum_baseline)
2079 	    *minimum_baseline = -1;
2080 	  if (natural_baseline)
2081 	    *natural_baseline = -1;
2082 	  gtk_box_compute_size_for_orientation (box, for_size, minimum, natural);
2083 	}
2084     }
2085 }
2086 
2087 static void
gtk_box_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum_height,gint * natural_height)2088 gtk_box_get_preferred_height_for_width (GtkWidget *widget,
2089                                         gint       width,
2090                                         gint      *minimum_height,
2091                                         gint      *natural_height)
2092 {
2093   gtk_box_get_preferred_height_and_baseline_for_width (widget, width, minimum_height, natural_height, NULL, NULL);
2094 }
2095 
2096 static void
gtk_box_init(GtkBox * box)2097 gtk_box_init (GtkBox *box)
2098 {
2099   GtkBoxPrivate *private;
2100 
2101   box->priv = gtk_box_get_instance_private (box);
2102   private = box->priv;
2103 
2104   gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
2105 
2106   private->orientation = GTK_ORIENTATION_HORIZONTAL;
2107   private->children = NULL;
2108 
2109   private->default_expand = FALSE;
2110   private->homogeneous = FALSE;
2111   private->spacing = 0;
2112   private->spacing_set = FALSE;
2113   private->baseline_pos = GTK_BASELINE_POSITION_CENTER;
2114 
2115   private->gadget = gtk_css_custom_gadget_new_for_node (gtk_widget_get_css_node (GTK_WIDGET (box)),
2116                                                         GTK_WIDGET (box),
2117                                                         gtk_box_get_content_size,
2118                                                         gtk_box_allocate_contents,
2119                                                         gtk_box_draw_contents,
2120                                                         NULL,
2121                                                         NULL);
2122 
2123   _gtk_orientable_set_style_classes (GTK_ORIENTABLE (box));
2124 }
2125 
2126 GtkCssGadget *
gtk_box_get_gadget(GtkBox * box)2127 gtk_box_get_gadget (GtkBox *box)
2128 {
2129   return box->priv->gadget;
2130 }
2131 
2132 /**
2133  * gtk_box_new:
2134  * @orientation: the box’s orientation.
2135  * @spacing: the number of pixels to place by default between children.
2136  *
2137  * Creates a new #GtkBox.
2138  *
2139  * Returns: a new #GtkBox.
2140  *
2141  * Since: 3.0
2142  **/
2143 GtkWidget*
gtk_box_new(GtkOrientation orientation,gint spacing)2144 gtk_box_new (GtkOrientation orientation,
2145              gint           spacing)
2146 {
2147   return g_object_new (GTK_TYPE_BOX,
2148                        "orientation", orientation,
2149                        "spacing",     spacing,
2150                        NULL);
2151 }
2152 
2153 /**
2154  * gtk_box_pack_start:
2155  * @box: a #GtkBox
2156  * @child: the #GtkWidget to be added to @box
2157  * @expand: %TRUE if the new child is to be given extra space allocated
2158  *     to @box. The extra space will be divided evenly between all children
2159  *     that use this option
2160  * @fill: %TRUE if space given to @child by the @expand option is
2161  *     actually allocated to @child, rather than just padding it.  This
2162  *     parameter has no effect if @expand is set to %FALSE.  A child is
2163  *     always allocated the full height of a horizontal #GtkBox and the full width
2164  *     of a vertical #GtkBox. This option affects the other dimension
2165  * @padding: extra space in pixels to put between this child and its
2166  *   neighbors, over and above the global amount specified by
2167  *   #GtkBox:spacing property.  If @child is a widget at one of the
2168  *   reference ends of @box, then @padding pixels are also put between
2169  *   @child and the reference edge of @box
2170  *
2171  * Adds @child to @box, packed with reference to the start of @box.
2172  * The @child is packed after any other child packed with reference
2173  * to the start of @box.
2174  */
2175 void
gtk_box_pack_start(GtkBox * box,GtkWidget * child,gboolean expand,gboolean fill,guint padding)2176 gtk_box_pack_start (GtkBox    *box,
2177 		    GtkWidget *child,
2178 		    gboolean   expand,
2179 		    gboolean   fill,
2180 		    guint      padding)
2181 {
2182   gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_START);
2183 }
2184 
2185 /**
2186  * gtk_box_pack_end:
2187  * @box: a #GtkBox
2188  * @child: the #GtkWidget to be added to @box
2189  * @expand: %TRUE if the new child is to be given extra space allocated
2190  *   to @box. The extra space will be divided evenly between all children
2191  *   of @box that use this option
2192  * @fill: %TRUE if space given to @child by the @expand option is
2193  *   actually allocated to @child, rather than just padding it.  This
2194  *   parameter has no effect if @expand is set to %FALSE.  A child is
2195  *   always allocated the full height of a horizontal #GtkBox and the full width
2196  *   of a vertical #GtkBox.  This option affects the other dimension
2197  * @padding: extra space in pixels to put between this child and its
2198  *   neighbors, over and above the global amount specified by
2199  *   #GtkBox:spacing property.  If @child is a widget at one of the
2200  *   reference ends of @box, then @padding pixels are also put between
2201  *   @child and the reference edge of @box
2202  *
2203  * Adds @child to @box, packed with reference to the end of @box.
2204  * The @child is packed after (away from end of) any other child
2205  * packed with reference to the end of @box.
2206  */
2207 void
gtk_box_pack_end(GtkBox * box,GtkWidget * child,gboolean expand,gboolean fill,guint padding)2208 gtk_box_pack_end (GtkBox    *box,
2209 		  GtkWidget *child,
2210 		  gboolean   expand,
2211 		  gboolean   fill,
2212 		  guint      padding)
2213 {
2214   gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_END);
2215 }
2216 
2217 /**
2218  * gtk_box_set_homogeneous:
2219  * @box: a #GtkBox
2220  * @homogeneous: a boolean value, %TRUE to create equal allotments,
2221  *   %FALSE for variable allotments
2222  *
2223  * Sets the #GtkBox:homogeneous property of @box, controlling
2224  * whether or not all children of @box are given equal space
2225  * in the box.
2226  */
2227 void
gtk_box_set_homogeneous(GtkBox * box,gboolean homogeneous)2228 gtk_box_set_homogeneous (GtkBox  *box,
2229 			 gboolean homogeneous)
2230 {
2231   GtkBoxPrivate *private;
2232 
2233   g_return_if_fail (GTK_IS_BOX (box));
2234 
2235   private = box->priv;
2236 
2237   homogeneous = homogeneous != FALSE;
2238 
2239   if (private->homogeneous != homogeneous)
2240     {
2241       private->homogeneous = homogeneous;
2242       g_object_notify_by_pspec (G_OBJECT (box), props[PROP_HOMOGENEOUS]);
2243       gtk_widget_queue_resize (GTK_WIDGET (box));
2244     }
2245 }
2246 
2247 /**
2248  * gtk_box_get_homogeneous:
2249  * @box: a #GtkBox
2250  *
2251  * Returns whether the box is homogeneous (all children are the
2252  * same size). See gtk_box_set_homogeneous().
2253  *
2254  * Returns: %TRUE if the box is homogeneous.
2255  **/
2256 gboolean
gtk_box_get_homogeneous(GtkBox * box)2257 gtk_box_get_homogeneous (GtkBox *box)
2258 {
2259   g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
2260 
2261   return box->priv->homogeneous;
2262 }
2263 
2264 /**
2265  * gtk_box_set_spacing:
2266  * @box: a #GtkBox
2267  * @spacing: the number of pixels to put between children
2268  *
2269  * Sets the #GtkBox:spacing property of @box, which is the
2270  * number of pixels to place between children of @box.
2271  */
2272 void
gtk_box_set_spacing(GtkBox * box,gint spacing)2273 gtk_box_set_spacing (GtkBox *box,
2274 		     gint    spacing)
2275 {
2276   GtkBoxPrivate *private;
2277 
2278   g_return_if_fail (GTK_IS_BOX (box));
2279 
2280   private = box->priv;
2281 
2282   if (private->spacing != spacing)
2283     {
2284       private->spacing = spacing;
2285       _gtk_box_set_spacing_set (box, TRUE);
2286 
2287       g_object_notify_by_pspec (G_OBJECT (box), props[PROP_SPACING]);
2288 
2289       gtk_widget_queue_resize (GTK_WIDGET (box));
2290     }
2291 }
2292 
2293 /**
2294  * gtk_box_get_spacing:
2295  * @box: a #GtkBox
2296  *
2297  * Gets the value set by gtk_box_set_spacing().
2298  *
2299  * Returns: spacing between children
2300  **/
2301 gint
gtk_box_get_spacing(GtkBox * box)2302 gtk_box_get_spacing (GtkBox *box)
2303 {
2304   g_return_val_if_fail (GTK_IS_BOX (box), 0);
2305 
2306   return box->priv->spacing;
2307 }
2308 
2309 /**
2310  * gtk_box_set_baseline_position:
2311  * @box: a #GtkBox
2312  * @position: a #GtkBaselinePosition
2313  *
2314  * Sets the baseline position of a box. This affects
2315  * only horizontal boxes with at least one baseline aligned
2316  * child. If there is more vertical space available than requested,
2317  * and the baseline is not allocated by the parent then
2318  * @position is used to allocate the baseline wrt the
2319  * extra space available.
2320  *
2321  * Since: 3.10
2322  */
2323 void
gtk_box_set_baseline_position(GtkBox * box,GtkBaselinePosition position)2324 gtk_box_set_baseline_position (GtkBox             *box,
2325 			       GtkBaselinePosition position)
2326 {
2327   GtkBoxPrivate *private;
2328 
2329   g_return_if_fail (GTK_IS_BOX (box));
2330 
2331   private = box->priv;
2332 
2333   if (private->baseline_pos != position)
2334     {
2335       private->baseline_pos = position;
2336 
2337       g_object_notify_by_pspec (G_OBJECT (box), props[PROP_BASELINE_POSITION]);
2338 
2339       gtk_widget_queue_resize (GTK_WIDGET (box));
2340     }
2341 }
2342 
2343 /**
2344  * gtk_box_get_baseline_position:
2345  * @box: a #GtkBox
2346  *
2347  * Gets the value set by gtk_box_set_baseline_position().
2348  *
2349  * Returns: the baseline position
2350  *
2351  * Since: 3.10
2352  **/
2353 GtkBaselinePosition
gtk_box_get_baseline_position(GtkBox * box)2354 gtk_box_get_baseline_position (GtkBox *box)
2355 {
2356   g_return_val_if_fail (GTK_IS_BOX (box), GTK_BASELINE_POSITION_CENTER);
2357 
2358   return box->priv->baseline_pos;
2359 }
2360 
2361 
2362 void
_gtk_box_set_spacing_set(GtkBox * box,gboolean spacing_set)2363 _gtk_box_set_spacing_set (GtkBox  *box,
2364                           gboolean spacing_set)
2365 {
2366   GtkBoxPrivate *private;
2367 
2368   g_return_if_fail (GTK_IS_BOX (box));
2369 
2370   private = box->priv;
2371 
2372   private->spacing_set = spacing_set ? TRUE : FALSE;
2373 }
2374 
2375 gboolean
_gtk_box_get_spacing_set(GtkBox * box)2376 _gtk_box_get_spacing_set (GtkBox *box)
2377 {
2378   GtkBoxPrivate *private;
2379 
2380   g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
2381 
2382   private = box->priv;
2383 
2384   return private->spacing_set;
2385 }
2386 
2387 /**
2388  * gtk_box_reorder_child:
2389  * @box: a #GtkBox
2390  * @child: the #GtkWidget to move
2391  * @position: the new position for @child in the list of children
2392  *   of @box, starting from 0. If negative, indicates the end of
2393  *   the list
2394  *
2395  * Moves @child to a new @position in the list of @box children.
2396  * The list contains widgets packed #GTK_PACK_START
2397  * as well as widgets packed #GTK_PACK_END, in the order that these
2398  * widgets were added to @box.
2399  *
2400  * A widget’s position in the @box children list determines where
2401  * the widget is packed into @box.  A child widget at some position
2402  * in the list will be packed just after all other widgets of the
2403  * same packing type that appear earlier in the list.
2404  */
2405 void
gtk_box_reorder_child(GtkBox * box,GtkWidget * child,gint position)2406 gtk_box_reorder_child (GtkBox    *box,
2407 		       GtkWidget *child,
2408 		       gint       position)
2409 {
2410   GtkBoxPrivate *priv;
2411   GList *old_link;
2412   GList *new_link;
2413   GtkBoxChild *child_info = NULL;
2414   gint old_position;
2415 
2416   g_return_if_fail (GTK_IS_BOX (box));
2417   g_return_if_fail (GTK_IS_WIDGET (child));
2418 
2419   priv = box->priv;
2420 
2421   old_link = priv->children;
2422   old_position = 0;
2423   while (old_link)
2424     {
2425       child_info = old_link->data;
2426       if (child_info->widget == child)
2427 	break;
2428 
2429       old_link = old_link->next;
2430       old_position++;
2431     }
2432 
2433   g_return_if_fail (old_link != NULL);
2434 
2435   if (position == old_position)
2436     return;
2437 
2438   priv->children = g_list_delete_link (priv->children, old_link);
2439 
2440   if (position < 0)
2441     new_link = NULL;
2442   else
2443     new_link = g_list_nth (priv->children, position);
2444 
2445   priv->children = g_list_insert_before (priv->children, new_link, child_info);
2446   gtk_box_update_child_css_position (box, child_info);
2447 
2448   gtk_container_child_notify_by_pspec (GTK_CONTAINER (box), child, child_props[CHILD_PROP_POSITION]);
2449   if (_gtk_widget_get_visible (child) &&
2450       _gtk_widget_get_visible (GTK_WIDGET (box)))
2451     {
2452       gtk_widget_queue_resize (child);
2453     }
2454 }
2455 
2456 /**
2457  * gtk_box_query_child_packing:
2458  * @box: a #GtkBox
2459  * @child: the #GtkWidget of the child to query
2460  * @expand: (out): pointer to return location for expand child
2461  *     property
2462  * @fill: (out): pointer to return location for fill child
2463  *     property
2464  * @padding: (out): pointer to return location for padding
2465  *     child property
2466  * @pack_type: (out): pointer to return location for pack-type
2467  *     child property
2468  *
2469  * Obtains information about how @child is packed into @box.
2470  */
2471 void
gtk_box_query_child_packing(GtkBox * box,GtkWidget * child,gboolean * expand,gboolean * fill,guint * padding,GtkPackType * pack_type)2472 gtk_box_query_child_packing (GtkBox      *box,
2473 			     GtkWidget   *child,
2474 			     gboolean    *expand,
2475 			     gboolean    *fill,
2476 			     guint       *padding,
2477 			     GtkPackType *pack_type)
2478 {
2479   GtkBoxPrivate *private;
2480   GList *list;
2481   GtkBoxChild *child_info = NULL;
2482 
2483   g_return_if_fail (GTK_IS_BOX (box));
2484   g_return_if_fail (GTK_IS_WIDGET (child));
2485 
2486   private = box->priv;
2487 
2488   list = private->children;
2489   while (list)
2490     {
2491       child_info = list->data;
2492       if (child_info->widget == child)
2493 	break;
2494 
2495       list = list->next;
2496     }
2497 
2498   if (list)
2499     {
2500       if (expand)
2501 	*expand = child_info->expand;
2502       if (fill)
2503 	*fill = child_info->fill;
2504       if (padding)
2505 	*padding = child_info->padding;
2506       if (pack_type)
2507 	*pack_type = child_info->pack;
2508     }
2509 }
2510 
2511 /**
2512  * gtk_box_set_child_packing:
2513  * @box: a #GtkBox
2514  * @child: the #GtkWidget of the child to set
2515  * @expand: the new value of the expand child property
2516  * @fill: the new value of the fill child property
2517  * @padding: the new value of the padding child property
2518  * @pack_type: the new value of the pack-type child property
2519  *
2520  * Sets the way @child is packed into @box.
2521  */
2522 void
gtk_box_set_child_packing(GtkBox * box,GtkWidget * child,gboolean expand,gboolean fill,guint padding,GtkPackType pack_type)2523 gtk_box_set_child_packing (GtkBox      *box,
2524 			   GtkWidget   *child,
2525 			   gboolean     expand,
2526 			   gboolean     fill,
2527 			   guint        padding,
2528 			   GtkPackType  pack_type)
2529 {
2530   GtkBoxPrivate *private;
2531   GList *list;
2532   GtkBoxChild *child_info = NULL;
2533 
2534   g_return_if_fail (GTK_IS_BOX (box));
2535   g_return_if_fail (GTK_IS_WIDGET (child));
2536 
2537   private = box->priv;
2538 
2539   list = private->children;
2540   while (list)
2541     {
2542       child_info = list->data;
2543       if (child_info->widget == child)
2544 	break;
2545 
2546       list = list->next;
2547     }
2548 
2549   gtk_widget_freeze_child_notify (child);
2550   if (list)
2551     {
2552       expand = expand != FALSE;
2553 
2554       if (child_info->expand != expand)
2555         {
2556           child_info->expand = expand;
2557           gtk_container_child_notify_by_pspec (GTK_CONTAINER (box), child, child_props[CHILD_PROP_EXPAND]);
2558         }
2559 
2560       fill = fill != FALSE;
2561 
2562       if (child_info->fill != fill)
2563         {
2564           child_info->fill = fill;
2565           gtk_container_child_notify_by_pspec (GTK_CONTAINER (box), child, child_props[CHILD_PROP_FILL]);
2566         }
2567 
2568       if (child_info->padding != padding)
2569         {
2570           child_info->padding = padding;
2571           gtk_container_child_notify_by_pspec (GTK_CONTAINER (box), child, child_props[CHILD_PROP_PADDING]);
2572         }
2573 
2574       if (pack_type != GTK_PACK_END)
2575         pack_type = GTK_PACK_START;
2576       if (child_info->pack != pack_type)
2577         {
2578 	  child_info->pack = pack_type;
2579           gtk_box_update_child_css_position (box, child_info);
2580           gtk_container_child_notify_by_pspec (GTK_CONTAINER (box), child, child_props[CHILD_PROP_PACK_TYPE]);
2581         }
2582 
2583       if (_gtk_widget_get_visible (child) &&
2584           _gtk_widget_get_visible (GTK_WIDGET (box)))
2585 	gtk_widget_queue_resize (child);
2586     }
2587   gtk_widget_thaw_child_notify (child);
2588 }
2589 
2590 void
_gtk_box_set_old_defaults(GtkBox * box)2591 _gtk_box_set_old_defaults (GtkBox *box)
2592 {
2593   GtkBoxPrivate *private;
2594 
2595   g_return_if_fail (GTK_IS_BOX (box));
2596 
2597   private = box->priv;
2598 
2599   private->default_expand = TRUE;
2600 }
2601 
2602 static void
gtk_box_add(GtkContainer * container,GtkWidget * widget)2603 gtk_box_add (GtkContainer *container,
2604 	     GtkWidget    *widget)
2605 {
2606   GtkBoxPrivate *priv = GTK_BOX (container)->priv;
2607 
2608   gtk_box_pack_start (GTK_BOX (container), widget,
2609                       priv->default_expand,
2610                       TRUE,
2611                       0);
2612 }
2613 
2614 static void
gtk_box_remove(GtkContainer * container,GtkWidget * widget)2615 gtk_box_remove (GtkContainer *container,
2616 		GtkWidget    *widget)
2617 {
2618   GtkBox *box = GTK_BOX (container);
2619   GtkBoxPrivate *priv = box->priv;
2620   GtkBoxChild *child;
2621   GList *children;
2622 
2623   children = priv->children;
2624   while (children)
2625     {
2626       child = children->data;
2627 
2628       if (child->widget == widget)
2629 	{
2630 	  gboolean was_visible;
2631 
2632           if (priv->center == child)
2633             priv->center = NULL;
2634 
2635 	  was_visible = _gtk_widget_get_visible (widget);
2636 	  gtk_widget_unparent (widget);
2637 
2638 	  priv->children = g_list_remove_link (priv->children, children);
2639 	  g_list_free (children);
2640 	  g_free (child);
2641 
2642 	  /* queue resize regardless of gtk_widget_get_visible (container),
2643 	   * since that's what is needed by toplevels.
2644 	   */
2645 	  if (was_visible)
2646             {
2647 	      gtk_widget_queue_resize (GTK_WIDGET (container));
2648             }
2649 
2650 	  break;
2651 	}
2652 
2653       children = children->next;
2654     }
2655 }
2656 
2657 static void
gtk_box_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)2658 gtk_box_forall (GtkContainer *container,
2659 		gboolean      include_internals,
2660 		GtkCallback   callback,
2661 		gpointer      callback_data)
2662 {
2663   GtkBox *box = GTK_BOX (container);
2664   GtkBoxPrivate *priv = box->priv;
2665   GtkBoxChild *child;
2666   GList *children;
2667 
2668   children = priv->children;
2669   while (children)
2670     {
2671       child = children->data;
2672       children = children->next;
2673 
2674       if (child == priv->center)
2675         continue;
2676 
2677       if (child->pack == GTK_PACK_START)
2678 	(* callback) (child->widget, callback_data);
2679     }
2680 
2681   if (priv->center)
2682     (* callback) (priv->center->widget, callback_data);
2683 
2684   children = g_list_last (priv->children);
2685   while (children)
2686     {
2687       child = children->data;
2688       children = children->prev;
2689 
2690       if (child == priv->center)
2691         continue;
2692 
2693       if (child->pack == GTK_PACK_END)
2694 	(* callback) (child->widget, callback_data);
2695     }
2696 }
2697 
2698 GList *
_gtk_box_get_children(GtkBox * box)2699 _gtk_box_get_children (GtkBox *box)
2700 {
2701   GtkBoxPrivate *priv;
2702   GtkBoxChild *child;
2703   GList *children;
2704   GList *retval = NULL;
2705 
2706   g_return_val_if_fail (GTK_IS_BOX (box), NULL);
2707 
2708   priv = box->priv;
2709 
2710   children = priv->children;
2711   while (children)
2712     {
2713       child = children->data;
2714       children = children->next;
2715 
2716       retval = g_list_prepend (retval, child->widget);
2717     }
2718 
2719   return g_list_reverse (retval);
2720 }
2721 
2722 /**
2723  * gtk_box_set_center_widget:
2724  * @box: a #GtkBox
2725  * @widget: (allow-none): the widget to center
2726  *
2727  * Sets a center widget; that is a child widget that will be
2728  * centered with respect to the full width of the box, even
2729  * if the children at either side take up different amounts
2730  * of space.
2731  *
2732  * Since: 3.12
2733  */
2734 void
gtk_box_set_center_widget(GtkBox * box,GtkWidget * widget)2735 gtk_box_set_center_widget (GtkBox    *box,
2736                            GtkWidget *widget)
2737 {
2738   GtkBoxPrivate *priv = box->priv;
2739   GtkWidget *old_center = NULL;
2740 
2741   g_return_if_fail (GTK_IS_BOX (box));
2742 
2743   if (priv->center)
2744     {
2745       old_center = g_object_ref (priv->center->widget);
2746       gtk_box_remove (GTK_CONTAINER (box), priv->center->widget);
2747       priv->center = NULL;
2748     }
2749 
2750   if (widget)
2751     priv->center = gtk_box_pack (box, widget, FALSE, TRUE, 0, GTK_PACK_START);
2752 
2753   if (old_center)
2754     g_object_unref (old_center);
2755 }
2756 
2757 /**
2758  * gtk_box_get_center_widget:
2759  * @box: a #GtkBox
2760  *
2761  * Retrieves the center widget of the box.
2762  *
2763  * Returns: (transfer none) (nullable): the center widget
2764  *   or %NULL in case no center widget is set.
2765  *
2766  * Since: 3.12
2767  */
2768 GtkWidget *
gtk_box_get_center_widget(GtkBox * box)2769 gtk_box_get_center_widget (GtkBox *box)
2770 {
2771   GtkBoxPrivate *priv = box->priv;
2772 
2773   g_return_val_if_fail (GTK_IS_BOX (box), NULL);
2774 
2775   if (priv->center)
2776     return priv->center->widget;
2777 
2778   return NULL;
2779 }
2780