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