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