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