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