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