]> Pileus Git - ~andy/gtk/blob - gtk/gtkbox.c
Remove GtkObject completely
[~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 "gtktypeutils.h"
82 #include "gtkprivate.h"
83 #include "gtkintl.h"
84
85
86 enum {
87   PROP_0,
88   PROP_ORIENTATION,
89   PROP_SPACING,
90   PROP_HOMOGENEOUS
91 };
92
93 enum {
94   CHILD_PROP_0,
95   CHILD_PROP_EXPAND,
96   CHILD_PROP_FILL,
97   CHILD_PROP_PADDING,
98   CHILD_PROP_PACK_TYPE,
99   CHILD_PROP_POSITION
100 };
101
102 struct _GtkBoxPrivate
103 {
104   GtkOrientation  orientation;
105
106   GList          *children;
107
108   gint16          spacing;
109
110   guint           default_expand : 1;
111   guint           homogeneous    : 1;
112   guint           spacing_set    : 1;
113 };
114
115 typedef struct _GtkBoxChild        GtkBoxChild;
116
117 /*
118  * GtkBoxChild:
119  * @widget: the child widget, packed into the GtkBox.
120  * @padding: the number of extra pixels to put between this child and its
121  *  neighbors, set when packed, zero by default.
122  * @expand: flag indicates whether extra space should be given to this child.
123  *  Any extra space given to the parent GtkBox is divided up among all children
124  *  with this attribute set to %TRUE; set when packed, %TRUE by default.
125  * @fill: flag indicates whether any extra space given to this child due to its
126  *  @expand attribute being set is actually allocated to the child, rather than
127  *  being used as padding around the widget; set when packed, %TRUE by default.
128  * @pack: one of #GtkPackType indicating whether the child is packed with
129  *  reference to the start (top/left) or end (bottom/right) of the GtkBox.
130  */
131 struct _GtkBoxChild
132 {
133   GtkWidget *widget;
134
135   guint16    padding;
136
137   guint      expand : 1;
138   guint      fill   : 1;
139   guint      pack   : 1;
140 };
141
142 static void gtk_box_size_allocate         (GtkWidget              *widget,
143                                            GtkAllocation          *allocation);
144
145 static void gtk_box_set_property       (GObject        *object,
146                                         guint           prop_id,
147                                         const GValue   *value,
148                                         GParamSpec     *pspec);
149 static void gtk_box_get_property       (GObject        *object,
150                                         guint           prop_id,
151                                         GValue         *value,
152                                         GParamSpec     *pspec);
153
154 static void gtk_box_add                (GtkContainer   *container,
155                                         GtkWidget      *widget);
156 static void gtk_box_remove             (GtkContainer   *container,
157                                         GtkWidget      *widget);
158 static void gtk_box_forall             (GtkContainer   *container,
159                                         gboolean        include_internals,
160                                         GtkCallback     callback,
161                                         gpointer        callback_data);
162 static void gtk_box_set_child_property (GtkContainer   *container,
163                                         GtkWidget      *child,
164                                         guint           property_id,
165                                         const GValue   *value,
166                                         GParamSpec     *pspec);
167 static void gtk_box_get_child_property (GtkContainer   *container,
168                                         GtkWidget      *child,
169                                         guint           property_id,
170                                         GValue         *value,
171                                         GParamSpec     *pspec);
172 static GType gtk_box_child_type        (GtkContainer   *container);
173
174
175 static GtkSizeRequestMode gtk_box_get_request_mode               (GtkWidget           *widget);
176 static void               gtk_box_get_preferred_width            (GtkWidget           *widget,
177                                                                   gint                *minimum_size,
178                                                                   gint                *natural_size);
179 static void               gtk_box_get_preferred_height           (GtkWidget           *widget,
180                                                                   gint                *minimum_size,
181                                                                   gint                *natural_size);
182 static void               gtk_box_get_preferred_width_for_height (GtkWidget           *widget,
183                                                                   gint                 height,
184                                                                   gint                *minimum_width,
185                                                                   gint                *natural_width);
186 static void               gtk_box_get_preferred_height_for_width (GtkWidget           *widget,
187                                                                   gint                 width,
188                                                                   gint                *minimum_height,
189                                                                   gint                *natural_height);
190
191 G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
192                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
193                                                 NULL))
194
195 static void
196 gtk_box_class_init (GtkBoxClass *class)
197 {
198   GObjectClass *object_class = G_OBJECT_CLASS (class);
199   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
200   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
201
202   object_class->set_property = gtk_box_set_property;
203   object_class->get_property = gtk_box_get_property;
204
205   widget_class->size_allocate                  = gtk_box_size_allocate;
206   widget_class->get_request_mode               = gtk_box_get_request_mode;
207   widget_class->get_preferred_width            = gtk_box_get_preferred_width;
208   widget_class->get_preferred_height           = gtk_box_get_preferred_height;
209   widget_class->get_preferred_height_for_width = gtk_box_get_preferred_height_for_width;
210   widget_class->get_preferred_width_for_height = gtk_box_get_preferred_width_for_height;
211
212   container_class->add = gtk_box_add;
213   container_class->remove = gtk_box_remove;
214   container_class->forall = gtk_box_forall;
215   container_class->child_type = gtk_box_child_type;
216   container_class->set_child_property = gtk_box_set_child_property;
217   container_class->get_child_property = gtk_box_get_child_property;
218   gtk_container_class_handle_border_width (container_class);
219
220   g_object_class_override_property (object_class,
221                                     PROP_ORIENTATION,
222                                     "orientation");
223
224   g_object_class_install_property (object_class,
225                                    PROP_SPACING,
226                                    g_param_spec_int ("spacing",
227                                                      P_("Spacing"),
228                                                      P_("The amount of space between children"),
229                                                      0,
230                                                      G_MAXINT,
231                                                      0,
232                                                      GTK_PARAM_READWRITE));
233
234   g_object_class_install_property (object_class,
235                                    PROP_HOMOGENEOUS,
236                                    g_param_spec_boolean ("homogeneous",
237                                                          P_("Homogeneous"),
238                                                          P_("Whether the children should all be the same size"),
239                                                          FALSE,
240                                                          GTK_PARAM_READWRITE));
241
242   /**
243    * GtkBox:expand:
244    *
245    * Whether the child should receive extra space when the parent grows.
246    *
247    * Note that the default value for this property is %FALSE for GtkBox,
248    * but #GtkHBox, #GtkVBox and other subclasses use the old default
249    * of %TRUE.
250    */
251   gtk_container_class_install_child_property (container_class,
252                                               CHILD_PROP_EXPAND,
253                                               g_param_spec_boolean ("expand",
254                                                                     P_("Expand"),
255                                                                     P_("Whether the child should receive extra space when the parent grows"),
256                                                                     FALSE,
257                                                                     GTK_PARAM_READWRITE));
258
259   /**
260    * GtkBox:fill:
261    *
262    * Whether the child should receive extra space when the parent grows.
263    *
264    * Note that the default value for this property is %FALSE for GtkBox,
265    * but #GtkHBox, #GtkVBox and other subclasses use the old default
266    * of %TRUE.
267    */
268   gtk_container_class_install_child_property (container_class,
269                                               CHILD_PROP_FILL,
270                                               g_param_spec_boolean ("fill",
271                                                                     P_("Fill"),
272                                                                     P_("Whether extra space given to the child should be allocated to the child or used as padding"),
273                                                                     FALSE,
274                                                                     GTK_PARAM_READWRITE));
275
276   gtk_container_class_install_child_property (container_class,
277                                               CHILD_PROP_PADDING,
278                                               g_param_spec_uint ("padding",
279                                                                  P_("Padding"),
280                                                                  P_("Extra space to put between the child and its neighbors, in pixels"),
281                                                                  0, G_MAXINT, 0,
282                                                                  GTK_PARAM_READWRITE));
283   gtk_container_class_install_child_property (container_class,
284                                               CHILD_PROP_PACK_TYPE,
285                                               g_param_spec_enum ("pack-type",
286                                                                  P_("Pack type"), 
287                                                                  P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
288                                                                  GTK_TYPE_PACK_TYPE, GTK_PACK_START,
289                                                                  GTK_PARAM_READWRITE));
290   gtk_container_class_install_child_property (container_class,
291                                               CHILD_PROP_POSITION,
292                                               g_param_spec_int ("position", 
293                                                                 P_("Position"), 
294                                                                 P_("The index of the child in the parent"),
295                                                                 -1, G_MAXINT, 0,
296                                                                 GTK_PARAM_READWRITE));
297
298   g_type_class_add_private (object_class, sizeof (GtkBoxPrivate));
299 }
300
301 static void
302 gtk_box_init (GtkBox *box)
303 {
304   GtkBoxPrivate *private;
305
306   box->priv = G_TYPE_INSTANCE_GET_PRIVATE (box,
307                                            GTK_TYPE_BOX,
308                                            GtkBoxPrivate);
309   private = box->priv;
310
311   gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
312   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), FALSE);
313
314   private->orientation = GTK_ORIENTATION_HORIZONTAL;
315   private->children = NULL;
316
317   private->default_expand = FALSE;
318   private->homogeneous = FALSE;
319   private->spacing = 0;
320   private->spacing_set = FALSE;
321 }
322
323 static void
324 gtk_box_set_property (GObject      *object,
325                       guint         prop_id,
326                       const GValue *value,
327                       GParamSpec   *pspec)
328 {
329   GtkBox *box = GTK_BOX (object);
330   GtkBoxPrivate *private = box->priv;
331
332   switch (prop_id)
333     {
334     case PROP_ORIENTATION:
335       private->orientation = g_value_get_enum (value);
336       gtk_widget_queue_resize (GTK_WIDGET (box));
337       break;
338     case PROP_SPACING:
339       gtk_box_set_spacing (box, g_value_get_int (value));
340       break;
341     case PROP_HOMOGENEOUS:
342       gtk_box_set_homogeneous (box, g_value_get_boolean (value));
343       break;
344     default:
345       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
346       break;
347     }
348 }
349
350 static void
351 gtk_box_get_property (GObject    *object,
352                       guint       prop_id,
353                       GValue     *value,
354                       GParamSpec *pspec)
355 {
356   GtkBox *box = GTK_BOX (object);
357   GtkBoxPrivate *private = box->priv;
358
359   switch (prop_id)
360     {
361     case PROP_ORIENTATION:
362       g_value_set_enum (value, private->orientation);
363       break;
364     case PROP_SPACING:
365       g_value_set_int (value, private->spacing);
366       break;
367     case PROP_HOMOGENEOUS:
368       g_value_set_boolean (value, private->homogeneous);
369       break;
370     default:
371       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372       break;
373     }
374 }
375
376
377 static void
378 count_expand_children (GtkBox *box,
379                        gint *visible_children,
380                        gint *expand_children)
381 {
382   GtkBoxPrivate  *private = box->priv;
383   GList       *children;
384   GtkBoxChild *child;
385
386   *visible_children = *expand_children = 0;
387
388   for (children = private->children; children; children = children->next)
389     {
390       child = children->data;
391
392       if (gtk_widget_get_visible (child->widget))
393         {
394           *visible_children += 1;
395           if (child->expand)
396             *expand_children += 1;
397         }
398     }
399 }
400
401 static void
402 gtk_box_size_allocate (GtkWidget     *widget,
403                        GtkAllocation *allocation)
404 {
405   GtkBox *box = GTK_BOX (widget);
406   GtkBoxPrivate *private = box->priv;
407   GtkBoxChild *child;
408   GList *children;
409   gint nvis_children;
410   gint nexpand_children;
411
412   GtkTextDirection direction;
413   GtkAllocation child_allocation;
414   GtkRequestedSize *sizes;
415
416   GtkPackType packing;
417
418   gint size;
419   gint extra;
420   gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */
421   gint x = 0, y = 0, i;
422   gint child_size;
423
424
425   gtk_widget_set_allocation (widget, allocation);
426
427   count_expand_children (box, &nvis_children, &nexpand_children);
428
429   /* If there is no visible child, simply return. */
430   if (nvis_children <= 0)
431     return;
432
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 - (nvis_children - 1) * private->spacing;
438   else
439     size = allocation->height - (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_widget_get_preferred_width_for_height (child->widget,
451                                                    allocation->height,
452                                                    &sizes[i].minimum_size,
453                                                    &sizes[i].natural_size);
454       else
455         gtk_widget_get_preferred_height_for_width (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 - (nvis_children - 1) * private->spacing;
494       else
495         size = allocation->height - (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;
523           child_allocation.height = MAX (1, allocation->height);
524           if (packing == GTK_PACK_START)
525             x = allocation->x;
526           else
527             x = allocation->x + allocation->width;
528         }
529       else
530         {
531           child_allocation.x = allocation->x;
532           child_allocation.width = MAX (1, allocation->width);
533           if (packing == GTK_PACK_START)
534             y = allocation->y;
535           else
536             y = allocation->y + allocation->height;
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 GtkSizeRequestMode
807 gtk_box_get_request_mode  (GtkWidget       *widget)
808 {
809   GtkBoxPrivate *private = GTK_BOX (widget)->priv;
810
811   return (private->orientation == GTK_ORIENTATION_VERTICAL) ?
812     GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH : GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
813 }
814
815 static void
816 gtk_box_get_size (GtkWidget      *widget,
817                   GtkOrientation  orientation,
818                   gint           *minimum_size,
819                   gint           *natural_size)
820 {
821   GtkBox *box;
822   GtkBoxPrivate *private;
823   GList *children;
824   gint nvis_children;
825   gint minimum, natural;
826
827   box = GTK_BOX (widget);
828   private = box->priv;
829
830   minimum = natural = 0;
831
832   nvis_children = 0;
833
834   for (children = private->children; children; children = children->next)
835     {
836       GtkBoxChild *child = children->data;
837
838       if (gtk_widget_get_visible (child->widget))
839         {
840           gint child_minimum, child_natural;
841
842           if (orientation == GTK_ORIENTATION_HORIZONTAL)
843             gtk_widget_get_preferred_width (child->widget,
844                                             &child_minimum, &child_natural);
845           else
846             gtk_widget_get_preferred_height (child->widget,
847                                              &child_minimum, &child_natural);
848
849           if (private->orientation == orientation)
850             {
851               if (private->homogeneous)
852                 {
853                   gint largest;
854
855                   largest = child_minimum + child->padding * 2;
856                   minimum = MAX (minimum, largest);
857
858                   largest = child_natural + child->padding * 2;
859                   natural = MAX (natural, largest);
860                 }
861               else
862                 {
863                   minimum += child_minimum + child->padding * 2;
864                   natural += child_natural + child->padding * 2;
865                 }
866             }
867           else
868             {
869               /* The biggest mins and naturals in the opposing orientation */
870               minimum = MAX (minimum, child_minimum);
871               natural = MAX (natural, child_natural);
872             }
873
874           nvis_children += 1;
875         }
876     }
877
878   if (nvis_children > 0 && private->orientation == orientation)
879     {
880       if (private->homogeneous)
881         {
882           minimum *= nvis_children;
883           natural *= nvis_children;
884         }
885       minimum += (nvis_children - 1) * private->spacing;
886       natural += (nvis_children - 1) * private->spacing;
887     }
888
889   if (minimum_size)
890     *minimum_size = minimum;
891
892   if (natural_size)
893     *natural_size = natural;
894 }
895
896 static void
897 gtk_box_get_preferred_width (GtkWidget *widget,
898                              gint      *minimum_size,
899                              gint      *natural_size)
900 {
901   gtk_box_get_size (widget, GTK_ORIENTATION_HORIZONTAL, minimum_size, natural_size);
902 }
903
904 static void
905 gtk_box_get_preferred_height (GtkWidget *widget,
906                               gint      *minimum_size,
907                               gint      *natural_size)
908 {
909   gtk_box_get_size (widget, GTK_ORIENTATION_VERTICAL, minimum_size, natural_size);
910 }
911
912 static void 
913 gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
914                                                gint    avail_size,
915                                                gint   *minimum_size,
916                                                gint   *natural_size)
917 {
918   GtkBoxPrivate       *private = box->priv;
919   GtkBoxChild      *child;
920   GList            *children;
921   gint              nvis_children;
922   gint              nexpand_children;
923   gint              computed_minimum = 0, computed_natural = 0;
924   GtkRequestedSize *sizes;
925   GtkPackType       packing;
926   gint              size, extra, i;
927   gint              child_size, child_minimum, child_natural;
928   gint              n_extra_widgets = 0;
929
930   count_expand_children (box, &nvis_children, &nexpand_children);
931
932   if (nvis_children <= 0)
933     return;
934
935   sizes = g_newa (GtkRequestedSize, nvis_children);
936   size = avail_size - (nvis_children - 1) * private->spacing;
937
938   /* Retrieve desired size for visible children */
939   for (i = 0, children = private->children; children; children = children->next)
940     {
941       child = children->data;
942           
943       if (gtk_widget_get_visible (child->widget))
944         {
945           if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
946             gtk_widget_get_preferred_width (child->widget,
947                                             &sizes[i].minimum_size,
948                                             &sizes[i].natural_size);
949           else
950             gtk_widget_get_preferred_height (child->widget,
951                                              &sizes[i].minimum_size,
952                                              &sizes[i].natural_size);
953
954           /* Assert the api is working properly */
955           if (sizes[i].minimum_size < 0)
956             g_error ("GtkBox child %s minimum %s: %d < 0",
957                      gtk_widget_get_name (GTK_WIDGET (child->widget)),
958                      (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
959                      sizes[i].minimum_size);
960
961           if (sizes[i].natural_size < sizes[i].minimum_size)
962             g_error ("GtkBox child %s natural %s: %d < minimum %d",
963                      gtk_widget_get_name (GTK_WIDGET (child->widget)),
964                      (private->orientation == GTK_ORIENTATION_HORIZONTAL) ? "width" : "height",
965                      sizes[i].natural_size,
966                      sizes[i].minimum_size);
967
968           size -= sizes[i].minimum_size;
969           size -= child->padding * 2;
970
971           sizes[i].data = child;
972
973           i += 1;
974         }
975     }
976
977   if (private->homogeneous)
978     {
979       /* If were homogenous we still need to run the above loop to get the
980        * minimum sizes for children that are not going to fill
981        */
982       size = avail_size - (nvis_children - 1) * private->spacing;
983       extra = size / nvis_children;
984       n_extra_widgets = size % nvis_children;
985     }
986   else
987     {
988       /* Bring children up to size first */
989       size = gtk_distribute_natural_allocation (MAX (0, size), nvis_children, sizes);
990
991       /* Calculate space which hasn't distributed yet,
992        * and is available for expanding children.
993        */
994       if (nexpand_children > 0)
995         {
996           extra = size / nexpand_children;
997           n_extra_widgets = size % nexpand_children;
998         }
999       else
1000         extra = 0;
1001     }
1002
1003   /* Allocate child positions. */
1004   for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
1005     {
1006       for (i = 0, children = private->children;
1007            children;
1008            children = children->next)
1009         {
1010           child = children->data;
1011
1012           /* If widget is not visible, skip it. */
1013           if (!gtk_widget_get_visible (child->widget))
1014             continue;
1015
1016           /* If widget is packed differently skip it, but still increment i,
1017            * since widget is visible and will be handled in next loop iteration.
1018            */
1019           if (child->pack != packing)
1020             {
1021               i++;
1022               continue;
1023             }
1024
1025           if (child->pack == packing)
1026             {
1027               /* Assign the child's size. */
1028               if (private->homogeneous)
1029                 {
1030                   child_size = extra;
1031
1032                   if (n_extra_widgets > 0)
1033                     {
1034                       child_size++;
1035                       n_extra_widgets--;
1036                     }
1037                 }
1038               else
1039                 {
1040                   child_size = sizes[i].minimum_size + child->padding * 2;
1041
1042                   if (child->expand)
1043                     {
1044                       child_size += extra;
1045
1046                       if (n_extra_widgets > 0)
1047                         {
1048                           child_size++;
1049                           n_extra_widgets--;
1050                         }
1051                     }
1052                 }
1053
1054               if (child->fill)
1055                 {
1056                   child_size = MAX (1, child_size - child->padding * 2);
1057                 }
1058               else
1059                 {
1060                   child_size = sizes[i].minimum_size;
1061                 }
1062
1063
1064               /* Assign the child's position. */
1065               if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1066                 gtk_widget_get_preferred_height_for_width (child->widget,
1067                                                            child_size, &child_minimum, &child_natural);
1068               else /* (private->orientation == GTK_ORIENTATION_VERTICAL) */
1069                 gtk_widget_get_preferred_width_for_height (child->widget,
1070                                                            child_size, &child_minimum, &child_natural);
1071
1072
1073               computed_minimum = MAX (computed_minimum, child_minimum);
1074               computed_natural = MAX (computed_natural, child_natural);
1075             }
1076           i += 1;
1077         }
1078     }
1079
1080   if (minimum_size)
1081     *minimum_size = computed_minimum;
1082   if (natural_size)
1083     *natural_size = computed_natural;
1084 }
1085
1086 static void
1087 gtk_box_compute_size_for_orientation (GtkBox *box,
1088                                       gint    avail_size,
1089                                       gint   *minimum_size,
1090                                       gint   *natural_size)
1091 {
1092   GtkBoxPrivate    *private = box->priv;
1093   GList         *children;
1094   gint           nvis_children = 0;
1095   gint           required_size = 0, required_natural = 0, child_size, child_natural;
1096   gint           largest_child = 0, largest_natural = 0;
1097
1098   for (children = private->children; children != NULL;
1099        children = children->next, nvis_children++)
1100     {
1101       GtkBoxChild *child = children->data;
1102
1103       if (gtk_widget_get_visible (child->widget))
1104         {
1105
1106           if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1107             gtk_widget_get_preferred_width_for_height (child->widget,
1108                                                        avail_size, &child_size, &child_natural);
1109           else
1110             gtk_widget_get_preferred_height_for_width (child->widget,
1111                                                        avail_size, &child_size, &child_natural);
1112
1113
1114           child_size    += child->padding * 2;
1115           child_natural += child->padding * 2;
1116
1117           if (child_size > largest_child)
1118             largest_child = child_size;
1119
1120           if (child_natural > largest_natural)
1121             largest_natural = child_natural;
1122
1123           required_size    += child_size;
1124           required_natural += child_natural;
1125         }
1126     }
1127
1128   if (nvis_children > 0)
1129     {
1130       if (private->homogeneous)
1131         {
1132           required_size    = largest_child   * nvis_children;
1133           required_natural = largest_natural * nvis_children;
1134         }
1135
1136       required_size     += (nvis_children - 1) * private->spacing;
1137       required_natural  += (nvis_children - 1) * private->spacing;
1138     }
1139
1140   if (minimum_size)
1141     *minimum_size = required_size;
1142
1143   if (natural_size)
1144     *natural_size = required_natural;
1145 }
1146
1147 static void
1148 gtk_box_get_preferred_width_for_height (GtkWidget *widget,
1149                                         gint       height,
1150                                         gint      *minimum_width,
1151                                         gint      *natural_width)
1152 {
1153   GtkBox        *box     = GTK_BOX (widget);
1154   GtkBoxPrivate *private = box->priv;
1155
1156   if (private->orientation == GTK_ORIENTATION_VERTICAL)
1157     gtk_box_compute_size_for_opposing_orientation (box, height, minimum_width, natural_width);
1158   else
1159     gtk_box_compute_size_for_orientation (box, height, minimum_width, natural_width);
1160 }
1161
1162 static void
1163 gtk_box_get_preferred_height_for_width (GtkWidget *widget,
1164                                         gint       width,
1165                                         gint      *minimum_height,
1166                                         gint      *natural_height)
1167 {
1168   GtkBox        *box     = GTK_BOX (widget);
1169   GtkBoxPrivate *private = box->priv;
1170
1171   if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
1172     gtk_box_compute_size_for_opposing_orientation (box, width, minimum_height, natural_height);
1173   else
1174     gtk_box_compute_size_for_orientation (box, width, minimum_height, natural_height);
1175 }
1176
1177 /**
1178  * gtk_box_new:
1179  * @orientation: the box' orientation.
1180  * @homogeneous: %TRUE if all children are to be given equal space allocations.
1181  * @spacing: the number of pixels to place by default between children.
1182  *
1183  * Creates a new #GtkBox.
1184  *
1185  * Return value: a new #GtkBox.
1186  *
1187  * Since: 3.0
1188  **/
1189 GtkWidget*
1190 gtk_box_new (GtkOrientation orientation,
1191              gboolean       homogeneous,
1192              gint           spacing)
1193 {
1194   return g_object_new (GTK_TYPE_BOX,
1195                        "orientation", orientation,
1196                        "spacing",     spacing,
1197                        "homogeneous", homogeneous ? TRUE : FALSE,
1198                        NULL);
1199 }
1200
1201 /**
1202  * gtk_box_pack_start:
1203  * @box: a #GtkBox
1204  * @child: the #GtkWidget to be added to @box
1205  * @expand: %TRUE if the new child is to be given extra space allocated to
1206  * @box.  The extra space will be divided evenly between all children of
1207  * @box that use this option
1208  * @fill: %TRUE if space given to @child by the @expand option is
1209  *   actually allocated to @child, rather than just padding it.  This
1210  *   parameter has no effect if @expand is set to %FALSE.  A child is
1211  *   always allocated the full height of a #GtkHBox and the full width 
1212  *   of a #GtkVBox. This option affects the other dimension
1213  * @padding: extra space in pixels to put between this child and its
1214  *   neighbors, over and above the global amount specified by
1215  *   #GtkBox:spacing property.  If @child is a widget at one of the 
1216  *   reference ends of @box, then @padding pixels are also put between 
1217  *   @child and the reference edge of @box
1218  *
1219  * Adds @child to @box, packed with reference to the start of @box.
1220  * The @child is packed after any other child packed with reference 
1221  * to the start of @box.
1222  */
1223 void
1224 gtk_box_pack_start (GtkBox    *box,
1225                     GtkWidget *child,
1226                     gboolean   expand,
1227                     gboolean   fill,
1228                     guint      padding)
1229 {
1230   gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_START);
1231 }
1232
1233 /**
1234  * gtk_box_pack_end:
1235  * @box: a #GtkBox
1236  * @child: the #GtkWidget to be added to @box
1237  * @expand: %TRUE if the new child is to be given extra space allocated 
1238  *   to @box. The extra space will be divided evenly between all children 
1239  *   of @box that use this option
1240  * @fill: %TRUE if space given to @child by the @expand option is
1241  *   actually allocated to @child, rather than just padding it.  This
1242  *   parameter has no effect if @expand is set to %FALSE.  A child is
1243  *   always allocated the full height of a #GtkHBox and the full width 
1244  *   of a #GtkVBox.  This option affects the other dimension
1245  * @padding: extra space in pixels to put between this child and its
1246  *   neighbors, over and above the global amount specified by
1247  *   #GtkBox:spacing property.  If @child is a widget at one of the 
1248  *   reference ends of @box, then @padding pixels are also put between 
1249  *   @child and the reference edge of @box
1250  *
1251  * Adds @child to @box, packed with reference to the end of @box.  
1252  * The @child is packed after (away from end of) any other child 
1253  * packed with reference to the end of @box.
1254  */
1255 void
1256 gtk_box_pack_end (GtkBox    *box,
1257                   GtkWidget *child,
1258                   gboolean   expand,
1259                   gboolean   fill,
1260                   guint      padding)
1261 {
1262   gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_END);
1263 }
1264
1265 /**
1266  * gtk_box_set_homogeneous:
1267  * @box: a #GtkBox
1268  * @homogeneous: a boolean value, %TRUE to create equal allotments,
1269  *   %FALSE for variable allotments
1270  * 
1271  * Sets the #GtkBox:homogeneous property of @box, controlling 
1272  * whether or not all children of @box are given equal space 
1273  * in the box.
1274  */
1275 void
1276 gtk_box_set_homogeneous (GtkBox  *box,
1277                          gboolean homogeneous)
1278 {
1279   GtkBoxPrivate *private;
1280
1281   g_return_if_fail (GTK_IS_BOX (box));
1282
1283   private = box->priv;
1284
1285   if ((homogeneous ? TRUE : FALSE) != private->homogeneous)
1286     {
1287       private->homogeneous = homogeneous ? TRUE : FALSE;
1288       g_object_notify (G_OBJECT (box), "homogeneous");
1289       gtk_widget_queue_resize (GTK_WIDGET (box));
1290     }
1291 }
1292
1293 /**
1294  * gtk_box_get_homogeneous:
1295  * @box: a #GtkBox
1296  *
1297  * Returns whether the box is homogeneous (all children are the
1298  * same size). See gtk_box_set_homogeneous().
1299  *
1300  * Return value: %TRUE if the box is homogeneous.
1301  **/
1302 gboolean
1303 gtk_box_get_homogeneous (GtkBox *box)
1304 {
1305   g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1306
1307   return box->priv->homogeneous;
1308 }
1309
1310 /**
1311  * gtk_box_set_spacing:
1312  * @box: a #GtkBox
1313  * @spacing: the number of pixels to put between children
1314  *
1315  * Sets the #GtkBox:spacing property of @box, which is the 
1316  * number of pixels to place between children of @box.
1317  */
1318 void
1319 gtk_box_set_spacing (GtkBox *box,
1320                      gint    spacing)
1321 {
1322   GtkBoxPrivate *private;
1323
1324   g_return_if_fail (GTK_IS_BOX (box));
1325
1326   private = box->priv;
1327
1328   if (spacing != private->spacing)
1329     {
1330       private->spacing = spacing;
1331       _gtk_box_set_spacing_set (box, TRUE);
1332
1333       g_object_notify (G_OBJECT (box), "spacing");
1334
1335       gtk_widget_queue_resize (GTK_WIDGET (box));
1336     }
1337 }
1338
1339 /**
1340  * gtk_box_get_spacing:
1341  * @box: a #GtkBox
1342  * 
1343  * Gets the value set by gtk_box_set_spacing().
1344  * 
1345  * Return value: spacing between children
1346  **/
1347 gint
1348 gtk_box_get_spacing (GtkBox *box)
1349 {
1350   g_return_val_if_fail (GTK_IS_BOX (box), 0);
1351
1352   return box->priv->spacing;
1353 }
1354
1355 void
1356 _gtk_box_set_spacing_set (GtkBox  *box,
1357                           gboolean spacing_set)
1358 {
1359   GtkBoxPrivate *private;
1360
1361   g_return_if_fail (GTK_IS_BOX (box));
1362
1363   private = box->priv;
1364
1365   private->spacing_set = spacing_set ? TRUE : FALSE;
1366 }
1367
1368 gboolean
1369 _gtk_box_get_spacing_set (GtkBox *box)
1370 {
1371   GtkBoxPrivate *private;
1372
1373   g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1374
1375   private = box->priv;
1376
1377   return private->spacing_set;
1378 }
1379
1380 /**
1381  * gtk_box_reorder_child:
1382  * @box: a #GtkBox
1383  * @child: the #GtkWidget to move
1384  * @position: the new position for @child in the list of children 
1385  *   of @box, starting from 0. If negative, indicates the end of 
1386  *   the list
1387  *
1388  * Moves @child to a new @position in the list of @box children.  
1389  * The list is the <structfield>children</structfield> field of
1390  * #GtkBox-struct, and contains both widgets packed #GTK_PACK_START 
1391  * as well as widgets packed #GTK_PACK_END, in the order that these 
1392  * widgets were added to @box.
1393  * 
1394  * A widget's position in the @box children list determines where 
1395  * the widget is packed into @box.  A child widget at some position 
1396  * in the list will be packed just after all other widgets of the 
1397  * same packing type that appear earlier in the list.
1398  */ 
1399 void
1400 gtk_box_reorder_child (GtkBox    *box,
1401                        GtkWidget *child,
1402                        gint       position)
1403 {
1404   GtkBoxPrivate *priv;
1405   GList *old_link;
1406   GList *new_link;
1407   GtkBoxChild *child_info = NULL;
1408   gint old_position;
1409
1410   g_return_if_fail (GTK_IS_BOX (box));
1411   g_return_if_fail (GTK_IS_WIDGET (child));
1412
1413   priv = box->priv;
1414
1415   old_link = priv->children;
1416   old_position = 0;
1417   while (old_link)
1418     {
1419       child_info = old_link->data;
1420       if (child_info->widget == child)
1421         break;
1422
1423       old_link = old_link->next;
1424       old_position++;
1425     }
1426
1427   g_return_if_fail (old_link != NULL);
1428
1429   if (position == old_position)
1430     return;
1431
1432   priv->children = g_list_delete_link (priv->children, old_link);
1433
1434   if (position < 0)
1435     new_link = NULL;
1436   else
1437     new_link = g_list_nth (priv->children, position);
1438
1439   priv->children = g_list_insert_before (priv->children, new_link, child_info);
1440
1441   gtk_widget_child_notify (child, "position");
1442   if (gtk_widget_get_visible (child)
1443       && gtk_widget_get_visible (GTK_WIDGET (box)))
1444     gtk_widget_queue_resize (child);
1445 }
1446
1447 /**
1448  * gtk_box_query_child_packing:
1449  * @box: a #GtkBox
1450  * @child: the #GtkWidget of the child to query
1451  * @expand: pointer to return location for #GtkBox:expand child property 
1452  * @fill: pointer to return location for #GtkBox:fill child property 
1453  * @padding: pointer to return location for #GtkBox:padding child property 
1454  * @pack_type: pointer to return location for #GtkBox:pack-type child property 
1455  * 
1456  * Obtains information about how @child is packed into @box.
1457  */
1458 void
1459 gtk_box_query_child_packing (GtkBox      *box,
1460                              GtkWidget   *child,
1461                              gboolean    *expand,
1462                              gboolean    *fill,
1463                              guint       *padding,
1464                              GtkPackType *pack_type)
1465 {
1466   GtkBoxPrivate *private;
1467   GList *list;
1468   GtkBoxChild *child_info = NULL;
1469
1470   g_return_if_fail (GTK_IS_BOX (box));
1471   g_return_if_fail (GTK_IS_WIDGET (child));
1472
1473   private = box->priv;
1474
1475   list = private->children;
1476   while (list)
1477     {
1478       child_info = list->data;
1479       if (child_info->widget == child)
1480         break;
1481
1482       list = list->next;
1483     }
1484
1485   if (list)
1486     {
1487       if (expand)
1488         *expand = child_info->expand;
1489       if (fill)
1490         *fill = child_info->fill;
1491       if (padding)
1492         *padding = child_info->padding;
1493       if (pack_type)
1494         *pack_type = child_info->pack;
1495     }
1496 }
1497
1498 /**
1499  * gtk_box_set_child_packing:
1500  * @box: a #GtkBox
1501  * @child: the #GtkWidget of the child to set
1502  * @expand: the new value of the #GtkBox:expand child property 
1503  * @fill: the new value of the #GtkBox:fill child property
1504  * @padding: the new value of the #GtkBox:padding child property
1505  * @pack_type: the new value of the #GtkBox:pack-type child property
1506  *
1507  * Sets the way @child is packed into @box.
1508  */
1509 void
1510 gtk_box_set_child_packing (GtkBox      *box,
1511                            GtkWidget   *child,
1512                            gboolean     expand,
1513                            gboolean     fill,
1514                            guint        padding,
1515                            GtkPackType  pack_type)
1516 {
1517   GtkBoxPrivate *private;
1518   GList *list;
1519   GtkBoxChild *child_info = NULL;
1520
1521   g_return_if_fail (GTK_IS_BOX (box));
1522   g_return_if_fail (GTK_IS_WIDGET (child));
1523
1524   private = box->priv;
1525
1526   list = private->children;
1527   while (list)
1528     {
1529       child_info = list->data;
1530       if (child_info->widget == child)
1531         break;
1532
1533       list = list->next;
1534     }
1535
1536   gtk_widget_freeze_child_notify (child);
1537   if (list)
1538     {
1539       child_info->expand = expand != FALSE;
1540       gtk_widget_child_notify (child, "expand");
1541       child_info->fill = fill != FALSE;
1542       gtk_widget_child_notify (child, "fill");
1543       child_info->padding = padding;
1544       gtk_widget_child_notify (child, "padding");
1545       if (pack_type == GTK_PACK_END)
1546         child_info->pack = GTK_PACK_END;
1547       else
1548         child_info->pack = GTK_PACK_START;
1549       gtk_widget_child_notify (child, "pack-type");
1550
1551       if (gtk_widget_get_visible (child)
1552           && gtk_widget_get_visible (GTK_WIDGET (box)))
1553         gtk_widget_queue_resize (child);
1554     }
1555   gtk_widget_thaw_child_notify (child);
1556 }
1557
1558 void
1559 _gtk_box_set_old_defaults (GtkBox *box)
1560 {
1561   GtkBoxPrivate *private;
1562
1563   g_return_if_fail (GTK_IS_BOX (box));
1564
1565   private = box->priv;
1566
1567   private->default_expand = TRUE;
1568 }
1569
1570 static void
1571 gtk_box_add (GtkContainer *container,
1572              GtkWidget    *widget)
1573 {
1574   GtkBoxPrivate *priv = GTK_BOX (container)->priv;
1575
1576   gtk_box_pack_start (GTK_BOX (container), widget,
1577                       priv->default_expand,
1578                       priv->default_expand,
1579                       0);
1580 }
1581
1582 static void
1583 gtk_box_remove (GtkContainer *container,
1584                 GtkWidget    *widget)
1585 {
1586   GtkBox *box = GTK_BOX (container);
1587   GtkBoxPrivate *priv = box->priv;
1588   GtkBoxChild *child;
1589   GList *children;
1590
1591   children = priv->children;
1592   while (children)
1593     {
1594       child = children->data;
1595
1596       if (child->widget == widget)
1597         {
1598           gboolean was_visible;
1599
1600           was_visible = gtk_widget_get_visible (widget);
1601           gtk_widget_unparent (widget);
1602
1603           priv->children = g_list_remove_link (priv->children, children);
1604           g_list_free (children);
1605           g_free (child);
1606
1607           /* queue resize regardless of gtk_widget_get_visible (container),
1608            * since that's what is needed by toplevels.
1609            */
1610           if (was_visible)
1611             gtk_widget_queue_resize (GTK_WIDGET (container));
1612
1613           break;
1614         }
1615
1616       children = children->next;
1617     }
1618 }
1619
1620 static void
1621 gtk_box_forall (GtkContainer *container,
1622                 gboolean      include_internals,
1623                 GtkCallback   callback,
1624                 gpointer      callback_data)
1625 {
1626   GtkBox *box = GTK_BOX (container);
1627   GtkBoxPrivate *priv = box->priv;
1628   GtkBoxChild *child;
1629   GList *children;
1630
1631   children = priv->children;
1632   while (children)
1633     {
1634       child = children->data;
1635       children = children->next;
1636
1637       if (child->pack == GTK_PACK_START)
1638         (* callback) (child->widget, callback_data);
1639     }
1640
1641   children = g_list_last (priv->children);
1642   while (children)
1643     {
1644       child = children->data;
1645       children = children->prev;
1646
1647       if (child->pack == GTK_PACK_END)
1648         (* callback) (child->widget, callback_data);
1649     }
1650 }
1651
1652 GList *
1653 _gtk_box_get_children (GtkBox *box)
1654 {
1655   GtkBoxPrivate *priv;
1656   GtkBoxChild *child;
1657   GList *children;
1658   GList *retval = NULL;
1659
1660   g_return_val_if_fail (GTK_IS_BOX (box), NULL);
1661
1662   priv = box->priv;
1663
1664   children = priv->children;
1665   while (children)
1666     {
1667       child = children->data;
1668       children = children->next;
1669
1670       retval = g_list_prepend (retval, child->widget);
1671     }
1672
1673   return g_list_reverse (retval);
1674 }