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