]> Pileus Git - ~andy/gtk/blob - gtk/gtkbox.c
gtk: Fix warnings for some uses of GtkLinkButton
[~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 "gtkprivate.h"
85 #include "gtktypebuiltins.h"
86 #include "gtksizerequest.h"
87 #include "gtkwidgetpath.h"
88 #include "gtkwidgetprivate.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_create_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_foreach (GtkWidget *widget)
943 {
944   _gtk_widget_invalidate_style_context (widget, GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_SIBLING_POSITION);
945 }
946
947 static void
948 gtk_box_invalidate_order (GtkBox *box)
949 {
950   gtk_container_foreach (GTK_CONTAINER (box),
951                          (GtkCallback) gtk_box_invalidate_order_foreach,
952                          NULL);
953 }
954
955 static void
956 gtk_box_direction_changed (GtkWidget        *widget,
957                            GtkTextDirection  previous_direction)
958 {
959   gtk_box_invalidate_order (GTK_BOX (widget));
960 }
961
962 static void
963 box_child_visibility_notify_cb (GObject *obj,
964                                 GParamSpec *pspec,
965                                 gpointer user_data)
966 {
967   GtkBox *box = user_data;
968
969   gtk_box_invalidate_order (box);
970 }
971
972 static void
973 gtk_box_pack (GtkBox      *box,
974               GtkWidget   *child,
975               gboolean     expand,
976               gboolean     fill,
977               guint        padding,
978               GtkPackType  pack_type)
979 {
980   GtkBoxPrivate *private = box->priv;
981   GtkBoxChild *child_info;
982
983   g_return_if_fail (GTK_IS_BOX (box));
984   g_return_if_fail (GTK_IS_WIDGET (child));
985   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
986
987   child_info = g_new (GtkBoxChild, 1);
988   child_info->widget = child;
989   child_info->padding = padding;
990   child_info->expand = expand ? TRUE : FALSE;
991   child_info->fill = fill ? TRUE : FALSE;
992   child_info->pack = pack_type;
993
994   private->children = g_list_append (private->children, child_info);
995
996   gtk_widget_freeze_child_notify (child);
997
998   gtk_box_invalidate_order (box);
999   gtk_widget_set_parent (child, GTK_WIDGET (box));
1000
1001   g_signal_connect (child, "notify::visible",
1002                     G_CALLBACK (box_child_visibility_notify_cb), box);
1003
1004   gtk_widget_child_notify (child, "expand");
1005   gtk_widget_child_notify (child, "fill");
1006   gtk_widget_child_notify (child, "padding");
1007   gtk_widget_child_notify (child, "pack-type");
1008   gtk_widget_child_notify (child, "position");
1009   gtk_widget_thaw_child_notify (child);
1010 }
1011
1012 static void
1013 gtk_box_get_size (GtkWidget      *widget,
1014                   GtkOrientation  orientation,
1015                   gint           *minimum_size,
1016                   gint           *natural_size)
1017 {
1018   GtkBox *box;
1019   GtkBoxPrivate *private;
1020   GList *children;
1021   gint nvis_children;
1022   gint minimum, natural;
1023
1024   box = GTK_BOX (widget);
1025   private = box->priv;
1026
1027   minimum = natural = 0;
1028
1029   nvis_children = 0;
1030
1031   for (children = private->children; children; children = children->next)
1032     {
1033       GtkBoxChild *child = children->data;
1034
1035       if (gtk_widget_get_visible (child->widget))
1036         {
1037           gint child_minimum, child_natural;
1038
1039           if (orientation == GTK_ORIENTATION_HORIZONTAL)
1040             gtk_widget_get_preferred_width (child->widget,
1041                                             &child_minimum, &child_natural);
1042           else
1043             gtk_widget_get_preferred_height (child->widget,
1044                                              &child_minimum, &child_natural);
1045
1046           if (private->orientation == orientation)
1047             {
1048               if (private->homogeneous)
1049                 {
1050                   gint largest;
1051
1052                   largest = child_minimum + child->padding * 2;
1053                   minimum = MAX (minimum, largest);
1054
1055                   largest = child_natural + child->padding * 2;
1056                   natural = MAX (natural, largest);
1057                 }
1058               else
1059                 {
1060                   minimum += child_minimum + child->padding * 2;
1061                   natural += child_natural + child->padding * 2;
1062                 }
1063             }
1064           else
1065             {
1066               /* The biggest mins and naturals in the opposing orientation */
1067               minimum = MAX (minimum, child_minimum);
1068               natural = MAX (natural, child_natural);
1069             }
1070
1071           nvis_children += 1;
1072         }
1073     }
1074
1075   if (nvis_children > 0 && private->orientation == orientation)
1076     {
1077       if (private->homogeneous)
1078         {
1079           minimum *= nvis_children;
1080           natural *= nvis_children;
1081         }
1082       minimum += (nvis_children - 1) * private->spacing;
1083       natural += (nvis_children - 1) * private->spacing;
1084     }
1085
1086   if (minimum_size)
1087     *minimum_size = minimum;
1088
1089   if (natural_size)
1090     *natural_size = natural;
1091 }
1092
1093 static void
1094 gtk_box_get_preferred_width (GtkWidget *widget,
1095                              gint      *minimum_size,
1096                              gint      *natural_size)
1097 {
1098   gtk_box_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
1099 }
1100
1101 static void
1102 gtk_box_get_preferred_height (GtkWidget *widget,
1103                               gint      *minimum_size,
1104                               gint      *natural_size)
1105 {
1106   gtk_box_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
1107 }
1108
1109 static void
1110 gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
1111                                                gint    avail_size,
1112                                                gint   *minimum_size,
1113                                                gint   *natural_size)
1114 {
1115   GtkBoxPrivate       *private = box->priv;
1116   GtkBoxChild      *child;
1117   GList            *children;
1118   gint              nvis_children;
1119   gint              nexpand_children;
1120   gint              computed_minimum = 0, computed_natural = 0;
1121   GtkRequestedSize *sizes;
1122   GtkPackType       packing;
1123   gint              size, extra, i;
1124   gint              child_size, child_minimum, child_natural;
1125   gint              n_extra_widgets = 0;
1126
1127   count_expand_children (box, &nvis_children, &nexpand_children);
1128
1129   if (nvis_children <= 0)
1130     return;
1131
1132   sizes = g_newa (GtkRequestedSize, nvis_children);
1133   size = avail_size - (nvis_children - 1) * private->spacing;
1134
1135   /* Retrieve desired size for visible children */
1136   for (i = 0, children = private->children; children; children = children->next)
1137     {
1138       child = children->data;
1139
1140       if (gtk_widget_get_visible (child->widget))
1141         {
1142           if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1143             gtk_widget_get_preferred_width (child->widget,
1144                                             &sizes[i].minimum_size,
1145                                             &sizes[i].natural_size);
1146           else
1147             gtk_widget_get_preferred_height (child->widget,
1148                                              &sizes[i].minimum_size,
1149                                              &sizes[i].natural_size);
1150
1151           /* Assert the api is working properly */
1152           if (sizes[i].minimum_size < 0)
1153             g_error ("GtkBox child %s minimum %s: %d < 0",
1154                      gtk_widget_get_name (GTK_WIDGET (child->widget)),
1155                      (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1156                      sizes[i].minimum_size);
1157
1158           if (sizes[i].natural_size < sizes[i].minimum_size)
1159             g_error ("GtkBox child %s natural %s: %d < minimum %d",
1160                      gtk_widget_get_name (GTK_WIDGET (child->widget)),
1161                      (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
1162                      sizes[i].natural_size,
1163                      sizes[i].minimum_size);
1164
1165           size -= sizes[i].minimum_size;
1166           size -= child->padding * 2;
1167
1168           sizes[i].data = child;
1169
1170           i += 1;
1171         }
1172     }
1173
1174   if (private->homogeneous)
1175     {
1176       /* If were homogenous we still need to run the above loop to get the
1177        * minimum sizes for children that are not going to fill
1178        */
1179       size = avail_size - (nvis_children - 1) * private->spacing;
1180       extra = size / nvis_children;
1181       n_extra_widgets = size % nvis_children;
1182     }
1183   else
1184     {
1185       /* Bring children up to size first */
1186       size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
1187
1188       /* Calculate space which hasn't distributed yet,
1189        * and is available for expanding children.
1190        */
1191       if (nexpand_children > 0)
1192         {
1193           extra = size / nexpand_children;
1194           n_extra_widgets = size % nexpand_children;
1195         }
1196       else
1197         extra = 0;
1198     }
1199
1200   /* Allocate child positions. */
1201   for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
1202     {
1203       for (i = 0, children = private->children;
1204            children;
1205            children = children->next)
1206         {
1207           child = children->data;
1208
1209           /* If widget is not visible, skip it. */
1210           if (!gtk_widget_get_visible (child->widget))
1211             continue;
1212
1213           /* If widget is packed differently skip it, but still increment i,
1214            * since widget is visible and will be handled in next loop iteration.
1215            */
1216           if (child->pack != packing)
1217             {
1218               i++;
1219               continue;
1220             }
1221
1222           if (child->pack == packing)
1223             {
1224               /* Assign the child's size. */
1225               if (private->homogeneous)
1226                 {
1227                   child_size = extra;
1228
1229                   if (n_extra_widgets > 0)
1230                     {
1231                       child_size++;
1232                       n_extra_widgets--;
1233                     }
1234                 }
1235               else
1236                 {
1237                   child_size = sizes[i].minimum_size + child->padding * 2;
1238
1239                   if (child->expand || gtk_widget_compute_expand (child->widget, private->orientation))
1240                     {
1241                       child_size += extra;
1242
1243                       if (n_extra_widgets > 0)
1244                         {
1245                           child_size++;
1246                           n_extra_widgets--;
1247                         }
1248                     }
1249                 }
1250
1251               if (child->fill)
1252                 {
1253                   child_size = MAX (1, child_size - child->padding * 2);
1254                 }
1255               else
1256                 {
1257                   child_size = sizes[i].minimum_size;
1258                 }
1259
1260
1261               /* Assign the child's position. */
1262               if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1263                 gtk_widget_get_preferred_height_for_width (child->widget,
1264                                                            child_size, &child_minimum, &child_natural);
1265               else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1266                 gtk_widget_get_preferred_width_for_height (child->widget,
1267                                                            child_size, &child_minimum, &child_natural);
1268
1269
1270               computed_minimum = MAX (computed_minimum, child_minimum);
1271               computed_natural = MAX (computed_natural, child_natural);
1272             }
1273           i += 1;
1274         }
1275     }
1276
1277   if (minimum_size)
1278     *minimum_size = computed_minimum;
1279   if (natural_size)
1280     *natural_size = computed_natural;
1281 }
1282
1283 static void
1284 gtk_box_compute_size_for_orientation (GtkBox *box,
1285                                       gint    avail_size,
1286                                       gint   *minimum_size,
1287                                       gint   *natural_size)
1288 {
1289   GtkBoxPrivate    *private = box->priv;
1290   GList         *children;
1291   gint           nvis_children = 0;
1292   gint           required_size = 0, required_natural = 0, child_size, child_natural;
1293   gint           largest_child = 0, largest_natural = 0;
1294
1295   for (children = private->children; children != NULL;
1296        children = children->next)
1297     {
1298       GtkBoxChild *child = children->data;
1299
1300       if (gtk_widget_get_visible (child->widget))
1301         {
1302
1303           if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1304             gtk_widget_get_preferred_width_for_height (child->widget,
1305                                                        avail_size, &child_size, &child_natural);
1306           else
1307             gtk_widget_get_preferred_height_for_width (child->widget,
1308                                                        avail_size, &child_size, &child_natural);
1309
1310
1311           child_size    += child->padding * 2;
1312           child_natural += child->padding * 2;
1313
1314           if (child_size > largest_child)
1315             largest_child = child_size;
1316
1317           if (child_natural > largest_natural)
1318             largest_natural = child_natural;
1319
1320           required_size    += child_size;
1321           required_natural += child_natural;
1322
1323           nvis_children += 1;
1324         }
1325     }
1326
1327   if (nvis_children > 0)
1328     {
1329       if (private->homogeneous)
1330         {
1331           required_size    = largest_child   * nvis_children;
1332           required_natural = largest_natural * nvis_children;
1333         }
1334
1335       required_size     += (nvis_children - 1) * private->spacing;
1336       required_natural  += (nvis_children - 1) * private->spacing;
1337     }
1338
1339   if (minimum_size)
1340     *minimum_size = required_size;
1341
1342   if (natural_size)
1343     *natural_size = required_natural;
1344 }
1345
1346 static void
1347 gtk_box_get_preferred_width_for_height (GtkWidget *widget,
1348                                         gint       height,
1349                                         gint      *minimum_width,
1350                                         gint      *natural_width)
1351 {
1352   GtkBox        *box     = GTK_BOX (widget);
1353   GtkBoxPrivate *private = box->priv;
1354
1355   if (private->orientation == GTK_ORIENTATION_VERTICAL)
1356     gtk_box_compute_size_for_opposing_orientation (box, height, minimum_width, natural_width);
1357   else
1358     gtk_box_compute_size_for_orientation (box, height, minimum_width, natural_width);
1359 }
1360
1361 static void
1362 gtk_box_get_preferred_height_for_width (GtkWidget *widget,
1363                                         gint       width,
1364                                         gint      *minimum_height,
1365                                         gint      *natural_height)
1366 {
1367   GtkBox        *box     = GTK_BOX (widget);
1368   GtkBoxPrivate *private = box->priv;
1369
1370   if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1371     gtk_box_compute_size_for_opposing_orientation (box, width, minimum_height, natural_height);
1372   else
1373     gtk_box_compute_size_for_orientation (box, width, minimum_height, natural_height);
1374 }
1375
1376 /**
1377  * gtk_box_new:
1378  * @orientation: the box's orientation.
1379  * @spacing: the number of pixels to place by default between children.
1380  *
1381  * Creates a new #GtkBox.
1382  *
1383  * Return value: a new #GtkBox.
1384  *
1385  * Since: 3.0
1386  **/
1387 GtkWidget*
1388 gtk_box_new (GtkOrientation orientation,
1389              gint           spacing)
1390 {
1391   return g_object_new (GTK_TYPE_BOX,
1392                        "orientation", orientation,
1393                        "spacing",     spacing,
1394                        NULL);
1395 }
1396
1397 /**
1398  * gtk_box_pack_start:
1399  * @box: a #GtkBox
1400  * @child: the #GtkWidget to be added to @box
1401  * @expand: %TRUE if the new child is to be given extra space allocated
1402  *     to @box. The extra space will be divided evenly between all children
1403  *     that use this option
1404  * @fill: %TRUE if space given to @child by the @expand option is
1405  *     actually allocated to @child, rather than just padding it.  This
1406  *     parameter has no effect if @expand is set to %FALSE.  A child is
1407  *     always allocated the full height of a horizontal #GtkBox and the full width
1408  *     of a vertical #GtkBox. This option affects the other dimension
1409  * @padding: extra space in pixels to put between this child and its
1410  *   neighbors, over and above the global amount specified by
1411  *   #GtkBox:spacing property.  If @child is a widget at one of the
1412  *   reference ends of @box, then @padding pixels are also put between
1413  *   @child and the reference edge of @box
1414  *
1415  * Adds @child to @box, packed with reference to the start of @box.
1416  * The @child is packed after any other child packed with reference
1417  * to the start of @box.
1418  */
1419 void
1420 gtk_box_pack_start (GtkBox    *box,
1421                     GtkWidget *child,
1422                     gboolean   expand,
1423                     gboolean   fill,
1424                     guint      padding)
1425 {
1426   gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_START);
1427 }
1428
1429 /**
1430  * gtk_box_pack_end:
1431  * @box: a #GtkBox
1432  * @child: the #GtkWidget to be added to @box
1433  * @expand: %TRUE if the new child is to be given extra space allocated
1434  *   to @box. The extra space will be divided evenly between all children
1435  *   of @box that use this option
1436  * @fill: %TRUE if space given to @child by the @expand option is
1437  *   actually allocated to @child, rather than just padding it.  This
1438  *   parameter has no effect if @expand is set to %FALSE.  A child is
1439  *   always allocated the full height of a horizontal #GtkBox and the full width
1440  *   of a vertical #GtkBox.  This option affects the other dimension
1441  * @padding: extra space in pixels to put between this child and its
1442  *   neighbors, over and above the global amount specified by
1443  *   #GtkBox:spacing property.  If @child is a widget at one of the
1444  *   reference ends of @box, then @padding pixels are also put between
1445  *   @child and the reference edge of @box
1446  *
1447  * Adds @child to @box, packed with reference to the end of @box.
1448  * The @child is packed after (away from end of) any other child
1449  * packed with reference to the end of @box.
1450  */
1451 void
1452 gtk_box_pack_end (GtkBox    *box,
1453                   GtkWidget *child,
1454                   gboolean   expand,
1455                   gboolean   fill,
1456                   guint      padding)
1457 {
1458   gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_END);
1459 }
1460
1461 /**
1462  * gtk_box_set_homogeneous:
1463  * @box: a #GtkBox
1464  * @homogeneous: a boolean value, %TRUE to create equal allotments,
1465  *   %FALSE for variable allotments
1466  *
1467  * Sets the #GtkBox:homogeneous property of @box, controlling
1468  * whether or not all children of @box are given equal space
1469  * in the box.
1470  */
1471 void
1472 gtk_box_set_homogeneous (GtkBox  *box,
1473                          gboolean homogeneous)
1474 {
1475   GtkBoxPrivate *private;
1476
1477   g_return_if_fail (GTK_IS_BOX (box));
1478
1479   private = box->priv;
1480
1481   if ((homogeneous ? TRUE : FALSE) != private->homogeneous)
1482     {
1483       private->homogeneous = homogeneous ? TRUE : FALSE;
1484       g_object_notify (G_OBJECT (box), "homogeneous");
1485       gtk_widget_queue_resize (GTK_WIDGET (box));
1486     }
1487 }
1488
1489 /**
1490  * gtk_box_get_homogeneous:
1491  * @box: a #GtkBox
1492  *
1493  * Returns whether the box is homogeneous (all children are the
1494  * same size). See gtk_box_set_homogeneous().
1495  *
1496  * Return value: %TRUE if the box is homogeneous.
1497  **/
1498 gboolean
1499 gtk_box_get_homogeneous (GtkBox *box)
1500 {
1501   g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1502
1503   return box->priv->homogeneous;
1504 }
1505
1506 /**
1507  * gtk_box_set_spacing:
1508  * @box: a #GtkBox
1509  * @spacing: the number of pixels to put between children
1510  *
1511  * Sets the #GtkBox:spacing property of @box, which is the
1512  * number of pixels to place between children of @box.
1513  */
1514 void
1515 gtk_box_set_spacing (GtkBox *box,
1516                      gint    spacing)
1517 {
1518   GtkBoxPrivate *private;
1519
1520   g_return_if_fail (GTK_IS_BOX (box));
1521
1522   private = box->priv;
1523
1524   if (spacing != private->spacing)
1525     {
1526       private->spacing = spacing;
1527       _gtk_box_set_spacing_set (box, TRUE);
1528
1529       g_object_notify (G_OBJECT (box), "spacing");
1530
1531       gtk_widget_queue_resize (GTK_WIDGET (box));
1532     }
1533 }
1534
1535 /**
1536  * gtk_box_get_spacing:
1537  * @box: a #GtkBox
1538  *
1539  * Gets the value set by gtk_box_set_spacing().
1540  *
1541  * Return value: spacing between children
1542  **/
1543 gint
1544 gtk_box_get_spacing (GtkBox *box)
1545 {
1546   g_return_val_if_fail (GTK_IS_BOX (box), 0);
1547
1548   return box->priv->spacing;
1549 }
1550
1551 void
1552 _gtk_box_set_spacing_set (GtkBox  *box,
1553                           gboolean spacing_set)
1554 {
1555   GtkBoxPrivate *private;
1556
1557   g_return_if_fail (GTK_IS_BOX (box));
1558
1559   private = box->priv;
1560
1561   private->spacing_set = spacing_set ? TRUE : FALSE;
1562 }
1563
1564 gboolean
1565 _gtk_box_get_spacing_set (GtkBox *box)
1566 {
1567   GtkBoxPrivate *private;
1568
1569   g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1570
1571   private = box->priv;
1572
1573   return private->spacing_set;
1574 }
1575
1576 /**
1577  * gtk_box_reorder_child:
1578  * @box: a #GtkBox
1579  * @child: the #GtkWidget to move
1580  * @position: the new position for @child in the list of children
1581  *   of @box, starting from 0. If negative, indicates the end of
1582  *   the list
1583  *
1584  * Moves @child to a new @position in the list of @box children.
1585  * The list is the <structfield>children</structfield> field of
1586  * #GtkBox-struct, and contains both widgets packed #GTK_PACK_START
1587  * as well as widgets packed #GTK_PACK_END, in the order that these
1588  * widgets were added to @box.
1589  *
1590  * A widget's position in the @box children list determines where
1591  * the widget is packed into @box.  A child widget at some position
1592  * in the list will be packed just after all other widgets of the
1593  * same packing type that appear earlier in the list.
1594  */
1595 void
1596 gtk_box_reorder_child (GtkBox    *box,
1597                        GtkWidget *child,
1598                        gint       position)
1599 {
1600   GtkBoxPrivate *priv;
1601   GList *old_link;
1602   GList *new_link;
1603   GtkBoxChild *child_info = NULL;
1604   gint old_position;
1605
1606   g_return_if_fail (GTK_IS_BOX (box));
1607   g_return_if_fail (GTK_IS_WIDGET (child));
1608
1609   priv = box->priv;
1610
1611   old_link = priv->children;
1612   old_position = 0;
1613   while (old_link)
1614     {
1615       child_info = old_link->data;
1616       if (child_info->widget == child)
1617         break;
1618
1619       old_link = old_link->next;
1620       old_position++;
1621     }
1622
1623   g_return_if_fail (old_link != NULL);
1624
1625   if (position == old_position)
1626     return;
1627
1628   priv->children = g_list_delete_link (priv->children, old_link);
1629
1630   if (position < 0)
1631     new_link = NULL;
1632   else
1633     new_link = g_list_nth (priv->children, position);
1634
1635   priv->children = g_list_insert_before (priv->children, new_link, child_info);
1636
1637   gtk_widget_child_notify (child, "position");
1638   if (gtk_widget_get_visible (child)
1639       && gtk_widget_get_visible (GTK_WIDGET (box)))
1640     {
1641       gtk_box_invalidate_order (box);
1642       gtk_widget_queue_resize (child);
1643     }
1644 }
1645
1646 /**
1647  * gtk_box_query_child_packing:
1648  * @box: a #GtkBox
1649  * @child: the #GtkWidget of the child to query
1650  * @expand: (out): pointer to return location for #GtkBox:expand child
1651  *     property
1652  * @fill: (out): pointer to return location for #GtkBox:fill child
1653  *     property
1654  * @padding: (out): pointer to return location for #GtkBox:padding
1655  *     child property
1656  * @pack_type: (out): pointer to return location for #GtkBox:pack-type
1657  *     child property
1658  *
1659  * Obtains information about how @child is packed into @box.
1660  */
1661 void
1662 gtk_box_query_child_packing (GtkBox      *box,
1663                              GtkWidget   *child,
1664                              gboolean    *expand,
1665                              gboolean    *fill,
1666                              guint       *padding,
1667                              GtkPackType *pack_type)
1668 {
1669   GtkBoxPrivate *private;
1670   GList *list;
1671   GtkBoxChild *child_info = NULL;
1672
1673   g_return_if_fail (GTK_IS_BOX (box));
1674   g_return_if_fail (GTK_IS_WIDGET (child));
1675
1676   private = box->priv;
1677
1678   list = private->children;
1679   while (list)
1680     {
1681       child_info = list->data;
1682       if (child_info->widget == child)
1683         break;
1684
1685       list = list->next;
1686     }
1687
1688   if (list)
1689     {
1690       if (expand)
1691         *expand = child_info->expand;
1692       if (fill)
1693         *fill = child_info->fill;
1694       if (padding)
1695         *padding = child_info->padding;
1696       if (pack_type)
1697         *pack_type = child_info->pack;
1698     }
1699 }
1700
1701 /**
1702  * gtk_box_set_child_packing:
1703  * @box: a #GtkBox
1704  * @child: the #GtkWidget of the child to set
1705  * @expand: the new value of the #GtkBox:expand child property
1706  * @fill: the new value of the #GtkBox:fill child property
1707  * @padding: the new value of the #GtkBox:padding child property
1708  * @pack_type: the new value of the #GtkBox:pack-type child property
1709  *
1710  * Sets the way @child is packed into @box.
1711  */
1712 void
1713 gtk_box_set_child_packing (GtkBox      *box,
1714                            GtkWidget   *child,
1715                            gboolean     expand,
1716                            gboolean     fill,
1717                            guint        padding,
1718                            GtkPackType  pack_type)
1719 {
1720   GtkBoxPrivate *private;
1721   GList *list;
1722   GtkBoxChild *child_info = NULL;
1723
1724   g_return_if_fail (GTK_IS_BOX (box));
1725   g_return_if_fail (GTK_IS_WIDGET (child));
1726
1727   private = box->priv;
1728
1729   list = private->children;
1730   while (list)
1731     {
1732       child_info = list->data;
1733       if (child_info->widget == child)
1734         break;
1735
1736       list = list->next;
1737     }
1738
1739   gtk_widget_freeze_child_notify (child);
1740   if (list)
1741     {
1742       gboolean expanded;
1743
1744       expanded = expand != FALSE;
1745
1746       /* avoid setting expand if unchanged, since queue_compute_expand
1747        * can be expensive-ish
1748        */
1749       if (child_info->expand != expanded)
1750         {
1751           child_info->expand = expand != FALSE;
1752           gtk_widget_queue_compute_expand (GTK_WIDGET (box));
1753           gtk_widget_child_notify (child, "expand");
1754         }
1755
1756       child_info->fill = fill != FALSE;
1757       gtk_widget_child_notify (child, "fill");
1758       child_info->padding = padding;
1759       gtk_widget_child_notify (child, "padding");
1760       if (pack_type != GTK_PACK_END)
1761         pack_type = GTK_PACK_START;
1762       if (child_info->pack != pack_type)
1763         {
1764           child_info->pack = GTK_PACK_END;
1765           gtk_widget_child_notify (child, "pack-type");
1766           gtk_box_invalidate_order (box);
1767         }
1768
1769       if (gtk_widget_get_visible (child)
1770           && gtk_widget_get_visible (GTK_WIDGET (box)))
1771         gtk_widget_queue_resize (child);
1772     }
1773   gtk_widget_thaw_child_notify (child);
1774 }
1775
1776 void
1777 _gtk_box_set_old_defaults (GtkBox *box)
1778 {
1779   GtkBoxPrivate *private;
1780
1781   g_return_if_fail (GTK_IS_BOX (box));
1782
1783   private = box->priv;
1784
1785   private->default_expand = TRUE;
1786 }
1787
1788 static void
1789 gtk_box_add (GtkContainer *container,
1790              GtkWidget    *widget)
1791 {
1792   GtkBoxPrivate *priv = GTK_BOX (container)->priv;
1793
1794   gtk_box_pack_start (GTK_BOX (container), widget,
1795                       priv->default_expand,
1796                       TRUE,
1797                       0);
1798 }
1799
1800 static void
1801 gtk_box_remove (GtkContainer *container,
1802                 GtkWidget    *widget)
1803 {
1804   GtkBox *box = GTK_BOX (container);
1805   GtkBoxPrivate *priv = box->priv;
1806   GtkBoxChild *child;
1807   GList *children;
1808
1809   children = priv->children;
1810   while (children)
1811     {
1812       child = children->data;
1813
1814       if (child->widget == widget)
1815         {
1816           gboolean was_visible;
1817
1818           g_signal_handlers_disconnect_by_func (widget,
1819                                                 box_child_visibility_notify_cb,
1820                                                 box);
1821
1822           was_visible = gtk_widget_get_visible (widget);
1823           gtk_widget_unparent (widget);
1824
1825           priv->children = g_list_remove_link (priv->children, children);
1826           g_list_free (children);
1827           g_free (child);
1828
1829           /* queue resize regardless of gtk_widget_get_visible (container),
1830            * since that's what is needed by toplevels.
1831            */
1832           if (was_visible)
1833             {
1834               gtk_box_invalidate_order (box);
1835               gtk_widget_queue_resize (GTK_WIDGET (container));
1836             }
1837
1838           break;
1839         }
1840
1841       children = children->next;
1842     }
1843 }
1844
1845 static void
1846 gtk_box_forall (GtkContainer *container,
1847                 gboolean      include_internals,
1848                 GtkCallback   callback,
1849                 gpointer      callback_data)
1850 {
1851   GtkBox *box = GTK_BOX (container);
1852   GtkBoxPrivate *priv = box->priv;
1853   GtkBoxChild *child;
1854   GList *children;
1855
1856   children = priv->children;
1857   while (children)
1858     {
1859       child = children->data;
1860       children = children->next;
1861
1862       if (child->pack == GTK_PACK_START)
1863         (* callback) (child->widget, callback_data);
1864     }
1865
1866   children = g_list_last (priv->children);
1867   while (children)
1868     {
1869       child = children->data;
1870       children = children->prev;
1871
1872       if (child->pack == GTK_PACK_END)
1873         (* callback) (child->widget, callback_data);
1874     }
1875 }
1876
1877 GList *
1878 _gtk_box_get_children (GtkBox *box)
1879 {
1880   GtkBoxPrivate *priv;
1881   GtkBoxChild *child;
1882   GList *children;
1883   GList *retval = NULL;
1884
1885   g_return_val_if_fail (GTK_IS_BOX (box), NULL);
1886
1887   priv = box->priv;
1888
1889   children = priv->children;
1890   while (children)
1891     {
1892       child = children->data;
1893       children = children->next;
1894
1895       retval = g_list_prepend (retval, child->widget);
1896     }
1897
1898   return g_list_reverse (retval);
1899 }