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