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