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