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