]> Pileus Git - ~andy/gtk/blob - gtk/gtkbox.c
native-layout: Introduce GtkExtendedLayout interface.
[~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_FLAGS (box, GTK_NO_WINDOW);
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_VISIBLE (child->widget))
306         {
307           GtkRequisition child_minimum_size;
308           GtkRequisition child_natural_size;
309
310           gtk_widget_get_desired_size (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_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
452       GtkBoxSpreading *spreading = g_newa (GtkBoxSpreading, nvis_children);
453       GtkBoxDesiredSizes *sizes = g_newa (GtkBoxDesiredSizes, nvis_children);
454
455       GtkPackType packing;
456
457       gint size;
458       gint extra;
459       gint x, y, i;
460       gint child_size;
461
462       if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
463         size = allocation->width - border_width * 2 - (nvis_children - 1) * box->spacing;
464       else
465         size = allocation->height - border_width * 2 - (nvis_children - 1) * box->spacing;
466
467       if (box->homogeneous)
468         {
469           extra = size / nvis_children;
470         }
471       else
472         {
473           /* Retrieve desired size for visible children */
474
475           i = 0;
476           children = box->children;
477           while (children)
478             {
479               child = children->data;
480               children = children->next;
481
482               if (GTK_WIDGET_VISIBLE (child->widget))
483                 {
484                   if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
485                     gtk_widget_get_width_for_height (child->widget,
486                                                      allocation->height,
487                                                      &sizes[i].minimum_size,
488                                                      &sizes[i].natural_size);
489                   else
490                     gtk_widget_get_height_for_width (child->widget,
491                                                      allocation->width,
492                                                      &sizes[i].minimum_size,
493                                                      &sizes[i].natural_size);
494
495                   size -= sizes[i].minimum_size;
496
497                   spreading[i].index = i;
498                   spreading[i].child = child;
499
500                   i += 1;
501                 }
502             }
503
504           /* Distribute the container's extra space c_gap. We want to assign
505            * this space such that the sum of extra space assigned to children
506            * (c^i_gap) is equal to c_cap. The case that there's not enough
507            * space for all children to take their natural size needs some
508            * attention. The goals we want to achieve are:
509            *
510            *   a) Maximize number of children taking their natural size.
511            *   b) The allocated size of children should be a continuous
512            *   function of c_gap.  That is, increasing the container size by
513            *   one pixel should never make drastic changes in the distribution.
514            *   c) If child i takes its natural size and child j doesn't,
515            *   child j should have received at least as much gap as child i.
516            *
517            * The following code distributes the additional space by following
518            * this rules.
519            */
520
521           /* Sort descending by gap and position. */
522
523           g_qsort_with_data (spreading,
524                              nvis_children, sizeof (GtkBoxSpreading),
525                              gtk_box_compare_gap, sizes);
526
527           /* Distribute available space.
528            * This master piece of a loop was conceived by Behdad Esfahbod.
529            */
530           for (i = nvis_children - 1; i >= 0; --i)
531             {
532               /* Divide remaining space by number of remaining children.
533                * Sort order and reducing remaining space by assigned space
534                * ensures that space is distributed equally.
535                */
536               gint glue = (size + i) / (i + 1);
537               gint gap = sizes[spreading[i].index].natural_size
538                        - sizes[spreading[i].index].minimum_size;
539
540               extra = MIN (glue, gap);
541               sizes[spreading[i].index].minimum_size += extra;
542
543               size -= extra;
544             }
545
546           /* Calculate space which hasn't distributed yet,
547            * and is available for expanding children.
548            */
549           if (nexpand_children > 0)
550             extra = size / nexpand_children;
551           else
552             extra = 0;
553         }
554
555       /* Allocate child positions. */
556
557       for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
558         {
559           if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
560             {
561               child_allocation.y = allocation->y + border_width;
562               child_allocation.height = MAX (1, allocation->height - border_width * 2);
563               if (packing == GTK_PACK_START)
564                 x = allocation->x + border_width;
565               else
566                 x = allocation->x + allocation->width - border_width;
567             }
568           else
569             {
570               child_allocation.x = allocation->x + border_width;
571               child_allocation.width = MAX (1, allocation->width - border_width * 2);
572               if (packing == GTK_PACK_START)
573                 y = allocation->y + border_width;
574               else
575                 y = allocation->y + allocation->height - border_width;
576             }
577
578           i = 0;
579           children = box->children;
580           while (children)
581             {
582               child = children->data;
583               children = children->next;
584
585               if (GTK_WIDGET_VISIBLE (child->widget))
586                 {
587                   if (child->pack == packing)
588                     {
589                       /* Assign the child's size. */
590
591                       if (box->homogeneous)
592                         {
593                           if (nvis_children == 1)
594                             child_size = size;
595                           else
596                             child_size = extra;
597
598                           nvis_children -= 1;
599                           size -= extra;
600                         }
601                       else
602                         {
603                           child_size = sizes[i].minimum_size + child->padding * 2;
604
605                           if (child->expand)
606                             {
607                               if (nexpand_children == 1)
608                                 child_size += size;
609                               else
610                                 child_size += extra;
611
612                               nexpand_children -= 1;
613                               size -= extra;
614                             }
615                         }
616
617                       /* Assign the child's position. */
618
619                       if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
620                         {
621                           if (child->fill)
622                             {
623                               child_allocation.width = MAX (1, child_size - child->padding * 2);
624                               child_allocation.x = x + child->padding;
625                             }
626                           else
627                             {
628                               child_allocation.width = sizes[i].minimum_size;
629                               child_allocation.x = x + (child_size - child_allocation.width) / 2;
630                             }
631
632                           if (direction == GTK_TEXT_DIR_RTL)
633                             child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
634
635                           if (packing == GTK_PACK_START)
636                             x += child_size + box->spacing;
637                           else
638                             x -= child_size + box->spacing;
639                         }
640                       else
641                         {
642                           if (child->fill)
643                             {
644                               child_allocation.height = MAX (1, child_size - child->padding * 2);
645                               child_allocation.y = y + child->padding;
646                             }
647                           else
648                             {
649                               child_allocation.height = sizes[i].minimum_size;
650                               child_allocation.y = y + (child_size - child_allocation.height) / 2;
651                             }
652
653                          if (packing == GTK_PACK_START)
654                            y += child_size + box->spacing;
655                          else
656                            y -= child_size + box->spacing;
657                         }
658
659                       gtk_widget_size_allocate (child->widget, &child_allocation);
660                     }
661                   i += 1;
662                 }
663             }
664         }
665     }
666 }
667
668 static GType
669 gtk_box_child_type (GtkContainer   *container)
670 {
671   return GTK_TYPE_WIDGET;
672 }
673
674 static void
675 gtk_box_set_child_property (GtkContainer *container,
676                             GtkWidget    *child,
677                             guint         property_id,
678                             const GValue *value,
679                             GParamSpec   *pspec)
680 {
681   gboolean expand = 0;
682   gboolean fill = 0;
683   guint padding = 0;
684   GtkPackType pack_type = 0;
685
686   if (property_id != CHILD_PROP_POSITION)
687     gtk_box_query_child_packing (GTK_BOX (container),
688                                  child,
689                                  &expand,
690                                  &fill,
691                                  &padding,
692                                  &pack_type);
693   switch (property_id)
694     {
695     case CHILD_PROP_EXPAND:
696       gtk_box_set_child_packing (GTK_BOX (container),
697                                  child,
698                                  g_value_get_boolean (value),
699                                  fill,
700                                  padding,
701                                  pack_type);
702       break;
703     case CHILD_PROP_FILL:
704       gtk_box_set_child_packing (GTK_BOX (container),
705                                  child,
706                                  expand,
707                                  g_value_get_boolean (value),
708                                  padding,
709                                  pack_type);
710       break;
711     case CHILD_PROP_PADDING:
712       gtk_box_set_child_packing (GTK_BOX (container),
713                                  child,
714                                  expand,
715                                  fill,
716                                  g_value_get_uint (value),
717                                  pack_type);
718       break;
719     case CHILD_PROP_PACK_TYPE:
720       gtk_box_set_child_packing (GTK_BOX (container),
721                                  child,
722                                  expand,
723                                  fill,
724                                  padding,
725                                  g_value_get_enum (value));
726       break;
727     case CHILD_PROP_POSITION:
728       gtk_box_reorder_child (GTK_BOX (container),
729                              child,
730                              g_value_get_int (value));
731       break;
732     default:
733       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
734       break;
735     }
736 }
737
738 static void
739 gtk_box_get_child_property (GtkContainer *container,
740                             GtkWidget    *child,
741                             guint         property_id,
742                             GValue       *value,
743                             GParamSpec   *pspec)
744 {
745   gboolean expand = 0;
746   gboolean fill = 0;
747   guint padding = 0;
748   GtkPackType pack_type = 0;
749   GList *list;
750
751   if (property_id != CHILD_PROP_POSITION)
752     gtk_box_query_child_packing (GTK_BOX (container),
753                                  child,
754                                  &expand,
755                                  &fill,
756                                  &padding,
757                                  &pack_type);
758   switch (property_id)
759     {
760       guint i;
761     case CHILD_PROP_EXPAND:
762       g_value_set_boolean (value, expand);
763       break;
764     case CHILD_PROP_FILL:
765       g_value_set_boolean (value, fill);
766       break;
767     case CHILD_PROP_PADDING:
768       g_value_set_uint (value, padding);
769       break;
770     case CHILD_PROP_PACK_TYPE:
771       g_value_set_enum (value, pack_type);
772       break;
773     case CHILD_PROP_POSITION:
774       i = 0;
775       for (list = GTK_BOX (container)->children; list; list = list->next)
776         {
777           GtkBoxChild *child_entry;
778
779           child_entry = list->data;
780           if (child_entry->widget == child)
781             break;
782           i++;
783         }
784       g_value_set_int (value, list ? i : -1);
785       break;
786     default:
787       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
788       break;
789     }
790 }
791
792 static void
793 gtk_box_pack (GtkBox      *box,
794               GtkWidget   *child,
795               gboolean     expand,
796               gboolean     fill,
797               guint        padding,
798               GtkPackType  pack_type)
799 {
800   GtkBoxChild *child_info;
801
802   g_return_if_fail (GTK_IS_BOX (box));
803   g_return_if_fail (GTK_IS_WIDGET (child));
804   g_return_if_fail (child->parent == NULL);
805
806   child_info = g_new (GtkBoxChild, 1);
807   child_info->widget = child;
808   child_info->padding = padding;
809   child_info->expand = expand ? TRUE : FALSE;
810   child_info->fill = fill ? TRUE : FALSE;
811   child_info->pack = pack_type;
812   child_info->is_secondary = FALSE;
813
814   box->children = g_list_append (box->children, child_info);
815
816   gtk_widget_freeze_child_notify (child);
817
818   gtk_widget_set_parent (child, GTK_WIDGET (box));
819   
820   gtk_widget_child_notify (child, "expand");
821   gtk_widget_child_notify (child, "fill");
822   gtk_widget_child_notify (child, "padding");
823   gtk_widget_child_notify (child, "pack-type");
824   gtk_widget_child_notify (child, "position");
825   gtk_widget_thaw_child_notify (child);
826 }
827
828 /**
829  * gtk_box_new:
830  * @orientation: the box' orientation.
831  * @homogeneous: %TRUE if all children are to be given equal space allocations.
832  * @spacing: the number of pixels to place by default between children.
833  *
834  * Creates a new #GtkHBox.
835  *
836  * Return value: a new #GtkHBox.
837  *
838  * Since: 2.16
839  **/
840 GtkWidget*
841 _gtk_box_new (GtkOrientation orientation,
842               gboolean       homogeneous,
843               gint           spacing)
844 {
845   return g_object_new (GTK_TYPE_BOX,
846                        "orientation", orientation,
847                        "spacing",     spacing,
848                        "homogeneous", homogeneous ? TRUE : FALSE,
849                        NULL);
850 }
851
852 /**
853  * gtk_box_pack_start:
854  * @box: a #GtkBox
855  * @child: the #GtkWidget to be added to @box
856  * @expand: %TRUE if the new child is to be given extra space allocated to
857  * @box.  The extra space will be divided evenly between all children of
858  * @box that use this option
859  * @fill: %TRUE if space given to @child by the @expand option is
860  *   actually allocated to @child, rather than just padding it.  This
861  *   parameter has no effect if @expand is set to %FALSE.  A child is
862  *   always allocated the full height of a #GtkHBox and the full width 
863  *   of a #GtkVBox. This option affects the other dimension
864  * @padding: extra space in pixels to put between this child and its
865  *   neighbors, over and above the global amount specified by
866  *   #GtkBox:spacing property.  If @child is a widget at one of the 
867  *   reference ends of @box, then @padding pixels are also put between 
868  *   @child and the reference edge of @box
869  *
870  * Adds @child to @box, packed with reference to the start of @box.
871  * The @child is packed after any other child packed with reference 
872  * to the start of @box.
873  */
874 void
875 gtk_box_pack_start (GtkBox    *box,
876                     GtkWidget *child,
877                     gboolean   expand,
878                     gboolean   fill,
879                     guint      padding)
880 {
881   gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_START);
882 }
883
884 /**
885  * gtk_box_pack_end:
886  * @box: a #GtkBox
887  * @child: the #GtkWidget to be added to @box
888  * @expand: %TRUE if the new child is to be given extra space allocated 
889  *   to @box. The extra space will be divided evenly between all children 
890  *   of @box that use this option
891  * @fill: %TRUE if space given to @child by the @expand option is
892  *   actually allocated to @child, rather than just padding it.  This
893  *   parameter has no effect if @expand is set to %FALSE.  A child is
894  *   always allocated the full height of a #GtkHBox and the full width 
895  *   of a #GtkVBox.  This option affects the other dimension
896  * @padding: extra space in pixels to put between this child and its
897  *   neighbors, over and above the global amount specified by
898  *   #GtkBox:spacing property.  If @child is a widget at one of the 
899  *   reference ends of @box, then @padding pixels are also put between 
900  *   @child and the reference edge of @box
901  *
902  * Adds @child to @box, packed with reference to the end of @box.  
903  * The @child is packed after (away from end of) any other child 
904  * packed with reference to the end of @box.
905  */
906 void
907 gtk_box_pack_end (GtkBox    *box,
908                   GtkWidget *child,
909                   gboolean   expand,
910                   gboolean   fill,
911                   guint      padding)
912 {
913   gtk_box_pack (box, child, expand, fill, padding, GTK_PACK_END);
914 }
915
916 /**
917  * gtk_box_pack_start_defaults:
918  * @box: a #GtkBox
919  * @widget: the #GtkWidget to be added to @box
920  *
921  * Adds @widget to @box, packed with reference to the start of @box.
922  * The child is packed after any other child packed with reference 
923  * to the start of @box. 
924  * 
925  * Parameters for how to pack the child @widget, #GtkBox:expand, 
926  * #GtkBox:fill and #GtkBox:padding, are given their default
927  * values, %TRUE, %TRUE, and 0, respectively.
928  *
929  * Deprecated: 2.14: Use gtk_box_pack_start()
930  */
931 void
932 gtk_box_pack_start_defaults (GtkBox    *box,
933                              GtkWidget *child)
934 {
935   gtk_box_pack_start (box, child, TRUE, TRUE, 0);
936 }
937
938 /**
939  * gtk_box_pack_end_defaults:
940  * @box: a #GtkBox
941  * @widget: the #GtkWidget to be added to @box
942  *
943  * Adds @widget to @box, packed with reference to the end of @box.
944  * The child is packed after any other child packed with reference 
945  * to the start of @box. 
946  * 
947  * Parameters for how to pack the child @widget, #GtkBox:expand, 
948  * #GtkBox:fill and #GtkBox:padding, are given their default
949  * values, %TRUE, %TRUE, and 0, respectively.
950  *
951  * Deprecated: 2.14: Use gtk_box_pack_end()
952  */
953 void
954 gtk_box_pack_end_defaults (GtkBox    *box,
955                            GtkWidget *child)
956 {
957   gtk_box_pack_end (box, child, TRUE, TRUE, 0);
958 }
959
960 /**
961  * gtk_box_set_homogeneous:
962  * @box: a #GtkBox
963  * @homogeneous: a boolean value, %TRUE to create equal allotments,
964  *   %FALSE for variable allotments
965  * 
966  * Sets the #GtkBox:homogeneous property of @box, controlling 
967  * whether or not all children of @box are given equal space 
968  * in the box.
969  */
970 void
971 gtk_box_set_homogeneous (GtkBox  *box,
972                          gboolean homogeneous)
973 {
974   g_return_if_fail (GTK_IS_BOX (box));
975
976   if ((homogeneous ? TRUE : FALSE) != box->homogeneous)
977     {
978       box->homogeneous = homogeneous ? TRUE : FALSE;
979       g_object_notify (G_OBJECT (box), "homogeneous");
980       gtk_widget_queue_resize (GTK_WIDGET (box));
981     }
982 }
983
984 /**
985  * gtk_box_get_homogeneous:
986  * @box: a #GtkBox
987  *
988  * Returns whether the box is homogeneous (all children are the
989  * same size). See gtk_box_set_homogeneous().
990  *
991  * Return value: %TRUE if the box is homogeneous.
992  **/
993 gboolean
994 gtk_box_get_homogeneous (GtkBox *box)
995 {
996   g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
997
998   return box->homogeneous;
999 }
1000
1001 /**
1002  * gtk_box_set_spacing:
1003  * @box: a #GtkBox
1004  * @spacing: the number of pixels to put between children
1005  *
1006  * Sets the #GtkBox:spacing property of @box, which is the 
1007  * number of pixels to place between children of @box.
1008  */
1009 void
1010 gtk_box_set_spacing (GtkBox *box,
1011                      gint    spacing)
1012 {
1013   g_return_if_fail (GTK_IS_BOX (box));
1014
1015   if (spacing != box->spacing)
1016     {
1017       box->spacing = spacing;
1018       _gtk_box_set_spacing_set (box, TRUE);
1019
1020       g_object_notify (G_OBJECT (box), "spacing");
1021
1022       gtk_widget_queue_resize (GTK_WIDGET (box));
1023     }
1024 }
1025
1026 /**
1027  * gtk_box_get_spacing:
1028  * @box: a #GtkBox
1029  * 
1030  * Gets the value set by gtk_box_set_spacing().
1031  * 
1032  * Return value: spacing between children
1033  **/
1034 gint
1035 gtk_box_get_spacing (GtkBox *box)
1036 {
1037   g_return_val_if_fail (GTK_IS_BOX (box), 0);
1038
1039   return box->spacing;
1040 }
1041
1042 void
1043 _gtk_box_set_spacing_set (GtkBox  *box,
1044                           gboolean spacing_set)
1045 {
1046   GtkBoxPrivate *private;
1047
1048   g_return_if_fail (GTK_IS_BOX (box));
1049
1050   private = GTK_BOX_GET_PRIVATE (box);
1051
1052   private->spacing_set = spacing_set ? TRUE : FALSE;
1053 }
1054
1055 gboolean
1056 _gtk_box_get_spacing_set (GtkBox *box)
1057 {
1058   GtkBoxPrivate *private;
1059
1060   g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
1061
1062   private = GTK_BOX_GET_PRIVATE (box);
1063
1064   return private->spacing_set;
1065 }
1066
1067 /**
1068  * gtk_box_reorder_child:
1069  * @box: a #GtkBox
1070  * @child: the #GtkWidget to move
1071  * @position: the new position for @child in the list of children 
1072  *   of @box, starting from 0. If negative, indicates the end of 
1073  *   the list
1074  *
1075  * Moves @child to a new @position in the list of @box children.  
1076  * The list is the <structfield>children</structfield> field of
1077  * #GtkBox-struct, and contains both widgets packed #GTK_PACK_START 
1078  * as well as widgets packed #GTK_PACK_END, in the order that these 
1079  * widgets were added to @box.
1080  * 
1081  * A widget's position in the @box children list determines where 
1082  * the widget is packed into @box.  A child widget at some position 
1083  * in the list will be packed just after all other widgets of the 
1084  * same packing type that appear earlier in the list.
1085  */ 
1086 void
1087 gtk_box_reorder_child (GtkBox    *box,
1088                        GtkWidget *child,
1089                        gint       position)
1090 {
1091   GList *old_link;
1092   GList *new_link;
1093   GtkBoxChild *child_info = NULL;
1094   gint old_position;
1095
1096   g_return_if_fail (GTK_IS_BOX (box));
1097   g_return_if_fail (GTK_IS_WIDGET (child));
1098
1099   old_link = box->children;
1100   old_position = 0;
1101   while (old_link)
1102     {
1103       child_info = old_link->data;
1104       if (child_info->widget == child)
1105         break;
1106
1107       old_link = old_link->next;
1108       old_position++;
1109     }
1110
1111   g_return_if_fail (old_link != NULL);
1112
1113   if (position == old_position)
1114     return;
1115
1116   box->children = g_list_delete_link (box->children, old_link);
1117
1118   if (position < 0)
1119     new_link = NULL;
1120   else
1121     new_link = g_list_nth (box->children, position);
1122
1123   box->children = g_list_insert_before (box->children, new_link, child_info);
1124
1125   gtk_widget_child_notify (child, "position");
1126   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (box))
1127     gtk_widget_queue_resize (child);
1128 }
1129
1130 /**
1131  * gtk_box_query_child_packing:
1132  * @box: a #GtkBox
1133  * @child: the #GtkWidget of the child to query
1134  * @expand: pointer to return location for #GtkBox:expand child property 
1135  * @fill: pointer to return location for #GtkBox:fill child property 
1136  * @padding: pointer to return location for #GtkBox:padding child property 
1137  * @pack_type: pointer to return location for #GtkBox:pack-type child property 
1138  * 
1139  * Obtains information about how @child is packed into @box.
1140  */
1141 void
1142 gtk_box_query_child_packing (GtkBox      *box,
1143                              GtkWidget   *child,
1144                              gboolean    *expand,
1145                              gboolean    *fill,
1146                              guint       *padding,
1147                              GtkPackType *pack_type)
1148 {
1149   GList *list;
1150   GtkBoxChild *child_info = NULL;
1151
1152   g_return_if_fail (GTK_IS_BOX (box));
1153   g_return_if_fail (GTK_IS_WIDGET (child));
1154
1155   list = box->children;
1156   while (list)
1157     {
1158       child_info = list->data;
1159       if (child_info->widget == child)
1160         break;
1161
1162       list = list->next;
1163     }
1164
1165   if (list)
1166     {
1167       if (expand)
1168         *expand = child_info->expand;
1169       if (fill)
1170         *fill = child_info->fill;
1171       if (padding)
1172         *padding = child_info->padding;
1173       if (pack_type)
1174         *pack_type = child_info->pack;
1175     }
1176 }
1177
1178 /**
1179  * gtk_box_set_child_packing:
1180  * @box: a #GtkBox
1181  * @child: the #GtkWidget of the child to set
1182  * @expand: the new value of the #GtkBox:expand child property 
1183  * @fill: the new value of the #GtkBox:fill child property
1184  * @padding: the new value of the #GtkBox:padding child property
1185  * @pack_type: the new value of the #GtkBox:pack-type child property
1186  *
1187  * Sets the way @child is packed into @box.
1188  */
1189 void
1190 gtk_box_set_child_packing (GtkBox      *box,
1191                            GtkWidget   *child,
1192                            gboolean     expand,
1193                            gboolean     fill,
1194                            guint        padding,
1195                            GtkPackType  pack_type)
1196 {
1197   GList *list;
1198   GtkBoxChild *child_info = NULL;
1199
1200   g_return_if_fail (GTK_IS_BOX (box));
1201   g_return_if_fail (GTK_IS_WIDGET (child));
1202
1203   list = box->children;
1204   while (list)
1205     {
1206       child_info = list->data;
1207       if (child_info->widget == child)
1208         break;
1209
1210       list = list->next;
1211     }
1212
1213   gtk_widget_freeze_child_notify (child);
1214   if (list)
1215     {
1216       child_info->expand = expand != FALSE;
1217       gtk_widget_child_notify (child, "expand");
1218       child_info->fill = fill != FALSE;
1219       gtk_widget_child_notify (child, "fill");
1220       child_info->padding = padding;
1221       gtk_widget_child_notify (child, "padding");
1222       if (pack_type == GTK_PACK_END)
1223         child_info->pack = GTK_PACK_END;
1224       else
1225         child_info->pack = GTK_PACK_START;
1226       gtk_widget_child_notify (child, "pack-type");
1227
1228       if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (box))
1229         gtk_widget_queue_resize (child);
1230     }
1231   gtk_widget_thaw_child_notify (child);
1232 }
1233
1234 void
1235 _gtk_box_set_old_defaults (GtkBox *box)
1236 {
1237   GtkBoxPrivate *private;
1238
1239   g_return_if_fail (GTK_IS_BOX (box));
1240
1241   private = GTK_BOX_GET_PRIVATE (box);
1242
1243   private->default_expand = TRUE;
1244 }
1245
1246 static void
1247 gtk_box_add (GtkContainer *container,
1248              GtkWidget    *widget)
1249 {
1250   GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (container);
1251
1252   gtk_box_pack_start (GTK_BOX (container), widget,
1253                       private->default_expand,
1254                       private->default_expand,
1255                       0);
1256 }
1257
1258 static void
1259 gtk_box_remove (GtkContainer *container,
1260                 GtkWidget    *widget)
1261 {
1262   GtkBox *box = GTK_BOX (container);
1263   GtkBoxChild *child;
1264   GList *children;
1265
1266   children = box->children;
1267   while (children)
1268     {
1269       child = children->data;
1270
1271       if (child->widget == widget)
1272         {
1273           gboolean was_visible;
1274
1275           was_visible = GTK_WIDGET_VISIBLE (widget);
1276           gtk_widget_unparent (widget);
1277
1278           box->children = g_list_remove_link (box->children, children);
1279           g_list_free (children);
1280           g_free (child);
1281
1282           /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
1283            * since that's what is needed by toplevels.
1284            */
1285           if (was_visible)
1286             gtk_widget_queue_resize (GTK_WIDGET (container));
1287
1288           break;
1289         }
1290
1291       children = children->next;
1292     }
1293 }
1294
1295 static void
1296 gtk_box_forall (GtkContainer *container,
1297                 gboolean      include_internals,
1298                 GtkCallback   callback,
1299                 gpointer      callback_data)
1300 {
1301   GtkBox *box = GTK_BOX (container);
1302   GtkBoxChild *child;
1303   GList *children;
1304
1305   children = box->children;
1306   while (children)
1307     {
1308       child = children->data;
1309       children = children->next;
1310
1311       if (child->pack == GTK_PACK_START)
1312         (* callback) (child->widget, callback_data);
1313     }
1314
1315   children = g_list_last (box->children);
1316   while (children)
1317     {
1318       child = children->data;
1319       children = children->prev;
1320
1321       if (child->pack == GTK_PACK_END)
1322         (* callback) (child->widget, callback_data);
1323     }
1324 }
1325
1326 #define __GTK_BOX_C__
1327 #include "gtkaliasdef.c"