]> Pileus Git - ~andy/gtk/blob - gtk/gtkbox.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkbox.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, 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 box
28  * @Title: GtkBox
29  * @See_also: #GtkFrame, #GtkGrid, #GtkLayout
30  *
31  * The GtkBox widget organizes child widgets into a rectangular area.
32  *
33  * The rectangular area of a GtkBox is organized into either a single row
34  * or a single column of child widgets depending upon the orientation.
35  * Thus, all children of a GtkBox are allocated one dimension in common,
36  * which is the height of a row, or the width of a column.
37  *
38  * GtkBox uses a notion of <emphasis>packing</emphasis>. Packing refers
39  * to adding widgets with reference to a particular position in a
40  * #GtkContainer. For a GtkBox, there are two reference positions: the
41  * <emphasis>start</emphasis> and the <emphasis>end</emphasis> of the box.
42  * For a vertical #GtkBox, the start is defined as the top of the box and
43  * the end is defined as the bottom. For a horizontal #GtkBox the start
44  * is defined as the left side and the end is defined as the right side.
45  *
46  * Use repeated calls to gtk_box_pack_start() to pack widgets into a
47  * GtkBox from start to end. Use gtk_box_pack_end() to add widgets from
48  * end to start. You may intersperse these calls and add widgets from
49  * both ends of the same GtkBox.
50  *
51  * Because GtkBox is a #GtkContainer, you may also use gtk_container_add()
52  * to insert widgets into the box, and they will be packed with the default
53  * values for #GtkBox:expand and #GtkBox:fill. Use gtk_container_remove()
54  * to remove widgets from the GtkBox.
55  *
56  * Use gtk_box_set_homogeneous() to specify whether or not all children
57  * of the GtkBox are forced to get the same amount of space.
58  *
59  * Use gtk_box_set_spacing() to determine how much space will be
60  * minimally placed between all children in the GtkBox. Note that
61  * spacing is added <emphasis>between</emphasis> the children, while
62  * padding added by gtk_box_pack_start() or gtk_box_pack_end() is added
63  * <emphasis>on either side</emphasis> of the widget it belongs to.
64  *
65  * Use gtk_box_reorder_child() to move a GtkBox child to a different
66  * place in the box.
67  *
68  * Use gtk_box_set_child_packing() to reset the #GtkBox:expand,
69  * #GtkBox:fill and #GtkBox:padding child properties.
70  * Use gtk_box_query_child_packing() to query these fields.
71  *
72  * <note><para>
73  * Note that a single-row or single-column #GtkGrid provides exactly
74  * the same functionality as #GtkBox.
75  * </para></note>
76  */
77
78 #include "config.h"
79
80 #include "gtkbox.h"
81 #include "gtkboxprivate.h"
82 #include "gtkintl.h"
83 #include "gtkorientable.h"
84 #include "gtkorientableprivate.h"
85 #include "gtkprivate.h"
86 #include "gtktypebuiltins.h"
87 #include "gtksizerequest.h"
88 #include "gtkwidgetpath.h"
89 #include "gtkwidgetprivate.h"
90 #include "a11y/gtkcontaineraccessible.h"
91
92
93 enum {
94   PROP_0,
95   PROP_ORIENTATION,
96   PROP_SPACING,
97   PROP_HOMOGENEOUS
98 };
99
100 enum {
101   CHILD_PROP_0,
102   CHILD_PROP_EXPAND,
103   CHILD_PROP_FILL,
104   CHILD_PROP_PADDING,
105   CHILD_PROP_PACK_TYPE,
106   CHILD_PROP_POSITION
107 };
108
109 struct _GtkBoxPrivate
110 {
111   GList          *children;
112
113   GtkOrientation  orientation;
114   gint16          spacing;
115
116   guint           default_expand : 1;
117   guint           homogeneous    : 1;
118   guint           spacing_set    : 1;
119 };
120
121 typedef struct _GtkBoxChild        GtkBoxChild;
122
123 /*
124  * GtkBoxChild:
125  * @widget: the child widget, packed into the GtkBox.
126  * @padding: the number of extra pixels to put between this child and its
127  *  neighbors, set when packed, zero by default.
128  * @expand: flag indicates whether extra space should be given to this child.
129  *  Any extra space given to the parent GtkBox is divided up among all children
130  *  with this attribute set to %TRUE; set when packed, %TRUE by default.
131  * @fill: flag indicates whether any extra space given to this child due to its
132  *  @expand attribute being set is actually allocated to the child, rather than
133  *  being used as padding around the widget; set when packed, %TRUE by default.
134  * @pack: one of #GtkPackType indicating whether the child is packed with
135  *  reference to the start (top/left) or end (bottom/right) of the GtkBox.
136  */
137 struct _GtkBoxChild
138 {
139   GtkWidget *widget;
140
141   guint16    padding;
142
143   guint      expand : 1;
144   guint      fill   : 1;
145   guint      pack   : 1;
146 };
147
148 static void gtk_box_size_allocate         (GtkWidget              *widget,
149                                            GtkAllocation          *allocation);
150
151 static void gtk_box_compute_expand     (GtkWidget      *widget,
152                                         gboolean       *hexpand,
153                                         gboolean       *vexpand);
154 static void gtk_box_direction_changed  (GtkWidget        *widget,
155                                         GtkTextDirection  previous_direction);
156
157 static void gtk_box_set_property       (GObject        *object,
158                                         guint           prop_id,
159                                         const GValue   *value,
160                                         GParamSpec     *pspec);
161 static void gtk_box_get_property       (GObject        *object,
162                                         guint           prop_id,
163                                         GValue         *value,
164                                         GParamSpec     *pspec);
165 static void gtk_box_add                (GtkContainer   *container,
166                                         GtkWidget      *widget);
167 static void gtk_box_remove             (GtkContainer   *container,
168                                         GtkWidget      *widget);
169 static void gtk_box_forall             (GtkContainer   *container,
170                                         gboolean        include_internals,
171                                         GtkCallback     callback,
172                                         gpointer        callback_data);
173 static void gtk_box_set_child_property (GtkContainer   *container,
174                                         GtkWidget      *child,
175                                         guint           property_id,
176                                         const GValue   *value,
177                                         GParamSpec     *pspec);
178 static void gtk_box_get_child_property (GtkContainer   *container,
179                                         GtkWidget      *child,
180                                         guint           property_id,
181                                         GValue         *value,
182                                         GParamSpec     *pspec);
183 static GType gtk_box_child_type        (GtkContainer   *container);
184 static GtkWidgetPath * gtk_box_get_path_for_child
185                                        (GtkContainer   *container,
186                                         GtkWidget      *child);
187
188
189 static void               gtk_box_get_preferred_width            (GtkWidget           *widget,
190                                                                   gint                *minimum_size,
191                                                                   gint                *natural_size);
192 static void               gtk_box_get_preferred_height           (GtkWidget           *widget,
193                                                                   gint                *minimum_size,
194                                                                   gint                *natural_size);
195 static void               gtk_box_get_preferred_width_for_height (GtkWidget           *widget,
196                                                                   gint                 height,
197                                                                   gint                *minimum_width,
198                                                                   gint                *natural_width);
199 static void               gtk_box_get_preferred_height_for_width (GtkWidget           *widget,
200                                                                   gint                 width,
201                                                                   gint                *minimum_height,
202                                                                   gint                *natural_height);
203
204
205 G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
206                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
207                                                 NULL))
208
209 static void
210 gtk_box_class_init (GtkBoxClass *class)
211 {
212   GObjectClass *object_class = G_OBJECT_CLASS (class);
213   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
214   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
215
216   object_class->set_property = gtk_box_set_property;
217   object_class->get_property = gtk_box_get_property;
218
219   widget_class->size_allocate                  = gtk_box_size_allocate;
220   widget_class->get_preferred_width            = gtk_box_get_preferred_width;
221   widget_class->get_preferred_height           = gtk_box_get_preferred_height;
222   widget_class->get_preferred_height_for_width = gtk_box_get_preferred_height_for_width;
223   widget_class->get_preferred_width_for_height = gtk_box_get_preferred_width_for_height;
224   widget_class->compute_expand                 = gtk_box_compute_expand;
225   widget_class->direction_changed              = gtk_box_direction_changed;
226
227   container_class->add = gtk_box_add;
228   container_class->remove = gtk_box_remove;
229   container_class->forall = gtk_box_forall;
230   container_class->child_type = gtk_box_child_type;
231   container_class->set_child_property = gtk_box_set_child_property;
232   container_class->get_child_property = gtk_box_get_child_property;
233   container_class->get_path_for_child = gtk_box_get_path_for_child;
234   gtk_container_class_handle_border_width (container_class);
235
236   g_object_class_override_property (object_class,
237                                     PROP_ORIENTATION,
238                                     "orientation");
239
240   g_object_class_install_property (object_class,
241                                    PROP_SPACING,
242                                    g_param_spec_int ("spacing",
243                                                      P_("Spacing"),
244                                                      P_("The amount of space between children"),
245                                                      0,
246                                                      G_MAXINT,
247                                                      0,
248                                                      GTK_PARAM_READWRITE));
249
250   g_object_class_install_property (object_class,
251                                    PROP_HOMOGENEOUS,
252                                    g_param_spec_boolean ("homogeneous",
253                                                          P_("Homogeneous"),
254                                                          P_("Whether the children should all be the same size"),
255                                                          FALSE,
256                                                          GTK_PARAM_READWRITE));
257
258   /**
259    * GtkBox:expand:
260    *
261    * Whether the child should receive extra space when the parent grows.
262    *
263    * Note that the default value for this property is %FALSE for GtkBox,
264    * but #GtkHBox, #GtkVBox and other subclasses use the old default
265    * of %TRUE.
266    *
267    * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand
268    * and #GtkWidget:vexpand properties are the preferred way to influence
269    * child size allocation in containers.
270    */
271   gtk_container_class_install_child_property (container_class,
272                                               CHILD_PROP_EXPAND,
273                                               g_param_spec_boolean ("expand",
274                                                                     P_("Expand"),
275                                                                     P_("Whether the child should receive extra space when the parent grows"),
276                                                                     FALSE,
277                                                                     GTK_PARAM_READWRITE));
278
279   /**
280    * GtkBox:fill:
281    *
282    * Whether the child should receive extra space when the parent grows.
283    *
284    * Note that the #GtkWidget:halign, #GtkWidget:valign, #GtkWidget:hexpand
285    * and #GtkWidget:vexpand properties are the preferred way to influence
286    * child size allocation in containers.
287    */
288   gtk_container_class_install_child_property (container_class,
289                                               CHILD_PROP_FILL,
290                                               g_param_spec_boolean ("fill",
291                                                                     P_("Fill"),
292                                                                     P_("Whether extra space given to the child should be allocated to the child or used as padding"),
293                                                                     TRUE,
294                                                                     GTK_PARAM_READWRITE));
295
296   gtk_container_class_install_child_property (container_class,
297                                               CHILD_PROP_PADDING,
298                                               g_param_spec_uint ("padding",
299                                                                  P_("Padding"),
300                                                                  P_("Extra space to put between the child and its neighbors, in pixels"),
301                                                                  0, G_MAXINT, 0,
302                                                                  GTK_PARAM_READWRITE));
303   gtk_container_class_install_child_property (container_class,
304                                               CHILD_PROP_PACK_TYPE,
305                                               g_param_spec_enum ("pack-type",
306                                                                  P_("Pack type"),
307                                                                  P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
308                                                                  GTK_TYPE_PACK_TYPE, GTK_PACK_START,
309                                                                  GTK_PARAM_READWRITE));
310   gtk_container_class_install_child_property (container_class,
311                                               CHILD_PROP_POSITION,
312                                               g_param_spec_int ("position",
313                                                                 P_("Position"),
314                                                                 P_("The index of the child in the parent"),
315                                                                 -1, G_MAXINT, 0,
316                                                                 GTK_PARAM_READWRITE));
317
318   g_type_class_add_private (object_class, sizeof (GtkBoxPrivate));
319
320   gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_FILLER);
321 }
322
323 static void
324 gtk_box_init (GtkBox *box)
325 {
326   GtkBoxPrivate *private;
327
328   box->priv = G_TYPE_INSTANCE_GET_PRIVATE (box,
329                                            GTK_TYPE_BOX,
330                                            GtkBoxPrivate);
331   private = box->priv;
332
333   gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
334   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), FALSE);
335
336   private->orientation = GTK_ORIENTATION_HORIZONTAL;
337   private->children = NULL;
338
339   private->default_expand = FALSE;
340   private->homogeneous = FALSE;
341   private->spacing = 0;
342   private->spacing_set = FALSE;
343 }
344
345 static void
346 gtk_box_set_property (GObject      *object,
347                       guint         prop_id,
348                       const GValue *value,
349                       GParamSpec   *pspec)
350 {
351   GtkBox *box = GTK_BOX (object);
352   GtkBoxPrivate *private = box->priv;
353
354   switch (prop_id)
355     {
356     case PROP_ORIENTATION:
357       private->orientation = g_value_get_enum (value);
358       _gtk_orientable_set_style_classes (GTK_ORIENTABLE (box));
359       gtk_widget_queue_resize (GTK_WIDGET (box));
360       break;
361     case PROP_SPACING:
362       gtk_box_set_spacing (box, g_value_get_int (value));
363       break;
364     case PROP_HOMOGENEOUS:
365       gtk_box_set_homogeneous (box, g_value_get_boolean (value));
366       break;
367     default:
368       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
369       break;
370     }
371 }
372
373 static void
374 gtk_box_get_property (GObject    *object,
375                       guint       prop_id,
376                       GValue     *value,
377                       GParamSpec *pspec)
378 {
379   GtkBox *box = GTK_BOX (object);
380   GtkBoxPrivate *private = box->priv;
381
382   switch (prop_id)
383     {
384     case PROP_ORIENTATION:
385       g_value_set_enum (value, private->orientation);
386       break;
387     case PROP_SPACING:
388       g_value_set_int (value, private->spacing);
389       break;
390     case PROP_HOMOGENEOUS:
391       g_value_set_boolean (value, private->homogeneous);
392       break;
393     default:
394       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
395       break;
396     }
397 }
398
399
400 static void
401 count_expand_children (GtkBox *box,
402                        gint *visible_children,
403                        gint *expand_children)
404 {
405   GtkBoxPrivate  *private = box->priv;
406   GList       *children;
407   GtkBoxChild *child;
408
409   *visible_children = *expand_children = 0;
410
411   for (children = private->children; children; children = children->next)
412     {
413       child = children->data;
414
415       if (gtk_widget_get_visible (child->widget))
416         {
417           *visible_children += 1;
418           if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
419             *expand_children += 1;
420         }
421     }
422 }
423
424 static void
425 gtk_box_size_allocate (GtkWidget     *widget,
426                        GtkAllocation *allocation)
427 {
428   GtkBox *box = GTK_BOX (widget);
429   GtkBoxPrivate *private = box->priv;
430   GtkBoxChild *child;
431   GList *children;
432   gint nvis_children;
433   gint nexpand_children;
434
435   GtkTextDirection direction;
436   GtkAllocation child_allocation;
437   GtkRequestedSize *sizes;
438
439   GtkPackType packing;
440
441   gint size;
442   gint extra;
443   gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */
444   gint x = 0, y = 0, i;
445   gint child_size;
446
447
448   gtk_widget_set_allocation (widget, allocation);
449
450   count_expand_children (box, &nvis_children, &nexpand_children);
451
452   /* If there is no visible child, simply return. */
453   if (nvis_children <= 0)
454     return;
455
456   direction = gtk_widget_get_direction (widget);
457   sizes = g_newa (GtkRequestedSize, nvis_children);
458
459   if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
460     size = allocation->width - (nvis_children - 1) * private->spacing;
461   else
462     size = allocation->height - (nvis_children - 1) * private->spacing;
463
464   /* Retrieve desired size for visible children. */
465   for (i = 0, children = private->children; children; children = children->next)
466     {
467       child = children->data;
468
469       if (!gtk_widget_get_visible (child->widget))
470         continue;
471
472       if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
473         gtk_widget_get_preferred_width_for_height (child->widget,
474                                                    allocation->height,
475                                                    &sizes[i].minimum_size,
476                                                    &sizes[i].natural_size);
477       else
478         gtk_widget_get_preferred_height_for_width (child->widget,
479                                                    allocation->width,
480                                                    &sizes[i].minimum_size,
481                                                    &sizes[i].natural_size);
482
483
484       /* Assert the api is working properly */
485       if (sizes[i].minimum_size < 0)
486         g_error ("GtkBox child %s minimum %s: %d < 0 for %s %d",
487                  gtk_widget_get_name (GTK_WIDGET (child->widget)),
488                  (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
489                  sizes[i].minimum_size,
490                  (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
491                  (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
492
493       if (sizes[i].natural_size < sizes[i].minimum_size)
494         g_error ("GtkBox child %s natural %s: %d < minimum %d for %s %d",
495                  gtk_widget_get_name (GTK_WIDGET (child->widget)),
496                  (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
497                  sizes[i].natural_size,
498                  sizes[i].minimum_size,
499                  (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "height" : "width",
500                  (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? allocation->height : allocation->width);
501
502       size -= sizes[i].minimum_size;
503       size -= child->padding * 2;
504
505       sizes[i].data = child;
506
507       i++;
508     }
509
510   if (private->homogeneous)
511     {
512       /* If were homogenous we still need to run the above loop to get the
513        * minimum sizes for children that are not going to fill
514        */
515       if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
516         size = allocation->width - (nvis_children - 1) * private->spacing;
517       else
518         size = allocation->height - (nvis_children - 1) * private->spacing;
519
520       extra = size / nvis_children;
521       n_extra_widgets = size % nvis_children;
522     }
523   else
524     {
525       /* Bring children up to size first */
526       size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
527
528       /* Calculate space which hasn't distributed yet,
529        * and is available for expanding children.
530        */
531       if (nexpand_children > 0)
532         {
533           extra = size / nexpand_children;
534           n_extra_widgets = size % nexpand_children;
535         }
536       else
537         extra = 0;
538     }
539
540   /* Allocate child positions. */
541   for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
542     {
543       if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
544         {
545           child_allocation.y = allocation->y;
546           child_allocation.height = MAX (1, allocation->height);
547           if (packing == GTK_PACK_START)
548             x = allocation->x;
549           else
550             x = allocation->x + allocation->width;
551         }
552       else
553         {
554           child_allocation.x = allocation->x;
555           child_allocation.width = MAX (1, allocation->width);
556           if (packing == GTK_PACK_START)
557             y = allocation->y;
558           else
559             y = allocation->y + allocation->height;
560         }
561
562       for (i = 0, children = private->children;
563            children;
564            children = children->next)
565         {
566           child = children->data;
567
568           /* If widget is not visible, skip it. */
569           if (!gtk_widget_get_visible (child->widget))
570             continue;
571
572           /* If widget is packed differently skip it, but still increment i,
573            * since widget is visible and will be handled in next loop iteration.
574            */
575           if (child->pack != packing)
576             {
577               i++;
578               continue;
579             }
580
581           /* Assign the child's size. */
582           if (private->homogeneous)
583             {
584               child_size = extra;
585
586               if (n_extra_widgets > 0)
587                 {
588                   child_size++;
589                   n_extra_widgets--;
590                 }
591             }
592           else
593             {
594               child_size = sizes[i].minimum_size + child->padding * 2;
595
596               if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
597                 {
598                   child_size += extra;
599
600                   if (n_extra_widgets > 0)
601                     {
602                       child_size++;
603                       n_extra_widgets--;
604                     }
605                 }
606             }
607
608           /* Assign the child's position. */
609           if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
610             {
611               if (child->fill)
612                 {
613                   child_allocation.width = MAX (1, child_size - child->padding * 2);
614                   child_allocation.x = x + child->padding;
615                 }
616               else
617                 {
618                   child_allocation.width = sizes[i].minimum_size;
619                   child_allocation.x = x + (child_size - child_allocation.width) / 2;
620                 }
621
622               if (packing == GTK_PACK_START)
623                 {
624                   x += child_size + private->spacing;
625                 }
626               else
627                 {
628                   x -= child_size + private->spacing;
629
630                   child_allocation.x -= child_size;
631                 }
632
633               if (direction == GTK_TEXT_DIR_RTL)
634                 child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
635
636             }
637           else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
638             {
639               if (child->fill)
640                 {
641                   child_allocation.height = MAX (1, child_size - child->padding * 2);
642                   child_allocation.y = y + child->padding;
643                 }
644               else
645                 {
646                   child_allocation.height = sizes[i].minimum_size;
647                   child_allocation.y = y + (child_size - child_allocation.height) / 2;
648                 }
649
650               if (packing == GTK_PACK_START)
651                 {
652                   y += child_size + private->spacing;
653                 }
654               else
655                 {
656                   y -= child_size + private->spacing;
657
658                   child_allocation.y -= child_size;
659                 }
660             }
661           gtk_widget_size_allocate (child->widget, &child_allocation);
662
663           i++;
664         }
665     }
666 }
667
668 static void
669 gtk_box_compute_expand (GtkWidget      *widget,
670                         gboolean       *hexpand_p,
671                         gboolean       *vexpand_p)
672 {
673   GtkBoxPrivate  *private = GTK_BOX (widget)->priv;
674   GList       *children;
675   GtkBoxChild *child;
676   gboolean our_expand;
677   gboolean opposite_expand;
678   GtkOrientation opposite_orientation;
679
680   if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
681     opposite_orientation = GTK_ORIENTATION_VERTICAL;
682   else
683     opposite_orientation = GTK_ORIENTATION_HORIZONTAL;
684
685   our_expand = FALSE;
686   opposite_expand = FALSE;
687
688   for (children = private->children; children; children = children->next)
689     {
690       child = children->data;
691
692       /* we don't recurse into children anymore as soon as we know
693        * expand=TRUE in an orientation
694        */
695
696       if (child->expand || (!our_expand && gtk_widget_compute_expand (child->widget, private->orientation)))
697         our_expand = TRUE;
698
699       if (!opposite_expand && gtk_widget_compute_expand (child->widget, opposite_orientation))
700         opposite_expand = TRUE;
701
702       if (our_expand && opposite_expand)
703         break;
704     }
705
706   if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
707     {
708       *hexpand_p = our_expand;
709       *vexpand_p = opposite_expand;
710     }
711   else
712     {
713       *hexpand_p = opposite_expand;
714       *vexpand_p = our_expand;
715     }
716 }
717
718 static GType
719 gtk_box_child_type (GtkContainer   *container)
720 {
721   return GTK_TYPE_WIDGET;
722 }
723
724 static void
725 gtk_box_set_child_property (GtkContainer *container,
726                             GtkWidget    *child,
727                             guint         property_id,
728                             const GValue *value,
729                             GParamSpec   *pspec)
730 {
731   gboolean expand = 0;
732   gboolean fill = 0;
733   guint padding = 0;
734   GtkPackType pack_type = 0;
735
736   if (property_id != CHILD_PROP_POSITION)
737     gtk_box_query_child_packing (GTK_BOX (container),
738                                  child,
739                                  &expand,
740                                  &fill,
741                                  &padding,
742                                  &pack_type);
743   switch (property_id)
744     {
745     case CHILD_PROP_EXPAND:
746       gtk_box_set_child_packing (GTK_BOX (container),
747                                  child,
748                                  g_value_get_boolean (value),
749                                  fill,
750                                  padding,
751                                  pack_type);
752       break;
753     case CHILD_PROP_FILL:
754       gtk_box_set_child_packing (GTK_BOX (container),
755                                  child,
756                                  expand,
757                                  g_value_get_boolean (value),
758                                  padding,
759                                  pack_type);
760       break;
761     case CHILD_PROP_PADDING:
762       gtk_box_set_child_packing (GTK_BOX (container),
763                                  child,
764                                  expand,
765                                  fill,
766                                  g_value_get_uint (value),
767                                  pack_type);
768       break;
769     case CHILD_PROP_PACK_TYPE:
770       gtk_box_set_child_packing (GTK_BOX (container),
771                                  child,
772                                  expand,
773                                  fill,
774                                  padding,
775                                  g_value_get_enum (value));
776       break;
777     case CHILD_PROP_POSITION:
778       gtk_box_reorder_child (GTK_BOX (container),
779                              child,
780                              g_value_get_int (value));
781       break;
782     default:
783       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
784       break;
785     }
786 }
787
788 static void
789 gtk_box_get_child_property (GtkContainer *container,
790                             GtkWidget    *child,
791                             guint         property_id,
792                             GValue       *value,
793                             GParamSpec   *pspec)
794 {
795   gboolean expand = FALSE;
796   gboolean fill = FALSE;
797   guint padding = 0;
798   GtkPackType pack_type = 0;
799   GList *list;
800
801   if (property_id != CHILD_PROP_POSITION)
802     gtk_box_query_child_packing (GTK_BOX (container),
803                                  child,
804                                  &expand,
805                                  &fill,
806                                  &padding,
807                                  &pack_type);
808   switch (property_id)
809     {
810       guint i;
811     case CHILD_PROP_EXPAND:
812       g_value_set_boolean (value, expand);
813       break;
814     case CHILD_PROP_FILL:
815       g_value_set_boolean (value, fill);
816       break;
817     case CHILD_PROP_PADDING:
818       g_value_set_uint (value, padding);
819       break;
820     case CHILD_PROP_PACK_TYPE:
821       g_value_set_enum (value, pack_type);
822       break;
823     case CHILD_PROP_POSITION:
824       i = 0;
825       for (list = GTK_BOX (container)->priv->children; list; list = list->next)
826         {
827           GtkBoxChild *child_entry;
828
829           child_entry = list->data;
830           if (child_entry->widget == child)
831             break;
832           i++;
833         }
834       g_value_set_int (value, list ? i : -1);
835       break;
836     default:
837       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
838       break;
839     }
840 }
841
842 typedef struct _CountingData CountingData;
843 struct _CountingData {
844   GtkWidget *widget;
845   gboolean found;
846   guint before;
847   guint after;
848 };
849
850 static void
851 count_widget_position (GtkWidget *widget,
852                        gpointer   data)
853 {
854   CountingData *count = data;
855
856   if (!gtk_widget_get_visible (widget))
857     return;
858
859   if (count->widget == widget)
860     count->found = TRUE;
861   else if (count->found)
862     count->after++;
863   else
864     count->before++;
865 }
866
867 static gint
868 gtk_box_get_visible_position (GtkBox    *box,
869                               GtkWidget *child)
870 {
871   CountingData count = { child, FALSE, 0, 0 };
872
873   /* foreach iterates in visible order */
874   gtk_container_foreach (GTK_CONTAINER (box),
875                          count_widget_position,
876                          &count);
877
878   /* the child wasn't found, it's likely an internal child of some
879    * subclass, return -1 to indicate that there is no sibling relation
880    * to the regular box children
881    */
882   if (!count.found)
883     return -1;
884
885   if (box->priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
886       gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
887     return count.after;
888   else
889     return count.before;
890 }
891
892 static GtkWidgetPath *
893 gtk_box_get_path_for_child (GtkContainer *container,
894                             GtkWidget    *child)
895 {
896   GtkWidgetPath *path, *sibling_path;
897   GtkBox *box;
898   GtkBoxPrivate *private;
899   GList *list, *children;
900
901   box = GTK_BOX (container);
902   private = box->priv;
903
904   path = _gtk_widget_create_path (GTK_WIDGET (container));
905
906   if (gtk_widget_get_visible (child))
907     {
908       gint position;
909
910       sibling_path = gtk_widget_path_new ();
911
912       /* get_children works in visible order */
913       children = gtk_container_get_children (container);
914       if (private->orientation == GTK_ORIENTATION_HORIZONTAL &&
915           gtk_widget_get_direction (GTK_WIDGET (box)) == GTK_TEXT_DIR_RTL)
916         children = g_list_reverse (children);
917
918       for (list = children; list; list = list->next)
919         {
920           if (!gtk_widget_get_visible (list->data))
921             continue;
922
923           gtk_widget_path_append_for_widget (sibling_path, list->data);
924         }
925
926       g_list_free (children);
927
928       position = gtk_box_get_visible_position (box, child);
929
930       if (position >= 0)
931         gtk_widget_path_append_with_siblings (path, sibling_path, position);
932       else
933         gtk_widget_path_append_for_widget (path, child);
934
935       gtk_widget_path_unref (sibling_path);
936     }
937   else
938     gtk_widget_path_append_for_widget (path, child);
939
940   return path;
941 }
942
943 static void
944 gtk_box_invalidate_order_foreach (GtkWidget *widget)
945 {
946   _gtk_widget_invalidate_style_context (widget, GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
947 }
948
949 static void
950 gtk_box_invalidate_order (GtkBox *box)
951 {
952   gtk_container_foreach (GTK_CONTAINER (box),
953                          (GtkCallback) gtk_box_invalidate_order_foreach,
954                          NULL);
955 }
956
957 static void
958 gtk_box_direction_changed (GtkWidget        *widget,
959                            GtkTextDirection  previous_direction)
960 {
961   gtk_box_invalidate_order (GTK_BOX (widget));
962 }
963
964 static void
965 box_child_visibility_notify_cb (GObject *obj,
966                                 GParamSpec *pspec,
967                                 gpointer user_data)
968 {
969   GtkBox *box = user_data;
970
971   gtk_box_invalidate_order (box);
972 }
973
974 static void
975 gtk_box_pack (GtkBox      *box,
976               GtkWidget   *child,
977               gboolean     expand,
978               gboolean     fill,
979               guint        padding,
980               GtkPackType  pack_type)
981 {
982   GtkBoxPrivate *private = box->priv;
983   GtkBoxChild *child_info;
984
985   g_return_if_fail (GTK_IS_BOX (box));
986   g_return_if_fail (GTK_IS_WIDGET (child));
987   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
988
989   child_info = g_new (GtkBoxChild, 1);
990   child_info->widget = child;
991   child_info->padding = padding;
992   child_info->expand = expand ? TRUE : FALSE;
993   child_info->fill = fill ? TRUE : FALSE;
994   child_info->pack = pack_type;
995
996   private->children = g_list_append (private->children, child_info);
997
998   gtk_widget_freeze_child_notify (child);
999
1000   gtk_box_invalidate_order (box);
1001   gtk_widget_set_parent (child, GTK_WIDGET (box));
1002
1003   g_signal_connect (child, "notify::visible",
1004                     G_CALLBACK (box_child_visibility_notify_cb), box);
1005
1006   gtk_widget_child_notify (child, "expand");
1007   gtk_widget_child_notify (child, "fill");
1008   gtk_widget_child_notify (child, "padding");
1009   gtk_widget_child_notify (child, "pack-type");
1010   gtk_widget_child_notify (child, "position");
1011   gtk_widget_thaw_child_notify (child);
1012 }
1013
1014 static void
1015 gtk_box_get_size (GtkWidget      *widget,
1016                   GtkOrientation  orientation,
1017                   gint           *minimum_size,
1018                   gint           *natural_size)
1019 {
1020   GtkBox *box;
1021   GtkBoxPrivate *private;
1022   GList *children;
1023   gint nvis_children;
1024   gint minimum, natural;
1025
1026   box = GTK_BOX (widget);
1027   private = box->priv;
1028
1029   minimum = natural = 0;
1030
1031   nvis_children = 0;
1032
1033   for (children = private->children; children; children = children->next)
1034     {
1035       GtkBoxChild *child = children->data;
1036
1037       if (gtk_widget_get_visible (child->widget))
1038         {
1039           gint child_minimum, child_natural;
1040
1041           if (orientation == GTK_ORIENTATION_HORIZONTAL)
1042             gtk_widget_get_preferred_width (child->widget,
1043                                             &child_minimum, &child_natural);
1044           else
1045             gtk_widget_get_preferred_height (child->widget,
1046                                              &child_minimum, &child_natural);
1047
1048           if (private->orientation == orientation)
1049             {
1050               if (private->homogeneous)
1051                 {
1052                   gint largest;
1053
1054                   largest = child_minimum + child->padding * 2;
1055                   minimum = MAX (minimum, largest);
1056
1057                   largest = child_natural + child->padding * 2;
1058                   natural = MAX (natural, largest);
1059                 }
1060               else
1061                 {
1062                   minimum += child_minimum + child->padding * 2;
1063                   natural += child_natural + child->padding * 2;
1064                 }
1065             }
1066           else
1067             {
1068               /* The biggest mins and naturals in the opposing orientation */
1069               minimum = MAX (minimum, child_minimum);
1070               natural = MAX (natural, child_natural);
1071             }
1072
1073           nvis_children += 1;
1074         }
1075     }
1076
1077   if (nvis_children > 0 && private->orientation == orientation)
1078     {
1079       if (private->homogeneous)
1080         {
1081           minimum *= nvis_children;
1082           natural *= nvis_children;
1083         }
1084       minimum += (nvis_children - 1) * private->spacing;
1085       natural += (nvis_children - 1) * private->spacing;
1086     }
1087
1088   if (minimum_size)
1089     *minimum_size = minimum;
1090
1091   if (natural_size)
1092     *natural_size = natural;
1093 }
1094
1095 static void
1096 gtk_box_get_preferred_width (GtkWidget *widget,
1097                              gint      *minimum_size,
1098                              gint      *natural_size)
1099 {
1100   gtk_box_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
1101 }
1102
1103 static void
1104 gtk_box_get_preferred_height (GtkWidget *widget,
1105                               gint      *minimum_size,
1106                               gint      *natural_size)
1107 {
1108   gtk_box_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
1109 }
1110
1111 static void
1112 gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
1113                                                gint    avail_size,
1114                                                gint   *minimum_size,
1115                                                gint   *natural_size)
1116 {
1117   GtkBoxPrivate       *private = box->priv;
1118   GtkBoxChild      *child;
1119   GList            *children;
1120   gint              nvis_children;
1121   gint              nexpand_children;
1122   gint              computed_minimum = 0, computed_natural = 0;
1123   GtkRequestedSize *sizes;
1124   GtkPackType       packing;
1125   gint              size, extra, i;
1126   gint              child_size, child_minimum, child_natural;
1127   gint              n_extra_widgets = 0;
1128
1129   count_expand_children (box, &nvis_children, &nexpand_children);
1130
1131   if (nvis_children <= 0)
1132     return;
1133
1134   sizes = g_newa (GtkRequestedSize, nvis_children);
1135   size = avail_size - (nvis_children - 1) * private->spacing;
1136
1137   /* Retrieve desired size for visible children */
1138   for (i = 0, children = private->children; children; children = children->next)
1139     {
1140       child = children->data;
1141
1142       if (gtk_widget_get_visible (child->widget))
1143         {
1144           if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1145             gtk_widget_get_preferred_width (child->widget,
1146                                             &sizes[i].minimum_size,
1147                                             &sizes[i].natural_size);
1148           else
1149             gtk_widget_get_preferred_height (child->widget,
1150                                              &sizes[i].minimum_size,
1151                                              &sizes[i].natural_size);
1152
1153           /* Assert the api is working properly */
1154           if (sizes[i].minimum_size < 0)
1155             g_error ("GtkBox child %s minimum %s: %d < 0",
1156                      gtk_widget_get_name (GTK_WIDGET (child->widget)),
1157                      (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1158                      sizes[i].minimum_size);
1159
1160           if (sizes[i].natural_size < sizes[i].minimum_size)
1161             g_error ("GtkBox child %s natural %s: %d < minimum %d",
1162                      gtk_widget_get_name (GTK_WIDGET (child->widget)),
1163                      (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1164                      sizes[i].natural_size,
1165                      sizes[i].minimum_size);
1166
1167           size -= sizes[i].minimum_size;
1168           size -= child->padding * 2;
1169
1170           sizes[i].data = child;
1171
1172           i += 1;
1173         }
1174     }
1175
1176   if (private->homogeneous)
1177     {
1178       /* If were homogenous we still need to run the above loop to get the
1179        * minimum sizes for children that are not going to fill
1180        */
1181       size = avail_size - (nvis_children - 1) * private->spacing;
1182       extra = size / nvis_children;
1183       n_extra_widgets = size % nvis_children;
1184     }
1185   else
1186     {
1187       /* Bring children up to size first */
1188       size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
1189
1190       /* Calculate space which hasn't distributed yet,
1191        * and is available for expanding children.
1192        */
1193       if (nexpand_children > 0)
1194         {
1195           extra = size / nexpand_children;
1196           n_extra_widgets = size % nexpand_children;
1197         }
1198       else
1199         extra = 0;
1200     }
1201
1202   /* Allocate child positions. */
1203   for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
1204     {
1205       for (i = 0, children = private->children;
1206            children;
1207            children = children->next)
1208         {
1209           child = children->data;
1210
1211           /* If widget is not visible, skip it. */
1212           if (!gtk_widget_get_visible (child->widget))
1213             continue;
1214
1215           /* If widget is packed differently skip it, but still increment i,
1216            * since widget is visible and will be handled in next loop iteration.
1217            */
1218           if (child->pack != packing)
1219             {
1220               i++;
1221               continue;
1222             }
1223
1224           if (child->pack == packing)
1225             {
1226               /* Assign the child's size. */
1227               if (private->homogeneous)
1228                 {
1229                   child_size = extra;
1230
1231                   if (n_extra_widgets > 0)
1232                     {
1233                       child_size++;
1234                       n_extra_widgets--;
1235                     }
1236                 }
1237               else
1238                 {
1239                   child_size = sizes[i].minimum_size + child->padding * 2;
1240
1241                   if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
1242                     {
1243                       child_size += extra;
1244
1245                       if (n_extra_widgets > 0)
1246                         {
1247                           child_size++;
1248                           n_extra_widgets--;
1249                         }
1250                     }
1251                 }
1252
1253               if (child->fill)
1254                 {
1255                   child_size = MAX (1, child_size - child->padding * 2);
1256                 }
1257               else
1258                 {
1259                   child_size = sizes[i].minimum_size;
1260                 }
1261
1262
1263               /* Assign the child's position. */
1264               if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1265                 gtk_widget_get_preferred_height_for_width (child->widget,
1266                                                            child_size, &child_minimum, &child_natural);
1267               else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1268                 gtk_widget_get_preferred_width_for_height (child->widget,
1269                                                            child_size, &child_minimum, &child_natural);
1270
1271
1272               computed_minimum = MAX (computed_minimum, child_minimum);
1273               computed_natural = MAX (computed_natural, child_natural);
1274             }
1275           i += 1;
1276         }
1277     }
1278
1279   if (minimum_size)
1280     *minimum_size = computed_minimum;
1281   if (natural_size)
1282     *natural_size = computed_natural;
1283 }
1284
1285 static void
1286 gtk_box_compute_size_for_orientation (GtkBox *box,
1287                                       gint    avail_size,
1288                                       gint   *minimum_size,
1289                                       gint   *natural_size)
1290 {
1291   GtkBoxPrivate    *private = box->priv;
1292   GList         *children;
1293   gint           nvis_children = 0;
1294   gint           required_size = 0, required_natural = 0, child_size, child_natural;
1295   gint           largest_child = 0, largest_natural = 0;
1296
1297   for (children = private->children; children != NULL;
1298        children = children->next)
1299     {
1300       GtkBoxChild *child = children->data;
1301
1302       if (gtk_widget_get_visible (child->widget))
1303         {
1304
1305           if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1306             gtk_widget_get_preferred_width_for_height (child->widget,
1307                                                        avail_size, &child_size, &child_natural);
1308           else
1309             gtk_widget_get_preferred_height_for_width (child->widget,
1310                                                        avail_size, &child_size, &child_natural);
1311
1312
1313           child_size    += child->padding * 2;
1314           child_natural += child->padding * 2;
1315
1316           if (child_size > largest_child)
1317             largest_child = child_size;
1318
1319           if (child_natural > largest_natural)
1320             largest_natural = child_natural;
1321
1322           required_size    += child_size;
1323           required_natural += child_natural;
1324
1325           nvis_children += 1;
1326         }
1327     }
1328
1329   if (nvis_children > 0)
1330     {
1331       if (private->homogeneous)
1332         {
1333           required_size    = largest_child   * nvis_children;
1334           required_natural = largest_natural * nvis_children;
1335         }
1336
1337       required_size     += (nvis_children - 1) * private->spacing;
1338       required_natural  += (nvis_children - 1) * private->spacing;
1339     }
1340
1341   if (minimum_size)
1342     *minimum_size = required_size;
1343
1344   if (natural_size)
1345     *natural_size = required_natural;
1346 }
1347
1348 static void
1349 gtk_box_get_preferred_width_for_height (GtkWidget *widget,
1350                                         gint       height,
1351                                         gint      *minimum_width,
1352                                         gint      *natural_width)
1353 {
1354   GtkBox        *box     = GTK_BOX (widget);
1355   GtkBoxPrivate *private = box->priv;
1356
1357   if (private->orientation == GTK_ORIENTATION_VERTICAL)
1358     gtk_box_compute_size_for_opposing_orientation (box, height, minimum_width, natural_width);
1359   else
1360     gtk_box_compute_size_for_orientation (box, height, minimum_width, natural_width);
1361 }
1362
1363 static void
1364 gtk_box_get_preferred_height_for_width (GtkWidget *widget,
1365                                         gint       width,
1366                                         gint      *minimum_height,
1367                                         gint      *natural_height)
1368 {
1369   GtkBox        *box     = GTK_BOX (widget);
1370   GtkBoxPrivate *private = box->priv;
1371
1372   if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1373     gtk_box_compute_size_for_opposing_orientation (box, width, minimum_height, natural_height);
1374   else
1375     gtk_box_compute_size_for_orientation (box, width, minimum_height, natural_height);
1376 }
1377
1378 /**
1379  * gtk_box_new:
1380  * @orientation: the box's orientation.
1381  * @spacing: the number of pixels to place by default between children.
1382  *
1383  * Creates a new #GtkBox.
1384  *
1385  * Return value: a new #GtkBox.
1386  *
1387  * Since: 3.0
1388  **/
1389 GtkWidget*
1390 gtk_box_new (GtkOrientation orientation,
1391              gint           spacing)
1392 {
1393   return g_object_new (GTK_TYPE_BOX,
1394                        "orientation", orientation,
1395                        "spacing",     spacing,
1396                        NULL);
1397 }
1398
1399 /**
1400  * gtk_box_pack_start:
1401  * @box: a #GtkBox
1402  * @child: the #GtkWidget to be added to @box
1403  * @expand: %TRUE if the new child is to be given extra space allocated
1404  *     to @box. The extra space will be divided evenly between all children
1405  *     that use this option
1406  * @fill: %TRUE if space given to @child by the @expand option is
1407  *     actually allocated to @child, rather than just padding it.  This
1408  *     parameter has no effect if @expand is set to %FALSE.  A child is
1409  *     always allocated the full height of a horizontal #GtkBox and the full width
1410  *     of a vertical #GtkBox. This option affects the other dimension
1411  * @padding: extra space in pixels to put between this child and its
1412  *   neighbors, over and above the global amount specified by
1413  *   #GtkBox:spacing property.  If @child is a widget at one of the
1414  *   reference ends of @box, then @padding pixels are also put between
1415  *   @child and the reference edge of @box
1416  *
1417  * Adds @child to @box, packed with reference to the start of @box.
1418  * The @child is packed after any other child packed with reference
1419  * to the start of @box.
1420  */
1421 void
1422 gtk_box_pack_start (GtkBox    *box,
1423                     GtkWidget *child,
1424                     gboolean   expand,
1425                     gboolean   fill,
1426                     guint      padding)
1427 {
1428   gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_START);
1429 }
1430
1431 /**
1432  * gtk_box_pack_end:
1433  * @box: a #GtkBox
1434  * @child: the #GtkWidget to be added to @box
1435  * @expand: %TRUE if the new child is to be given extra space allocated
1436  *   to @box. The extra space will be divided evenly between all children
1437  *   of @box that use this option
1438  * @fill: %TRUE if space given to @child by the @expand option is
1439  *   actually allocated to @child, rather than just padding it.  This
1440  *   parameter has no effect if @expand is set to %FALSE.  A child is
1441  *   always allocated the full height of a horizontal #GtkBox and the full width
1442  *   of a vertical #GtkBox.  This option affects the other dimension
1443  * @padding: extra space in pixels to put between this child and its
1444  *   neighbors, over and above the global amount specified by
1445  *   #GtkBox:spacing property.  If @child is a widget at one of the
1446  *   reference ends of @box, then @padding pixels are also put between
1447  *   @child and the reference edge of @box
1448  *
1449  * Adds @child to @box, packed with reference to the end of @box.
1450  * The @child is packed after (away from end of) any other child
1451  * packed with reference to the end of @box.
1452  */
1453 void
1454 gtk_box_pack_end (GtkBox    *box,
1455                   GtkWidget *child,
1456                   gboolean   expand,
1457                   gboolean   fill,
1458                   guint      padding)
1459 {
1460   gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_END);
1461 }
1462
1463 /**
1464  * gtk_box_set_homogeneous:
1465  * @box: a #GtkBox
1466  * @homogeneous: a boolean value, %TRUE to create equal allotments,
1467  *   %FALSE for variable allotments
1468  *
1469  * Sets the #GtkBox:homogeneous property of @box, controlling
1470  * whether or not all children of @box are given equal space
1471  * in the box.
1472  */
1473 void
1474 gtk_box_set_homogeneous (GtkBox  *box,
1475                          gboolean homogeneous)
1476 {
1477   GtkBoxPrivate *private;
1478
1479   g_return_if_fail (GTK_IS_BOX (box));
1480
1481   private = box->priv;
1482
1483   if ((homogeneous ? TRUE : FALSE) != private->homogeneous)
1484     {
1485       private->homogeneous = homogeneous ? TRUE : FALSE;
1486       g_object_notify (G_OBJECT (box), "homogeneous");
1487       gtk_widget_queue_resize (GTK_WIDGET (box));
1488     }
1489 }
1490
1491 /**
1492  * gtk_box_get_homogeneous:
1493  * @box: a #GtkBox
1494  *
1495  * Returns whether the box is homogeneous (all children are the
1496  * same size). See gtk_box_set_homogeneous().
1497  *
1498  * Return value: %TRUE if the box is homogeneous.
1499  **/
1500 gboolean
1501 gtk_box_get_homogeneous (GtkBox *box)
1502 {
1503   g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1504
1505   return box->priv->homogeneous;
1506 }
1507
1508 /**
1509  * gtk_box_set_spacing:
1510  * @box: a #GtkBox
1511  * @spacing: the number of pixels to put between children
1512  *
1513  * Sets the #GtkBox:spacing property of @box, which is the
1514  * number of pixels to place between children of @box.
1515  */
1516 void
1517 gtk_box_set_spacing (GtkBox *box,
1518                      gint    spacing)
1519 {
1520   GtkBoxPrivate *private;
1521
1522   g_return_if_fail (GTK_IS_BOX (box));
1523
1524   private = box->priv;
1525
1526   if (spacing != private->spacing)
1527     {
1528       private->spacing = spacing;
1529       _gtk_box_set_spacing_set (box, TRUE);
1530
1531       g_object_notify (G_OBJECT (box), "spacing");
1532
1533       gtk_widget_queue_resize (GTK_WIDGET (box));
1534     }
1535 }
1536
1537 /**
1538  * gtk_box_get_spacing:
1539  * @box: a #GtkBox
1540  *
1541  * Gets the value set by gtk_box_set_spacing().
1542  *
1543  * Return value: spacing between children
1544  **/
1545 gint
1546 gtk_box_get_spacing (GtkBox *box)
1547 {
1548   g_return_val_if_fail (GTK_IS_BOX (box), 0);
1549
1550   return box->priv->spacing;
1551 }
1552
1553 void
1554 _gtk_box_set_spacing_set (GtkBox  *box,
1555                           gboolean spacing_set)
1556 {
1557   GtkBoxPrivate *private;
1558
1559   g_return_if_fail (GTK_IS_BOX (box));
1560
1561   private = box->priv;
1562
1563   private->spacing_set = spacing_set ? TRUE : FALSE;
1564 }
1565
1566 gboolean
1567 _gtk_box_get_spacing_set (GtkBox *box)
1568 {
1569   GtkBoxPrivate *private;
1570
1571   g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1572
1573   private = box->priv;
1574
1575   return private->spacing_set;
1576 }
1577
1578 /**
1579  * gtk_box_reorder_child:
1580  * @box: a #GtkBox
1581  * @child: the #GtkWidget to move
1582  * @position: the new position for @child in the list of children
1583  *   of @box, starting from 0. If negative, indicates the end of
1584  *   the list
1585  *
1586  * Moves @child to a new @position in the list of @box children.
1587  * The list is the <structfield>children</structfield> field of
1588  * #GtkBox-struct, and contains both widgets packed #GTK_PACK_START
1589  * as well as widgets packed #GTK_PACK_END, in the order that these
1590  * widgets were added to @box.
1591  *
1592  * A widget's position in the @box children list determines where
1593  * the widget is packed into @box.  A child widget at some position
1594  * in the list will be packed just after all other widgets of the
1595  * same packing type that appear earlier in the list.
1596  */
1597 void
1598 gtk_box_reorder_child (GtkBox    *box,
1599                        GtkWidget *child,
1600                        gint       position)
1601 {
1602   GtkBoxPrivate *priv;
1603   GList *old_link;
1604   GList *new_link;
1605   GtkBoxChild *child_info = NULL;
1606   gint old_position;
1607
1608   g_return_if_fail (GTK_IS_BOX (box));
1609   g_return_if_fail (GTK_IS_WIDGET (child));
1610
1611   priv = box->priv;
1612
1613   old_link = priv->children;
1614   old_position = 0;
1615   while (old_link)
1616     {
1617       child_info = old_link->data;
1618       if (child_info->widget == child)
1619         break;
1620
1621       old_link = old_link->next;
1622       old_position++;
1623     }
1624
1625   g_return_if_fail (old_link != NULL);
1626
1627   if (position == old_position)
1628     return;
1629
1630   priv->children = g_list_delete_link (priv->children, old_link);
1631
1632   if (position < 0)
1633     new_link = NULL;
1634   else
1635     new_link = g_list_nth (priv->children, position);
1636
1637   priv->children = g_list_insert_before (priv->children, new_link, child_info);
1638
1639   gtk_widget_child_notify (child, "position");
1640   if (gtk_widget_get_visible (child)
1641       && gtk_widget_get_visible (GTK_WIDGET (box)))
1642     {
1643       gtk_box_invalidate_order (box);
1644       gtk_widget_queue_resize (child);
1645     }
1646 }
1647
1648 /**
1649  * gtk_box_query_child_packing:
1650  * @box: a #GtkBox
1651  * @child: the #GtkWidget of the child to query
1652  * @expand: (out): pointer to return location for #GtkBox:expand child
1653  *     property
1654  * @fill: (out): pointer to return location for #GtkBox:fill child
1655  *     property
1656  * @padding: (out): pointer to return location for #GtkBox:padding
1657  *     child property
1658  * @pack_type: (out): pointer to return location for #GtkBox:pack-type
1659  *     child property
1660  *
1661  * Obtains information about how @child is packed into @box.
1662  */
1663 void
1664 gtk_box_query_child_packing (GtkBox      *box,
1665                              GtkWidget   *child,
1666                              gboolean    *expand,
1667                              gboolean    *fill,
1668                              guint       *padding,
1669                              GtkPackType *pack_type)
1670 {
1671   GtkBoxPrivate *private;
1672   GList *list;
1673   GtkBoxChild *child_info = NULL;
1674
1675   g_return_if_fail (GTK_IS_BOX (box));
1676   g_return_if_fail (GTK_IS_WIDGET (child));
1677
1678   private = box->priv;
1679
1680   list = private->children;
1681   while (list)
1682     {
1683       child_info = list->data;
1684       if (child_info->widget == child)
1685         break;
1686
1687       list = list->next;
1688     }
1689
1690   if (list)
1691     {
1692       if (expand)
1693         *expand = child_info->expand;
1694       if (fill)
1695         *fill = child_info->fill;
1696       if (padding)
1697         *padding = child_info->padding;
1698       if (pack_type)
1699         *pack_type = child_info->pack;
1700     }
1701 }
1702
1703 /**
1704  * gtk_box_set_child_packing:
1705  * @box: a #GtkBox
1706  * @child: the #GtkWidget of the child to set
1707  * @expand: the new value of the #GtkBox:expand child property
1708  * @fill: the new value of the #GtkBox:fill child property
1709  * @padding: the new value of the #GtkBox:padding child property
1710  * @pack_type: the new value of the #GtkBox:pack-type child property
1711  *
1712  * Sets the way @child is packed into @box.
1713  */
1714 void
1715 gtk_box_set_child_packing (GtkBox      *box,
1716                            GtkWidget   *child,
1717                            gboolean     expand,
1718                            gboolean     fill,
1719                            guint        padding,
1720                            GtkPackType  pack_type)
1721 {
1722   GtkBoxPrivate *private;
1723   GList *list;
1724   GtkBoxChild *child_info = NULL;
1725
1726   g_return_if_fail (GTK_IS_BOX (box));
1727   g_return_if_fail (GTK_IS_WIDGET (child));
1728
1729   private = box->priv;
1730
1731   list = private->children;
1732   while (list)
1733     {
1734       child_info = list->data;
1735       if (child_info->widget == child)
1736         break;
1737
1738       list = list->next;
1739     }
1740
1741   gtk_widget_freeze_child_notify (child);
1742   if (list)
1743     {
1744       gboolean expanded;
1745
1746       expanded = expand != FALSE;
1747
1748       /* avoid setting expand if unchanged, since queue_compute_expand
1749        * can be expensive-ish
1750        */
1751       if (child_info->expand != expanded)
1752         {
1753           child_info->expand = expand != FALSE;
1754           gtk_widget_queue_compute_expand (GTK_WIDGET (box));
1755           gtk_widget_child_notify (child, "expand");
1756         }
1757
1758       child_info->fill = fill != FALSE;
1759       gtk_widget_child_notify (child, "fill");
1760       child_info->padding = padding;
1761       gtk_widget_child_notify (child, "padding");
1762       if (pack_type != GTK_PACK_END)
1763         pack_type = GTK_PACK_START;
1764       if (child_info->pack != pack_type)
1765         {
1766           child_info->pack = pack_type;
1767           gtk_widget_child_notify (child, "pack-type");
1768           gtk_box_invalidate_order (box);
1769         }
1770
1771       if (gtk_widget_get_visible (child)
1772           && gtk_widget_get_visible (GTK_WIDGET (box)))
1773         gtk_widget_queue_resize (child);
1774     }
1775   gtk_widget_thaw_child_notify (child);
1776 }
1777
1778 void
1779 _gtk_box_set_old_defaults (GtkBox *box)
1780 {
1781   GtkBoxPrivate *private;
1782
1783   g_return_if_fail (GTK_IS_BOX (box));
1784
1785   private = box->priv;
1786
1787   private->default_expand = TRUE;
1788 }
1789
1790 static void
1791 gtk_box_add (GtkContainer *container,
1792              GtkWidget    *widget)
1793 {
1794   GtkBoxPrivate *priv = GTK_BOX (container)->priv;
1795
1796   gtk_box_pack_start (GTK_BOX (container), widget,
1797                       priv->default_expand,
1798                       TRUE,
1799                       0);
1800 }
1801
1802 static void
1803 gtk_box_remove (GtkContainer *container,
1804                 GtkWidget    *widget)
1805 {
1806   GtkBox *box = GTK_BOX (container);
1807   GtkBoxPrivate *priv = box->priv;
1808   GtkBoxChild *child;
1809   GList *children;
1810
1811   children = priv->children;
1812   while (children)
1813     {
1814       child = children->data;
1815
1816       if (child->widget == widget)
1817         {
1818           gboolean was_visible;
1819
1820           g_signal_handlers_disconnect_by_func (widget,
1821                                                 box_child_visibility_notify_cb,
1822                                                 box);
1823
1824           was_visible = gtk_widget_get_visible (widget);
1825           gtk_widget_unparent (widget);
1826
1827           priv->children = g_list_remove_link (priv->children, children);
1828           g_list_free (children);
1829           g_free (child);
1830
1831           /* queue resize regardless of gtk_widget_get_visible (container),
1832            * since that's what is needed by toplevels.
1833            */
1834           if (was_visible)
1835             {
1836               gtk_box_invalidate_order (box);
1837               gtk_widget_queue_resize (GTK_WIDGET (container));
1838             }
1839
1840           break;
1841         }
1842
1843       children = children->next;
1844     }
1845 }
1846
1847 static void
1848 gtk_box_forall (GtkContainer *container,
1849                 gboolean      include_internals,
1850                 GtkCallback   callback,
1851                 gpointer      callback_data)
1852 {
1853   GtkBox *box = GTK_BOX (container);
1854   GtkBoxPrivate *priv = box->priv;
1855   GtkBoxChild *child;
1856   GList *children;
1857
1858   children = priv->children;
1859   while (children)
1860     {
1861       child = children->data;
1862       children = children->next;
1863
1864       if (child->pack == GTK_PACK_START)
1865         (* callback) (child->widget, callback_data);
1866     }
1867
1868   children = g_list_last (priv->children);
1869   while (children)
1870     {
1871       child = children->data;
1872       children = children->prev;
1873
1874       if (child->pack == GTK_PACK_END)
1875         (* callback) (child->widget, callback_data);
1876     }
1877 }
1878
1879 GList *
1880 _gtk_box_get_children (GtkBox *box)
1881 {
1882   GtkBoxPrivate *priv;
1883   GtkBoxChild *child;
1884   GList *children;
1885   GList *retval = NULL;
1886
1887   g_return_val_if_fail (GTK_IS_BOX (box), NULL);
1888
1889   priv = box->priv;
1890
1891   children = priv->children;
1892   while (children)
1893     {
1894       child = children->data;
1895       children = children->next;
1896
1897       retval = g_list_prepend (retval, child->widget);
1898     }
1899
1900   return g_list_reverse (retval);
1901 }