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