]> Pileus Git - ~andy/gtk/blob - gtk/gtkwrapbox.c
Remove GtkObject completely
[~andy/gtk] / gtk / gtkwrapbox.c
1 /* gtkwrapbox.c
2  * Copyright (C) 2007-2010 Openismus GmbH
3  *
4  * Authors:
5  *      Tristan Van Berkom <tristanvb@openismus.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23
24 /**
25  * SECTION:gtkwrapbox
26  * @Short_Description: A container that wraps its children;
27  * @Title: GtkWrapBox
28  *
29  * #GtkWrapBox allocates space for an ordered list of children
30  * by wrapping them over in the box's orentation.
31  *
32  */
33
34 #include "config.h"
35 #include "gtksizerequest.h"
36 #include "gtkorientable.h"
37 #include "gtkwrapbox.h"
38 #include "gtkprivate.h"
39 #include "gtkintl.h"
40 #include "gtktypeutils.h"
41
42
43 typedef struct _GtkWrapBoxChild  GtkWrapBoxChild;
44
45 enum {
46   PROP_0,
47   PROP_ORIENTATION,
48   PROP_ALLOCATION_MODE,
49   PROP_HORIZONTAL_SPREADING,
50   PROP_VERTICAL_SPREADING,
51   PROP_HORIZONTAL_SPACING,
52   PROP_VERTICAL_SPACING,
53   PROP_MINIMUM_LINE_CHILDREN,
54   PROP_NATURAL_LINE_CHILDREN
55 };
56
57 enum
58 {
59   CHILD_PROP_0,
60   CHILD_PROP_PACKING
61 };
62
63 struct _GtkWrapBoxPrivate
64 {
65   GtkOrientation        orientation;
66   GtkWrapAllocationMode mode;
67   GtkWrapBoxSpreading   horizontal_spreading;
68   GtkWrapBoxSpreading   vertical_spreading;
69
70   guint16               vertical_spacing;
71   guint16               horizontal_spacing;
72
73   guint16               minimum_line_children;
74   guint16               natural_line_children;
75
76   GList                *children;
77 };
78
79 struct _GtkWrapBoxChild
80 {
81   GtkWidget        *widget;
82
83   GtkWrapBoxPacking packing;
84 };
85
86 /* GObjectClass */
87 static void gtk_wrap_box_get_property         (GObject             *object,
88                                                guint                prop_id,
89                                                GValue              *value,
90                                                GParamSpec          *pspec);
91 static void gtk_wrap_box_set_property         (GObject             *object,
92                                                guint                prop_id,
93                                                const GValue        *value,
94                                                GParamSpec          *pspec);
95
96 /* GtkWidgetClass */
97 static void gtk_wrap_box_size_allocate        (GtkWidget           *widget,
98                                                GtkAllocation       *allocation);
99
100 /* GtkContainerClass */
101 static void gtk_wrap_box_add                  (GtkContainer        *container,
102                                                GtkWidget           *widget);
103 static void gtk_wrap_box_remove               (GtkContainer        *container,
104                                                GtkWidget           *widget);
105 static void gtk_wrap_box_forall               (GtkContainer        *container,
106                                                gboolean             include_internals,
107                                                GtkCallback          callback,
108                                                gpointer             callback_data);
109 static void gtk_wrap_box_set_child_property   (GtkContainer        *container,
110                                                GtkWidget           *child,
111                                                guint                property_id,
112                                                const GValue        *value,
113                                                GParamSpec          *pspec);
114 static void gtk_wrap_box_get_child_property   (GtkContainer        *container,
115                                                GtkWidget           *child,
116                                                guint                property_id,
117                                                GValue              *value,
118                                                GParamSpec          *pspec);
119 static GType gtk_wrap_box_child_type          (GtkContainer        *container);
120
121
122 /* GtkWidget      */
123 static GtkSizeRequestMode gtk_wrap_box_get_request_mode (GtkWidget           *widget);
124 static void gtk_wrap_box_get_preferred_width            (GtkWidget           *widget,
125                                                          gint                *minimum_size,
126                                                          gint                *natural_size);
127 static void gtk_wrap_box_get_preferred_height           (GtkWidget           *widget,
128                                                          gint                *minimum_size,
129                                                          gint                *natural_size);
130 static void gtk_wrap_box_get_preferred_height_for_width (GtkWidget           *box,
131                                                          gint                 width,
132                                                          gint                *minimum_height,
133                                                          gint                *natural_height);
134 static void gtk_wrap_box_get_preferred_width_for_height (GtkWidget           *box,
135                                                          gint                 width,
136                                                          gint                *minimum_height,
137                                                          gint                *natural_height);
138
139
140 G_DEFINE_TYPE_WITH_CODE (GtkWrapBox, gtk_wrap_box, GTK_TYPE_CONTAINER,
141                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
142
143
144 #define ORIENTATION_SPREADING(box)                                      \
145   (((GtkWrapBox *)(box))->priv->orientation == GTK_ORIENTATION_HORIZONTAL ? \
146    ((GtkWrapBox *)(box))->priv->horizontal_spreading :                  \
147    ((GtkWrapBox *)(box))->priv->vertical_spreading)
148
149 #define OPPOSING_ORIENTATION_SPREADING(box)                             \
150   (((GtkWrapBox *)(box))->priv->orientation == GTK_ORIENTATION_HORIZONTAL ? \
151    ((GtkWrapBox *)(box))->priv->vertical_spreading :                    \
152    ((GtkWrapBox *)(box))->priv->horizontal_spreading)
153
154
155
156 static void
157 gtk_wrap_box_class_init (GtkWrapBoxClass *class)
158 {
159   GObjectClass      *gobject_class    = G_OBJECT_CLASS (class);
160   GtkWidgetClass    *widget_class     = GTK_WIDGET_CLASS (class);
161   GtkContainerClass *container_class  = GTK_CONTAINER_CLASS (class);
162
163   gobject_class->get_property         = gtk_wrap_box_get_property;
164   gobject_class->set_property         = gtk_wrap_box_set_property;
165
166   widget_class->size_allocate         = gtk_wrap_box_size_allocate;
167   widget_class->get_request_mode      = gtk_wrap_box_get_request_mode;
168   widget_class->get_preferred_width   = gtk_wrap_box_get_preferred_width;
169   widget_class->get_preferred_height  = gtk_wrap_box_get_preferred_height;
170   widget_class->get_preferred_height_for_width = gtk_wrap_box_get_preferred_height_for_width;
171   widget_class->get_preferred_width_for_height = gtk_wrap_box_get_preferred_width_for_height;
172
173   container_class->add                = gtk_wrap_box_add;
174   container_class->remove             = gtk_wrap_box_remove;
175   container_class->forall             = gtk_wrap_box_forall;
176   container_class->child_type         = gtk_wrap_box_child_type;
177   container_class->set_child_property = gtk_wrap_box_set_child_property;
178   container_class->get_child_property = gtk_wrap_box_get_child_property;
179   gtk_container_class_handle_border_width (container_class);
180
181   /* GObjectClass properties */
182   g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
183
184   /**
185    * GtkWrapBox:allocation-mode:
186    *
187    * The #GtkWrapAllocationMode to use.
188    */
189   g_object_class_install_property (gobject_class,
190                                    PROP_ALLOCATION_MODE,
191                                    g_param_spec_enum ("allocation-mode",
192                                                       P_("Allocation Mode"),
193                                                       P_("The allocation mode to use"),
194                                                       GTK_TYPE_WRAP_ALLOCATION_MODE,
195                                                       GTK_WRAP_ALLOCATE_FREE,
196                                                       GTK_PARAM_READWRITE));
197
198   /**
199    * GtkWrapBox:horizontal-spreading:
200    *
201    * The #GtkWrapBoxSpreading to used to define what is done with extra
202    * space in a given orientation.
203    */
204   g_object_class_install_property (gobject_class,
205                                    PROP_HORIZONTAL_SPREADING,
206                                    g_param_spec_enum ("horizontal-spreading",
207                                                       P_("Horizontal Spreading"),
208                                                       P_("The spreading mode to use horizontally"),
209                                                       GTK_TYPE_WRAP_BOX_SPREADING,
210                                                       GTK_WRAP_BOX_SPREAD_START,
211                                                       GTK_PARAM_READWRITE));
212
213   /**
214    * GtkWrapBox:vertical-spreading:
215    *
216    * The #GtkWrapBoxSpreading to used to define what is done with extra
217    * space in a given orientation.
218    */
219   g_object_class_install_property (gobject_class,
220                                    PROP_VERTICAL_SPREADING,
221                                    g_param_spec_enum ("vertical-spreading",
222                                                       P_("Vertical Spreading"),
223                                                       P_("The spreading mode to use vertically"),
224                                                       GTK_TYPE_WRAP_BOX_SPREADING,
225                                                       GTK_WRAP_BOX_SPREAD_START,
226                                                       GTK_PARAM_READWRITE));
227
228
229   /**
230    * GtkWrapBox:minimum-line-children:
231    *
232    * The minimum number of children to allocate consecutively in the given orientation.
233    *
234    * <note><para>Setting the minimum children per line ensures
235    * that a reasonably small height will be requested
236    * for the overall minimum width of the box.</para></note>
237    *
238    */
239   g_object_class_install_property (gobject_class,
240                                    PROP_MINIMUM_LINE_CHILDREN,
241                                    g_param_spec_uint ("minimum-line-children",
242                                                       P_("Minimum Line Children"),
243                                                       P_("The minimum number of children to allocate "
244                                                         "consecutively in the given orientation."),
245                                                       0,
246                                                       65535,
247                                                       0,
248                                                       GTK_PARAM_READWRITE));
249
250   /**
251    * GtkWrapBox:natural-line-children:
252    *
253    * The maximum amount of children to request space for consecutively in the given orientation.
254    *
255    */
256   g_object_class_install_property (gobject_class,
257                                    PROP_NATURAL_LINE_CHILDREN,
258                                    g_param_spec_uint ("natural-line-children",
259                                                       P_("Natural Line Children"),
260                                                       P_("The maximum amount of children to request space for "
261                                                         "consecutively in the given orientation."),
262                                                       0,
263                                                       65535,
264                                                       0,
265                                                       GTK_PARAM_READWRITE));
266
267   /**
268    * GtkWrapBox:vertical-spacing:
269    *
270    * The amount of vertical space between two children.
271    *
272    */
273   g_object_class_install_property (gobject_class,
274                                    PROP_VERTICAL_SPACING,
275                                    g_param_spec_uint ("vertical-spacing",
276                                                      P_("Vertical spacing"),
277                                                      P_("The amount of vertical space between two children"),
278                                                      0,
279                                                      65535,
280                                                      0,
281                                                      GTK_PARAM_READWRITE));
282
283   /**
284    * GtkWrapBox:horizontal-spacing:
285    *
286    * The amount of horizontal space between two children.
287    *
288    */
289   g_object_class_install_property (gobject_class,
290                                    PROP_HORIZONTAL_SPACING,
291                                    g_param_spec_uint ("horizontal-spacing",
292                                                      P_("Horizontal spacing"),
293                                                      P_("The amount of horizontal space between two children"),
294                                                      0,
295                                                      65535,
296                                                      0,
297                                                      GTK_PARAM_READWRITE));
298
299   /* GtkContainerClass child properties */
300
301   /**
302    * GtkWrapBox:packing:
303    *
304    * The #GtkWrapBoxPacking options to specify how to pack a child into the box.
305    */
306   gtk_container_class_install_child_property (container_class,
307                                               CHILD_PROP_PACKING,
308                                               g_param_spec_flags
309                                               ("packing",
310                                                P_("Packing"),
311                                                P_("The packing options to use for this child"),
312                                                GTK_TYPE_WRAP_BOX_PACKING, 0,
313                                                GTK_PARAM_READWRITE));
314
315   g_type_class_add_private (class, sizeof (GtkWrapBoxPrivate));
316 }
317
318 static void
319 gtk_wrap_box_init (GtkWrapBox *box)
320 {
321   GtkWrapBoxPrivate *priv;
322
323   box->priv = priv =
324     G_TYPE_INSTANCE_GET_PRIVATE (box, GTK_TYPE_WRAP_BOX, GtkWrapBoxPrivate);
325
326   priv->orientation          = GTK_ORIENTATION_HORIZONTAL;
327   priv->mode                 = GTK_WRAP_ALLOCATE_FREE;
328   priv->horizontal_spreading = GTK_WRAP_BOX_SPREAD_START;
329   priv->vertical_spreading   = GTK_WRAP_BOX_SPREAD_START;
330   priv->horizontal_spacing   = 0;
331   priv->vertical_spacing     = 0;
332   priv->children             = NULL;
333
334   gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
335 }
336
337 /*****************************************************
338  *                  GObectClass                      *
339  *****************************************************/
340 static void
341 gtk_wrap_box_get_property (GObject      *object,
342                            guint         prop_id,
343                            GValue       *value,
344                            GParamSpec   *pspec)
345 {
346   GtkWrapBox        *box  = GTK_WRAP_BOX (object);
347   GtkWrapBoxPrivate *priv = box->priv;
348
349   switch (prop_id)
350     {
351     case PROP_ORIENTATION:
352       g_value_set_boolean (value, priv->orientation);
353       break;
354     case PROP_ALLOCATION_MODE:
355       g_value_set_enum (value, priv->mode);
356       break;
357     case PROP_HORIZONTAL_SPREADING:
358       g_value_set_enum (value, priv->horizontal_spreading);
359       break;
360     case PROP_VERTICAL_SPREADING:
361       g_value_set_enum (value, priv->vertical_spreading);
362       break;
363     case PROP_HORIZONTAL_SPACING:
364       g_value_set_uint (value, priv->horizontal_spacing);
365       break;
366     case PROP_VERTICAL_SPACING:
367       g_value_set_uint (value, priv->vertical_spacing);
368       break;
369     case PROP_MINIMUM_LINE_CHILDREN:
370       g_value_set_uint (value, priv->minimum_line_children);
371       break;
372     case PROP_NATURAL_LINE_CHILDREN:
373       g_value_set_uint (value, priv->natural_line_children);
374       break;
375     default:
376       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
377       break;
378     }
379 }
380
381 static void
382 gtk_wrap_box_set_property (GObject      *object,
383                            guint         prop_id,
384                            const GValue *value,
385                            GParamSpec   *pspec)
386 {
387   GtkWrapBox        *box = GTK_WRAP_BOX (object);
388   GtkWrapBoxPrivate *priv   = box->priv;
389
390   switch (prop_id)
391     {
392     case PROP_ORIENTATION:
393       priv->orientation = g_value_get_enum (value);
394
395       /* Re-box the children in the new orientation */
396       gtk_widget_queue_resize (GTK_WIDGET (box));
397       break;
398     case PROP_ALLOCATION_MODE:
399       gtk_wrap_box_set_allocation_mode (box, g_value_get_enum (value));
400       break;
401     case PROP_HORIZONTAL_SPREADING:
402       gtk_wrap_box_set_horizontal_spreading (box, g_value_get_enum (value));
403       break;
404     case PROP_VERTICAL_SPREADING:
405       gtk_wrap_box_set_vertical_spreading (box, g_value_get_enum (value));
406       break;
407     case PROP_HORIZONTAL_SPACING:
408       gtk_wrap_box_set_horizontal_spacing (box, g_value_get_uint (value));
409       break;
410     case PROP_VERTICAL_SPACING:
411       gtk_wrap_box_set_vertical_spacing (box, g_value_get_uint (value));
412       break;
413     case PROP_MINIMUM_LINE_CHILDREN:
414       gtk_wrap_box_set_minimum_line_children (box, g_value_get_uint (value));
415       break;
416     case PROP_NATURAL_LINE_CHILDREN:
417       gtk_wrap_box_set_natural_line_children (box, g_value_get_uint (value));
418       break;
419     default:
420       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
421       break;
422     }
423 }
424
425 /*****************************************************
426  *                 GtkWidgetClass                    *
427  *****************************************************/
428
429 static gint
430 get_visible_children (GtkWrapBox  *box)
431 {
432   GtkWrapBoxPrivate *priv = box->priv;
433   GList             *list;
434   gint               i = 0;
435
436   for (list = priv->children; list; list = list->next)
437     {
438       GtkWrapBoxChild *child = list->data;
439
440       if (!gtk_widget_get_visible (child->widget))
441         continue;
442
443       i++;
444     }
445
446   return i;
447 }
448
449 static gint
450 get_visible_expand_children (GtkWrapBox     *box,
451                              GtkOrientation  orientation,
452                              GList          *cursor,
453                              gint            n_visible)
454 {
455   GList *list;
456   gint   i, expand_children = 0;
457
458   for (i = 0, list = cursor; (n_visible > 0 ? i < n_visible : TRUE) && list; list = list->next)
459     {
460       GtkWrapBoxChild *child = list->data;
461
462       if (!gtk_widget_get_visible (child->widget))
463         continue;
464
465       if ((orientation == GTK_ORIENTATION_HORIZONTAL && (child->packing & GTK_WRAP_BOX_H_EXPAND) != 0) ||
466           (orientation == GTK_ORIENTATION_VERTICAL   && (child->packing & GTK_WRAP_BOX_V_EXPAND) != 0))
467         expand_children++;
468
469       i++;
470     }
471
472   return expand_children;
473 }
474
475 /* Used in columned modes where all items share at least their
476  * equal widths or heights
477  */
478 static void
479 get_average_item_size (GtkWrapBox      *box,
480                        GtkOrientation   orientation,
481                        gint            *min_size,
482                        gint            *nat_size)
483 {
484   GtkWrapBoxPrivate *priv = box->priv;
485   GList             *list;
486   gint               max_min_size = 0;
487   gint               max_nat_size = 0;
488
489   for (list = priv->children; list; list = list->next)
490     {
491       GtkWrapBoxChild *child = list->data;
492       gint             child_min, child_nat;
493
494       if (!gtk_widget_get_visible (child->widget))
495         continue;
496
497       if (orientation == GTK_ORIENTATION_HORIZONTAL)
498         gtk_widget_get_preferred_width (child->widget, &child_min, &child_nat);
499       else
500         gtk_widget_get_preferred_height (child->widget, &child_min, &child_nat);
501
502       max_min_size = MAX (max_min_size, child_min);
503       max_nat_size = MAX (max_nat_size, child_nat);
504     }
505
506   if (min_size)
507     *min_size = max_min_size;
508
509   if (nat_size)
510     *nat_size = max_nat_size;
511 }
512
513
514 /* Gets the largest minimum/natural size for a given size
515  * (used to get the largest item heights for a fixed item width and the opposite) */
516 static void
517 get_largest_size_for_opposing_orientation (GtkWrapBox         *box,
518                                            GtkOrientation      orientation,
519                                            gint                item_size,
520                                            gint               *min_item_size,
521                                            gint               *nat_item_size)
522 {
523   GtkWrapBoxPrivate *priv = box->priv;
524   GList             *list;
525   gint               max_min_size = 0;
526   gint               max_nat_size = 0;
527
528   for (list = priv->children; list; list = list->next)
529     {
530       GtkWrapBoxChild *child = list->data;
531       gint             child_min, child_nat;
532
533       if (!gtk_widget_get_visible (child->widget))
534         continue;
535
536       if (orientation == GTK_ORIENTATION_HORIZONTAL)
537         gtk_widget_get_preferred_height_for_width (child->widget,
538                                                          item_size,
539                                                          &child_min, &child_nat);
540       else
541         gtk_widget_get_preferred_width_for_height (child->widget,
542                                                    item_size,
543                                                    &child_min, &child_nat);
544
545       max_min_size = MAX (max_min_size, child_min);
546       max_nat_size = MAX (max_nat_size, child_nat);
547     }
548
549   if (min_item_size)
550     *min_item_size = max_min_size;
551
552   if (nat_item_size)
553     *nat_item_size = max_nat_size;
554 }
555
556
557 /* Gets the largest minimum/natural size on a single line for a given size
558  * (used to get the largest line heights for a fixed item width and the opposite
559  * while itterating over a list of children, note the new index is returned) */
560 static GList *
561 get_largest_size_for_line_in_opposing_orientation (GtkWrapBox       *box,
562                                                    GtkOrientation    orientation,
563                                                    GList            *cursor,
564                                                    gint              line_length,
565                                                    GtkRequestedSize *item_sizes,
566                                                    gint              extra_pixels,
567                                                    gint             *min_item_size,
568                                                    gint             *nat_item_size)
569 {
570   GList  *list;
571   gint    max_min_size = 0;
572   gint    max_nat_size = 0;
573   gint    i;
574
575   for (list = cursor, i = 0; list && i < line_length; list = list->next)
576     {
577       GtkWrapBoxChild *child = list->data;
578       gint             child_min, child_nat, this_item_size;
579
580       if (!gtk_widget_get_visible (child->widget))
581         continue;
582
583       /* Distribute the extra pixels to the first children in the line
584        * (could be fancier and spread them out more evenly) */
585       this_item_size = item_sizes[i].minimum_size;
586       if (extra_pixels > 0 && ORIENTATION_SPREADING (box) == GTK_WRAP_BOX_SPREAD_EXPAND)
587         {
588           this_item_size++;
589           extra_pixels--;
590         }
591
592       if (orientation == GTK_ORIENTATION_HORIZONTAL)
593         gtk_widget_get_preferred_height_for_width (child->widget,
594                                                    this_item_size,
595                                                    &child_min, &child_nat);
596       else
597         gtk_widget_get_preferred_width_for_height (child->widget,
598                                                    this_item_size,
599                                                    &child_min, &child_nat);
600
601       max_min_size = MAX (max_min_size, child_min);
602       max_nat_size = MAX (max_nat_size, child_nat);
603
604       i++;
605     }
606
607   if (min_item_size)
608     *min_item_size = max_min_size;
609
610   if (nat_item_size)
611     *nat_item_size = max_nat_size;
612
613   /* Return next item in the list */
614   return list;
615 }
616
617
618 /* Gets the largest minimum/natural size on a single line for a given allocated line size
619  * (used to get the largest line heights for a width in pixels and the opposite
620  * while itterating over a list of children, note the new index is returned) */
621 static GList *
622 get_largest_size_for_free_line_in_opposing_orientation (GtkWrapBox      *box,
623                                                         GtkOrientation   orientation,
624                                                         GList           *cursor,
625                                                         gint             min_items,
626                                                         gint             avail_size,
627                                                         gint            *min_item_size,
628                                                         gint            *nat_item_size,
629                                                         gint            *extra_pixels,
630                                                         GArray         **ret_array)
631 {
632   GtkWrapBoxPrivate *priv = box->priv;
633   GtkRequestedSize  *sizes;
634   GList             *list;
635   GArray            *array;
636   gint               max_min_size = 0;
637   gint               max_nat_size = 0;
638   gint               i, size = avail_size;
639   gint               line_length, spacing;
640   gint               expand_children = 0;
641   gint               expand_per_child;
642   gint               expand_remainder;
643
644   if (orientation == GTK_ORIENTATION_HORIZONTAL)
645     spacing = priv->horizontal_spacing;
646   else
647     spacing = priv->vertical_spacing;
648
649   /* First determine the length of this line in items (how many items fit) */
650   for (i = 0, list = cursor; size > 0 && list; list = list->next)
651     {
652       GtkWrapBoxChild *child = list->data;
653       gint             child_size;
654
655       if (!gtk_widget_get_visible (child->widget))
656         continue;
657
658       if (orientation == GTK_ORIENTATION_HORIZONTAL)
659         gtk_widget_get_preferred_width (child->widget, NULL, &child_size);
660       else
661         gtk_widget_get_preferred_height (child->widget, NULL, &child_size);
662
663       if (i > 0)
664         child_size += spacing;
665
666       if (size - child_size >= 0)
667         size -= child_size;
668       else
669         break;
670
671       i++;
672     }
673
674   line_length = MAX (min_items, i);
675   size        = avail_size;
676
677   /* Collect the sizes of the items on this line */
678   array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
679
680   for (i = 0, list = cursor; i < line_length && list; list = list->next)
681     {
682       GtkWrapBoxChild  *child = list->data;
683       GtkRequestedSize  requested;
684
685       if (!gtk_widget_get_visible (child->widget))
686         continue;
687
688       requested.data = child;
689       if (orientation == GTK_ORIENTATION_HORIZONTAL)
690         gtk_widget_get_preferred_width (child->widget,
691                                         &requested.minimum_size,
692                                         &requested.natural_size);
693       else
694         gtk_widget_get_preferred_height (child->widget,
695                                          &requested.minimum_size,
696                                          &requested.natural_size);
697
698       if (i > 0)
699         size -= spacing;
700
701       size -= requested.minimum_size;
702
703       g_array_append_val (array, requested);
704
705       i++;
706     }
707
708   sizes = (GtkRequestedSize *)array->data;
709   size  = gtk_distribute_natural_allocation (size, array->len, sizes);
710
711   if (extra_pixels)
712     *extra_pixels = size;
713
714   /* Cut out any expand space if we're not distributing any */
715   if (ORIENTATION_SPREADING (box) != GTK_WRAP_BOX_SPREAD_EXPAND)
716     size = 0;
717
718   /* Count how many children are going to expand... */
719   expand_children = get_visible_expand_children (box, orientation,
720                                                  cursor, line_length);
721
722   /* If no child prefers to expand, they all get some expand space */
723   if (expand_children == 0)
724     {
725       expand_per_child = size / line_length;
726       expand_remainder = size % line_length;
727     }
728   else
729     {
730       expand_per_child = size / expand_children;
731       expand_remainder = size % expand_children;
732     }
733
734   /* Now add the remaining expand space and get the collective size of this line
735    * in the opposing orientation */
736   for (i = 0, list = cursor; i < line_length && list; list = list->next)
737     {
738       GtkWrapBoxChild *child = list->data;
739       gint child_min, child_nat;
740
741       if (!gtk_widget_get_visible (child->widget))
742         continue;
743
744       g_assert (child == sizes[i].data);
745
746       if ((orientation == GTK_ORIENTATION_HORIZONTAL && (child->packing & GTK_WRAP_BOX_H_EXPAND) != 0) ||
747           (orientation == GTK_ORIENTATION_VERTICAL   && (child->packing & GTK_WRAP_BOX_V_EXPAND) != 0) ||
748           expand_children == 0)
749         {
750           sizes[i].minimum_size += expand_per_child;
751           if (expand_remainder)
752             {
753               sizes[i].minimum_size++;
754               expand_remainder--;
755             }
756         }
757
758       if (orientation == GTK_ORIENTATION_HORIZONTAL)
759         gtk_widget_get_preferred_height_for_width (child->widget,
760                                                    sizes[i].minimum_size,
761                                                    &child_min, &child_nat);
762       else
763         gtk_widget_get_preferred_width_for_height (child->widget,
764                                                    sizes[i].minimum_size,
765                                                    &child_min, &child_nat);
766
767       max_min_size = MAX (max_min_size, child_min);
768       max_nat_size = MAX (max_nat_size, child_nat);
769
770       i++;
771     }
772
773   if (ret_array)
774     *ret_array = array;
775   else
776     g_array_free (array, TRUE);
777
778   if (min_item_size)
779     *min_item_size = max_min_size;
780
781   if (nat_item_size)
782     *nat_item_size = max_nat_size;
783
784   /* Return the next item */
785   return list;
786 }
787
788 static void
789 allocate_child (GtkWrapBox      *box,
790                 GtkWrapBoxChild *child,
791                 gint             item_offset,
792                 gint             line_offset,
793                 gint             item_size,
794                 gint             line_size)
795 {
796   GtkWrapBoxPrivate  *priv   = box->priv;
797   GtkAllocation       widget_allocation;
798   GtkAllocation       child_allocation;
799
800   gtk_widget_get_allocation (GTK_WIDGET (box), &widget_allocation);
801
802   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
803     {
804       child_allocation.x      = widget_allocation.x + item_offset;
805       child_allocation.y      = widget_allocation.y + line_offset;
806       child_allocation.width  = item_size;
807       child_allocation.height = line_size;
808     }
809   else /* GTK_ORIENTATION_VERTICAL */
810     {
811       child_allocation.x      = widget_allocation.x + line_offset;
812       child_allocation.y      = widget_allocation.y + item_offset;
813       child_allocation.width  = line_size;
814       child_allocation.height = item_size;
815     }
816
817   gtk_widget_size_allocate (child->widget, &child_allocation);
818 }
819
820 /* fit_aligned_item_requests() helper */
821 static gint
822 gather_aligned_item_requests (GtkWrapBox       *box,
823                               GtkOrientation    orientation,
824                               gint              line_length,
825                               gint              item_spacing,
826                               gint              n_children,
827                               GtkRequestedSize *item_sizes)
828 {
829   GtkWrapBoxPrivate *priv   = box->priv;
830   GList             *list;
831   gint               i;
832   gint               extra_items, natural_line_size = 0;
833
834   extra_items = n_children % line_length;
835
836   for (list = priv->children, i = 0; list; list = list->next, i++)
837     {
838       GtkWrapBoxChild *child = list->data;
839       gint             child_min, child_nat;
840       gint             position;
841
842       if (!gtk_widget_get_visible (child->widget))
843         continue;
844
845       if (orientation == GTK_ORIENTATION_HORIZONTAL)
846         gtk_widget_get_preferred_width (child->widget,
847                                         &child_min, &child_nat);
848       else
849         gtk_widget_get_preferred_height (child->widget,
850                                          &child_min, &child_nat);
851
852       /* Get the index and push it over for the last line when spreading to the end */
853       position = i % line_length;
854
855       if (ORIENTATION_SPREADING (box) == GTK_WRAP_BOX_SPREAD_END && i >= n_children - extra_items)
856         position += line_length - extra_items;
857
858       /* Round up the size of every column/row */
859       item_sizes[position].minimum_size = MAX (item_sizes[position].minimum_size, child_min);
860       item_sizes[position].natural_size = MAX (item_sizes[position].natural_size, child_nat);
861     }
862
863   for (i = 0; i < line_length; i++)
864     natural_line_size += item_sizes[i].natural_size;
865
866   natural_line_size += (line_length - 1) * item_spacing;
867
868   return natural_line_size;
869 }
870
871 static GtkRequestedSize *
872 fit_aligned_item_requests (GtkWrapBox       *box, 
873                            GtkOrientation    orientation, 
874                            gint              avail_size,
875                            gint              item_spacing,
876                            gint             *line_length, /* in-out */
877                            gint              n_children)
878 {
879   GtkRequestedSize  *sizes, *try_sizes;
880   gint               try_line_size, try_length;
881
882   sizes = g_new0 (GtkRequestedSize, *line_length);
883
884   /* get the sizes for the initial guess */
885   try_line_size = 
886     gather_aligned_item_requests (box, orientation, *line_length, item_spacing, n_children, sizes);
887
888   /* Try columnizing the whole thing and adding an item to the end of the line;
889    * try to fit as many columns into the available size as possible */
890   for (try_length = *line_length + 1; try_line_size < avail_size; try_length++)
891     {
892       try_sizes     = g_new0 (GtkRequestedSize, try_length);
893       try_line_size = gather_aligned_item_requests (box, orientation, try_length, item_spacing, 
894                                                     n_children, try_sizes);
895
896       if (try_line_size <= avail_size)
897         {
898           *line_length = try_length;
899
900           g_free (sizes);
901           sizes = try_sizes;
902         }
903       else
904         {
905           /* oops, this one failed; stick to the last size that fit and then return */
906           g_free (try_sizes);
907           break;
908         }
909     }
910
911   return sizes;
912 }
913
914
915 typedef struct {
916   GArray *requested;
917   gint    extra_pixels;
918 } AllocatedLine;
919
920 static void
921 gtk_wrap_box_size_allocate (GtkWidget     *widget,
922                             GtkAllocation *allocation)
923 {
924   GtkWrapBox         *box  = GTK_WRAP_BOX (widget);
925   GtkWrapBoxPrivate  *priv = box->priv;
926   gint                avail_size, avail_other_size, min_items, item_spacing, line_spacing;
927   GtkWrapBoxSpreading item_spreading;
928   GtkWrapBoxSpreading line_spreading;
929
930   gtk_widget_set_allocation (widget, allocation);
931
932   min_items = MAX (1, priv->minimum_line_children);
933
934   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
935     {
936       avail_size       = allocation->width;
937       avail_other_size = allocation->height;
938       item_spacing     = priv->horizontal_spacing;
939       line_spacing     = priv->vertical_spacing;
940     }
941   else /* GTK_ORIENTATION_VERTICAL */
942     {
943       avail_size       = allocation->height;
944       avail_other_size = allocation->width;
945       item_spacing     = priv->vertical_spacing;
946       line_spacing     = priv->horizontal_spacing;
947     }
948
949   item_spreading = ORIENTATION_SPREADING (box);
950   line_spreading    = OPPOSING_ORIENTATION_SPREADING (box);
951
952
953   /*********************************************************
954    * Deal with ALIGNED/HOMOGENEOUS modes first, start with * 
955    * initial guesses at item/line sizes                    *
956    *********************************************************/
957   if (priv->mode == GTK_WRAP_ALLOCATE_ALIGNED ||
958       priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
959     {
960       GtkRequestedSize *line_sizes = NULL;
961       GtkRequestedSize *item_sizes = NULL;
962       GList *list;
963       gint   min_item_size, nat_item_size;
964       gint   line_length;
965       gint   item_size = 0;
966       gint   line_size = 0, min_fixed_line_size = 0, nat_fixed_line_size = 0;
967       gint   line_offset, item_offset, n_children, n_lines, line_count;
968       gint   extra_pixels, extra_per_item = 0, extra_extra = 0;
969       gint   extra_line_pixels, extra_per_line = 0, extra_line_extra = 0;
970       gint   i, this_line_size;
971
972       get_average_item_size (box, priv->orientation, &min_item_size, &nat_item_size);
973
974       /* By default wrap at the natural item width */
975       line_length = avail_size / (nat_item_size + item_spacing);
976
977       /* After the above aproximation, check if we cant fit one more on the line */
978       if (line_length * item_spacing + (line_length + 1) * nat_item_size <= avail_size)
979         line_length++;
980
981       /* Its possible we were allocated just less than the natural width of the
982        * minimum item wrap length */
983       line_length = MAX (min_items, line_length);
984
985       /* Get how many lines we'll be needing to wrap */
986       n_children = get_visible_children (box);
987
988       /* Here we just use the largest height-for-width and use that for the height
989        * of all lines */
990       if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
991         {
992           n_lines    = n_children / line_length;
993           if ((n_children % line_length) > 0)
994             n_lines++;
995           
996           n_lines = MAX (n_lines, 1);
997
998           /* Now we need the real item allocation size */
999           item_size = (avail_size - (line_length - 1) * item_spacing) / line_length;
1000           
1001           /* Cut out the expand space if we're not distributing any */
1002           if (item_spreading != GTK_WRAP_BOX_SPREAD_EXPAND)
1003             item_size = MIN (item_size, nat_item_size);
1004           
1005           get_largest_size_for_opposing_orientation (box, priv->orientation, item_size,
1006                                                      &min_fixed_line_size,
1007                                                      &nat_fixed_line_size);
1008
1009           /* resolve a fixed 'line_size' */
1010           line_size = (avail_other_size - (n_lines - 1) * line_spacing) / n_lines;
1011
1012           if (line_spreading != GTK_WRAP_BOX_SPREAD_EXPAND)
1013             line_size = MIN (line_size, nat_fixed_line_size);
1014
1015           /* Get the real extra pixels incase of GTK_WRAP_BOX_SPREAD_START lines */
1016           extra_pixels      = avail_size       - (line_length - 1) * item_spacing - item_size * line_length;
1017           extra_line_pixels = avail_other_size - (n_lines - 1)     * line_spacing - line_size * n_lines;
1018         }
1019       else /* GTK_WRAP_ALLOCATE_ALIGNED */
1020         {
1021           GList            *list;
1022           gboolean          first_line = TRUE;
1023
1024           /* Find the amount of columns that can fit aligned into the available space
1025            * and collect their requests.
1026            */
1027           item_sizes = fit_aligned_item_requests (box, priv->orientation, avail_size,
1028                                                   item_spacing, &line_length, n_children);
1029
1030           /* Calculate the number of lines after determining the final line_length */
1031           n_lines    = n_children / line_length;
1032           if ((n_children % line_length) > 0)
1033             n_lines++;
1034           
1035           n_lines = MAX (n_lines, 1);
1036           line_sizes = g_new0 (GtkRequestedSize, n_lines);
1037
1038           /* Get the available remaining size */
1039           avail_size -= (line_length - 1) * item_spacing;
1040           for (i = 0; i < line_length; i++)
1041             avail_size -= item_sizes[i].minimum_size;
1042
1043           /* Perform a natural allocation on the columnized items and get the remaining pixels */
1044           extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes);
1045
1046           /* Now that we have the size of each column of items find the size of each individual 
1047            * line based on the aligned item sizes.
1048            */
1049           for (i = 0, list = priv->children; list != NULL; i++)
1050             {
1051
1052               list =
1053                 get_largest_size_for_line_in_opposing_orientation (box, priv->orientation,
1054                                                                    list, line_length,
1055                                                                    item_sizes, extra_pixels,
1056                                                                    &line_sizes[i].minimum_size,
1057                                                                    &line_sizes[i].natural_size);
1058
1059
1060               /* Its possible a line is made of completely invisible children */
1061               if (line_sizes[i].natural_size > 0)
1062                 {
1063                   if (first_line)
1064                     first_line = FALSE;
1065                   else
1066                     avail_other_size -= line_spacing;
1067
1068                   avail_other_size -= line_sizes[i].minimum_size;
1069
1070                   line_sizes[i].data = GINT_TO_POINTER (i);
1071                 }
1072             }
1073
1074           /* Distribute space among lines naturally */
1075           extra_line_pixels = gtk_distribute_natural_allocation (avail_other_size, n_lines, line_sizes);
1076         }
1077
1078       /*********************************************************
1079        * Initial sizes of items/lines guessed at this point,   * 
1080        * go on to distribute expand space if needed.           *
1081        *********************************************************/
1082
1083       /* FIXME: This portion needs to consider which columns
1084        * and rows asked for expand space and distribute those
1085        * accordingly for the case of ALIGNED allocation.
1086        *
1087        * If at least one child in a column/row asked for expand;
1088        * we should make that row/column expand entirely.
1089        */
1090
1091       /* Calculate expand space per item */
1092       if (item_spreading == GTK_WRAP_BOX_SPREAD_EVEN)
1093         {
1094           extra_per_item = extra_pixels / MAX (line_length -1, 1);
1095           extra_extra    = extra_pixels % MAX (line_length -1, 1);
1096         }
1097       else if (item_spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
1098         {
1099           extra_per_item = extra_pixels / line_length;
1100           extra_extra    = extra_pixels % line_length;
1101         }
1102
1103       /* Calculate expand space per line */
1104       if (line_spreading == GTK_WRAP_BOX_SPREAD_EVEN)
1105         {
1106           extra_per_line   = extra_line_pixels / MAX (n_lines -1, 1);
1107           extra_line_extra = extra_line_pixels % MAX (n_lines -1, 1);
1108         }
1109       else if (line_spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
1110         {
1111           extra_per_line   = extra_line_pixels / n_lines;
1112           extra_line_extra = extra_line_pixels % n_lines;
1113         }
1114
1115       /*********************************************************
1116        * Prepare item/line initial offsets and jump into the   *
1117        * real allocation loop.                                 *
1118        *********************************************************/
1119       line_offset = item_offset = 0;
1120
1121       /* prepend extra space to item_offset/line_offset for SPREAD_END */
1122       if (item_spreading == GTK_WRAP_BOX_SPREAD_END)
1123         item_offset += extra_pixels;
1124
1125       if (line_spreading == GTK_WRAP_BOX_SPREAD_END)
1126         line_offset += extra_line_pixels;
1127
1128       /* Get the allocation size for the first line */
1129       if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
1130         this_line_size = line_size;
1131       else 
1132         {
1133           this_line_size  = line_sizes[0].minimum_size;
1134
1135           if (line_spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
1136             {
1137               this_line_size += extra_per_line;
1138
1139               if (extra_line_extra > 0)
1140                 this_line_size++;
1141             }
1142         }
1143
1144       for (i = 0, line_count = 0, list = priv->children; list; list = list->next)
1145         {
1146           GtkWrapBoxChild *child = list->data;
1147           gint             position;
1148           gint             this_item_size;
1149
1150           if (!gtk_widget_get_visible (child->widget))
1151             continue;
1152
1153           /* Get item position */
1154           position = i % line_length;
1155
1156           /* adjust the line_offset/count at the beginning of each new line */
1157           if (i > 0 && position == 0)
1158             {
1159               /* Push the line_offset */
1160               line_offset += this_line_size + line_spacing;
1161
1162               if (line_spreading == GTK_WRAP_BOX_SPREAD_EVEN)
1163                 {
1164                   line_offset += extra_per_line;
1165                       
1166                   if (line_count < extra_line_extra)
1167                     line_offset++;
1168                 }
1169
1170               line_count++;
1171
1172               /* Get the new line size */
1173               if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
1174                 this_line_size = line_size;
1175               else
1176                 {
1177                   this_line_size = line_sizes[line_count].minimum_size;
1178
1179                   if (line_spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
1180                     {
1181                       this_line_size += extra_per_line;
1182                       
1183                       if (line_count < extra_line_extra)
1184                         this_line_size++;
1185                     }
1186                 }
1187
1188               item_offset = 0;
1189
1190               if (item_spreading == GTK_WRAP_BOX_SPREAD_END)
1191                 {
1192                   item_offset += extra_pixels;
1193
1194                   /* If we're on the last line, prepend the space for
1195                    * any leading items */
1196                   if (line_count == n_lines -1)
1197                     {
1198                       gint extra_items = n_children % line_length;
1199
1200                       if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
1201                         {
1202                           item_offset += item_size * (line_length - extra_items);
1203                           item_offset += item_spacing * (line_length - extra_items);
1204                         }
1205                       else
1206                         {
1207                           gint j;
1208
1209                           for (j = 0; j < (line_length - extra_items); j++)
1210                             {
1211                               item_offset += item_sizes[j].minimum_size;
1212                               item_offset += item_spacing;
1213                             }
1214                         }
1215                     }
1216                 }
1217             }
1218
1219           /* Push the index along for the last line when spreading to the end */
1220           if (item_spreading == GTK_WRAP_BOX_SPREAD_END &&
1221               line_count == n_lines -1)
1222             {
1223               gint extra_items = n_children % line_length;
1224
1225               position += line_length - extra_items;
1226             }
1227
1228           if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
1229             this_item_size = item_size;
1230           else /* aligned mode */
1231             this_item_size = item_sizes[position].minimum_size;
1232
1233           if (item_spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
1234             {
1235               this_item_size += extra_per_item;
1236
1237               if (position < extra_extra)
1238                 this_item_size++;
1239             }
1240
1241           /* Do the actual allocation */
1242           allocate_child (box, child, item_offset, line_offset, this_item_size, this_line_size);
1243
1244           item_offset += this_item_size;
1245           item_offset += item_spacing;
1246
1247           /* deal with extra spacing here */
1248           if (item_spreading == GTK_WRAP_BOX_SPREAD_EVEN)
1249             {
1250               item_offset += extra_per_item;
1251
1252               if (position < extra_extra)
1253                 item_offset++;
1254             }
1255
1256           i++;
1257         }
1258
1259       g_free (item_sizes);
1260       g_free (line_sizes);
1261     }
1262   else /* GTK_WRAP_ALLOCATE_FREE */
1263     {
1264       /* Here we just fit as many children as we can allocate their natural size to
1265        * on each line and add the heights for each of them on each line */
1266       GtkRequestedSize  requested; 
1267       GtkRequestedSize *sizes = NULL;
1268       GList            *list = priv->children;
1269       gboolean          first_line = TRUE;
1270       gint              i, line_count = 0;
1271       gint              line_offset, item_offset;
1272       gint              extra_per_line = 0, extra_line_extra = 0;
1273       gint              extra_pixels;
1274       GArray           *array;
1275   
1276       array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
1277
1278       while (list != NULL)
1279         {
1280           GArray         *line_array;
1281           AllocatedLine  *line;
1282
1283           list =
1284             get_largest_size_for_free_line_in_opposing_orientation (box, priv->orientation,
1285                                                                     list, min_items, avail_size,
1286                                                                     &requested.minimum_size,
1287                                                                     &requested.natural_size,
1288                                                                     &extra_pixels,
1289                                                                     &line_array);
1290
1291           /* Its possible a line is made of completely invisible children */
1292           if (requested.natural_size > 0)
1293             {
1294               if (first_line)
1295                 first_line = FALSE;
1296               else
1297                 avail_other_size -= line_spacing;
1298
1299               avail_other_size -= requested.minimum_size;
1300
1301               line = g_slice_new0 (AllocatedLine);
1302               line->requested    = line_array;
1303               line->extra_pixels = extra_pixels;
1304
1305               requested.data  = line;
1306
1307               g_array_append_val (array, requested);
1308             }
1309         }
1310
1311       /* Distribute space among lines naturally */
1312       sizes            = (GtkRequestedSize *)array->data;
1313       avail_other_size = gtk_distribute_natural_allocation (avail_other_size, array->len, sizes);
1314
1315       /* Calculate expand space per line */
1316       if (line_spreading == GTK_WRAP_BOX_SPREAD_EVEN)
1317         {
1318           extra_per_line   = avail_other_size / MAX (array->len -1, 1);
1319           extra_line_extra = avail_other_size % MAX (array->len -1, 1);
1320         }
1321       else if (line_spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
1322         {
1323           extra_per_line   = avail_other_size / array->len;
1324           extra_line_extra = avail_other_size % array->len;
1325         }
1326
1327       if (line_spreading == GTK_WRAP_BOX_SPREAD_END)
1328         line_offset = avail_other_size;
1329       else
1330         line_offset = 0;
1331
1332       for (line_count = 0; line_count < array->len; line_count++)
1333         {
1334           AllocatedLine    *line       = (AllocatedLine *)sizes[line_count].data;
1335           GArray           *line_array = line->requested;
1336           GtkRequestedSize *line_sizes = (GtkRequestedSize *)line_array->data;
1337           gint              line_size  = sizes[line_count].minimum_size;
1338           gint              extra_per_item = 0;
1339           gint              extra_extra = 0;
1340
1341           /* Set line start offset */
1342           item_offset = 0;
1343
1344           if (line_spreading == GTK_WRAP_BOX_SPREAD_EXPAND)
1345             {
1346               line_size += extra_per_line;
1347
1348               if (line_count < extra_line_extra)
1349                 line_size++;
1350             }
1351
1352           if (item_spreading == GTK_WRAP_BOX_SPREAD_END)
1353             item_offset += line->extra_pixels;
1354           else if (item_spreading == GTK_WRAP_BOX_SPREAD_EVEN)
1355             {
1356               extra_per_item = line->extra_pixels / MAX (line_array->len -1, 1);
1357               extra_extra    = line->extra_pixels % MAX (line_array->len -1, 1);
1358             }
1359
1360           for (i = 0; i < line_array->len; i++)
1361             {
1362               GtkWrapBoxChild *child     = line_sizes[i].data;
1363               gint             item_size = line_sizes[i].minimum_size;
1364
1365               /* Do the actual allocation */
1366               allocate_child (box, child, item_offset, line_offset, item_size, line_size);
1367
1368               /* Add extra space evenly between children */
1369               if (item_spreading == GTK_WRAP_BOX_SPREAD_EVEN)
1370                 {
1371                   item_offset += extra_per_item;
1372                   if (i < extra_extra)
1373                     item_offset++;
1374                 }
1375
1376               /* Move item cursor along for the next allocation */
1377               item_offset += item_spacing;
1378               item_offset += item_size;
1379             }
1380
1381           /* New line, increment offset and reset item cursor */
1382           line_offset += line_spacing;
1383           line_offset += line_size;
1384
1385           if (line_spreading == GTK_WRAP_BOX_SPREAD_EVEN)
1386             {
1387               line_offset += extra_per_line;
1388
1389               if (line_count < extra_line_extra)
1390                 line_offset++;
1391             }
1392
1393           /* Free the array for this line now its not needed anymore */
1394           g_array_free (line_array, TRUE);
1395           g_slice_free (AllocatedLine, line);
1396         }
1397
1398       g_array_free (array, TRUE);
1399     }
1400 }
1401
1402 /*****************************************************
1403  *                GtkContainerClass                  *
1404  *****************************************************/
1405 static void
1406 gtk_wrap_box_add (GtkContainer *container,
1407                   GtkWidget    *widget)
1408 {
1409   gtk_wrap_box_insert_child (GTK_WRAP_BOX (container), widget, -1, 0);
1410 }
1411
1412 static gint
1413 find_child_in_list (GtkWrapBoxChild *child_in_list,
1414                     GtkWidget       *search)
1415 {
1416   return (child_in_list->widget == search) ? 0 : -1;
1417 }
1418
1419 static void
1420 gtk_wrap_box_remove (GtkContainer *container,
1421                      GtkWidget    *widget)
1422 {
1423   GtkWrapBox        *box = GTK_WRAP_BOX (container);
1424   GtkWrapBoxPrivate *priv   = box->priv;
1425   GList             *list;
1426
1427   list = g_list_find_custom (priv->children, widget,
1428                              (GCompareFunc)find_child_in_list);
1429
1430   if (list)
1431     {
1432       GtkWrapBoxChild *child = list->data;
1433       gboolean was_visible = gtk_widget_get_visible (widget);
1434
1435       gtk_widget_unparent (widget);
1436
1437       g_slice_free (GtkWrapBoxChild, child);
1438       priv->children = g_list_delete_link (priv->children, list);
1439
1440       if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
1441         gtk_widget_queue_resize (GTK_WIDGET (container));
1442     }
1443 }
1444
1445 static void
1446 gtk_wrap_box_forall (GtkContainer *container,
1447                      gboolean      include_internals,
1448                      GtkCallback   callback,
1449                      gpointer      callback_data)
1450 {
1451   GtkWrapBox        *box = GTK_WRAP_BOX (container);
1452   GtkWrapBoxPrivate *priv   = box->priv;
1453   GtkWrapBoxChild   *child;
1454   GList             *list;
1455
1456   for (list = priv->children; list; list = list->next)
1457     {
1458       child = list->data;
1459
1460       (* callback) (child->widget, callback_data);
1461     }
1462 }
1463
1464 static GType
1465 gtk_wrap_box_child_type (GtkContainer   *container)
1466 {
1467   return GTK_TYPE_WIDGET;
1468 }
1469
1470 static void
1471 gtk_wrap_box_set_child_property (GtkContainer    *container,
1472                                  GtkWidget       *widget,
1473                                  guint            property_id,
1474                                  const GValue    *value,
1475                                  GParamSpec      *pspec)
1476 {
1477   GtkWrapBox        *box  = GTK_WRAP_BOX (container);
1478   GtkWrapBoxPrivate *priv = box->priv;
1479   GtkWrapBoxChild   *child;
1480   GList             *list;
1481
1482   list = g_list_find_custom (priv->children, widget,
1483                              (GCompareFunc)find_child_in_list);
1484   g_return_if_fail (list != NULL);
1485
1486   child = list->data;
1487
1488   switch (property_id)
1489     {
1490     case CHILD_PROP_PACKING:
1491       child->packing = g_value_get_flags (value);
1492       break;
1493     default:
1494       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
1495       break;
1496     }
1497
1498   if (gtk_widget_get_visible (widget) &&
1499       gtk_widget_get_visible (GTK_WIDGET (box)))
1500     gtk_widget_queue_resize (widget);
1501 }
1502
1503 static void
1504 gtk_wrap_box_get_child_property (GtkContainer    *container,
1505                                  GtkWidget       *widget,
1506                                  guint            property_id,
1507                                  GValue          *value,
1508                                  GParamSpec      *pspec)
1509 {
1510   GtkWrapBox        *box = GTK_WRAP_BOX (container);
1511   GtkWrapBoxPrivate *priv   = box->priv;
1512   GtkWrapBoxChild   *child;
1513   GList             *list;
1514
1515   list = g_list_find_custom (priv->children, widget,
1516                              (GCompareFunc)find_child_in_list);
1517   g_return_if_fail (list != NULL);
1518
1519   child = list->data;
1520
1521   switch (property_id)
1522     {
1523     case CHILD_PROP_PACKING:
1524       g_value_set_flags (value, child->packing);
1525       break;
1526     default:
1527       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
1528       break;
1529     }
1530 }
1531
1532 /*****************************************************
1533  *                 size requests                     *
1534  *****************************************************/
1535
1536
1537 static GtkSizeRequestMode
1538 gtk_wrap_box_get_request_mode (GtkWidget      *widget)
1539 {
1540   GtkWrapBox        *box = GTK_WRAP_BOX (widget);
1541   GtkWrapBoxPrivate *priv   = box->priv;
1542
1543   return (priv->orientation == GTK_ORIENTATION_HORIZONTAL) ?
1544     GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH : GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
1545 }
1546
1547 /* Gets the largest minimum and natural length of
1548  * 'line_length' consecutive items */
1549 static void
1550 get_largest_line_length (GtkWrapBox      *box,
1551                          GtkOrientation   orientation,
1552                          gint             line_length,
1553                          gint            *min_size,
1554                          gint            *nat_size)
1555 {
1556   GtkWrapBoxPrivate *priv = box->priv;
1557   GList             *list, *l;
1558   gint               max_min_size = 0;
1559   gint               max_nat_size = 0;
1560   gint               spacing;
1561
1562   if (orientation == GTK_ORIENTATION_HORIZONTAL)
1563     spacing = priv->horizontal_spacing;
1564   else
1565     spacing = priv->vertical_spacing;
1566
1567   /* Get the largest size of 'line_length' consecutive items in the list.
1568    */
1569   for (list = priv->children; list; list = list->next)
1570     {
1571       gint   line_min = 0;
1572       gint   line_nat = 0;
1573       gint   i;
1574
1575       for (l = list, i = 0; l && i < line_length; l = l->next)
1576         {
1577           GtkWrapBoxChild *child = l->data;
1578           gint             child_min, child_nat;
1579
1580           if (!gtk_widget_get_visible (child->widget))
1581             continue;
1582
1583           if (orientation == GTK_ORIENTATION_HORIZONTAL)
1584             gtk_widget_get_preferred_width (child->widget,
1585                                             &child_min, &child_nat);
1586           else /* GTK_ORIENTATION_VERTICAL */
1587             gtk_widget_get_preferred_height (child->widget,
1588                                              &child_min, &child_nat);
1589
1590           line_min += child_min;
1591           line_nat += child_nat;
1592
1593           i++;
1594         }
1595
1596       max_min_size = MAX (max_min_size, line_min);
1597       max_nat_size = MAX (max_nat_size, line_nat);
1598     }
1599
1600   max_min_size += (line_length - 1) * spacing;
1601   max_nat_size += (line_length - 1) * spacing;
1602
1603   if (min_size)
1604     *min_size = max_min_size;
1605
1606   if (nat_size)
1607     *nat_size = max_nat_size;
1608 }
1609
1610 /* Gets the largest minimum and natural length of
1611  * 'line_length' consecutive items when aligned into rows/columns */
1612 static void
1613 get_largest_aligned_line_length (GtkWrapBox      *box,
1614                                  GtkOrientation   orientation,
1615                                  gint             line_length,
1616                                  gint            *min_size,
1617                                  gint            *nat_size)
1618 {
1619   GtkWrapBoxPrivate *priv = box->priv;
1620   GList             *list;
1621   gint               max_min_size = 0;
1622   gint               max_nat_size = 0;
1623   gint               spacing, i;
1624   GtkRequestedSize  *aligned_item_sizes;
1625
1626   if (orientation == GTK_ORIENTATION_HORIZONTAL)
1627     spacing = priv->horizontal_spacing;
1628   else
1629     spacing = priv->vertical_spacing;
1630
1631   aligned_item_sizes = g_new0 (GtkRequestedSize, line_length);
1632
1633   /* Get the largest sizes of each index in the line.
1634    */
1635   for (list = priv->children, i = 0; list; list = list->next)
1636     {
1637       GtkWrapBoxChild *child = list->data;
1638       gint             child_min, child_nat;
1639       
1640       if (!gtk_widget_get_visible (child->widget))
1641         continue;
1642
1643       if (orientation == GTK_ORIENTATION_HORIZONTAL)
1644         gtk_widget_get_preferred_width (child->widget,
1645                                         &child_min, &child_nat);
1646       else /* GTK_ORIENTATION_VERTICAL */
1647         gtk_widget_get_preferred_height (child->widget,
1648                                          &child_min, &child_nat);
1649
1650       aligned_item_sizes[i % line_length].minimum_size = 
1651         MAX (aligned_item_sizes[i % line_length].minimum_size, child_min);
1652
1653       aligned_item_sizes[i % line_length].natural_size = 
1654         MAX (aligned_item_sizes[i % line_length].natural_size, child_nat);
1655
1656       i++;
1657     }
1658
1659   /* Add up the largest indexes */
1660   for (i = 0; i < line_length; i++)
1661     {
1662       max_min_size += aligned_item_sizes[i].minimum_size;
1663       max_nat_size += aligned_item_sizes[i].natural_size;
1664     }
1665
1666   g_free (aligned_item_sizes);
1667
1668   max_min_size += (line_length - 1) * spacing;
1669   max_nat_size += (line_length - 1) * spacing;
1670
1671   if (min_size)
1672     *min_size = max_min_size;
1673
1674   if (nat_size)
1675     *nat_size = max_nat_size;
1676 }
1677
1678
1679 static void
1680 gtk_wrap_box_get_preferred_width (GtkWidget           *widget,
1681                                   gint                *minimum_size,
1682                                   gint                *natural_size)
1683 {
1684   GtkWrapBox        *box  = GTK_WRAP_BOX (widget);
1685   GtkWrapBoxPrivate *priv = box->priv;
1686   gint               min_item_width, nat_item_width;
1687   gint               min_items, nat_items;
1688   gint               min_width, nat_width;
1689
1690   min_items = MAX (1, priv->minimum_line_children);
1691   nat_items = MAX (min_items, priv->natural_line_children);
1692
1693   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1694     {
1695       min_width    = nat_width = 0;
1696
1697       if (priv->mode == GTK_WRAP_ALLOCATE_FREE ||
1698           priv->mode == GTK_WRAP_ALLOCATE_ALIGNED)
1699         {
1700           /* In FREE and ALIGNED modes; horizontally oriented boxes
1701            * need enough width for the widest row */
1702           if (min_items == 1)
1703             {
1704               get_average_item_size (box, GTK_ORIENTATION_HORIZONTAL,
1705                                      &min_item_width, &nat_item_width);
1706
1707               min_width += min_item_width;
1708               nat_width += nat_item_width;
1709             }
1710           else if (priv->mode == GTK_WRAP_ALLOCATE_FREE)
1711             {
1712               gint min_line_length, nat_line_length;
1713
1714               get_largest_line_length (box, GTK_ORIENTATION_HORIZONTAL, min_items,
1715                                        &min_line_length, &nat_line_length);
1716
1717               if (nat_items > min_items)
1718                 get_largest_line_length (box, GTK_ORIENTATION_HORIZONTAL, nat_items,
1719                                          NULL, &nat_line_length);
1720
1721               min_width += min_line_length;
1722               nat_width += nat_line_length;
1723             }
1724           else /* GTK_WRAP_MODE_ALIGNED */
1725             {
1726               gint min_line_length, nat_line_length;
1727
1728               get_largest_aligned_line_length (box, GTK_ORIENTATION_HORIZONTAL, min_items,
1729                                                &min_line_length, &nat_line_length);
1730
1731               if (nat_items > min_items)
1732                 get_largest_aligned_line_length (box, GTK_ORIENTATION_HORIZONTAL, nat_items,
1733                                                  NULL, &nat_line_length);
1734
1735               min_width += min_line_length;
1736               nat_width += nat_line_length;
1737             }
1738         }
1739       else /* In HOMOGENEOUS mode; horizontally oriented boxs
1740             * give the same width to all children */
1741         {
1742           get_average_item_size (box, GTK_ORIENTATION_HORIZONTAL,
1743                                  &min_item_width, &nat_item_width);
1744
1745           min_width += min_item_width * min_items;
1746           min_width += (min_items -1) * priv->horizontal_spacing;
1747
1748           nat_width += nat_item_width * nat_items;
1749           nat_width += (nat_items -1) * priv->horizontal_spacing;
1750         }
1751     }
1752   else /* GTK_ORIENTATION_VERTICAL */
1753     {
1754       /* Return the width for the minimum height */
1755       gint min_height;
1756
1757       GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, NULL);
1758       GTK_WIDGET_GET_CLASS (widget)->get_preferred_width_for_height (widget, min_height,
1759                                                                            &min_width, &nat_width);
1760
1761     }
1762
1763   if (minimum_size)
1764     *minimum_size = min_width;
1765
1766   if (natural_size)
1767     *natural_size = nat_width;
1768 }
1769
1770 static void
1771 gtk_wrap_box_get_preferred_height (GtkWidget           *widget,
1772                                    gint                *minimum_size,
1773                                    gint                *natural_size)
1774 {
1775   GtkWrapBox        *box  = GTK_WRAP_BOX (widget);
1776   GtkWrapBoxPrivate *priv = box->priv;
1777   gint               min_item_height, nat_item_height;
1778   gint               min_items, nat_items;
1779   gint               min_height, nat_height;
1780
1781   min_items = MAX (1, priv->minimum_line_children);
1782   nat_items = MAX (min_items, priv->natural_line_children);
1783
1784   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1785     {
1786       /* Return the height for the minimum width */
1787       gint min_width;
1788
1789       GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
1790       GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width,
1791                                                                            &min_height, &nat_height);
1792     }
1793   else /* GTK_ORIENTATION_VERTICAL */
1794     {
1795       min_height   = nat_height = 0;
1796
1797       if (priv->mode == GTK_WRAP_ALLOCATE_FREE ||
1798           priv->mode == GTK_WRAP_ALLOCATE_ALIGNED)
1799         {
1800           /* In FREE and ALIGNED modes; vertically oriented boxes
1801            * need enough height for the tallest column */
1802           if (min_items == 1)
1803             {
1804               get_average_item_size (box, GTK_ORIENTATION_VERTICAL,
1805                                      &min_item_height, &nat_item_height);
1806
1807               min_height += min_item_height;
1808               nat_height += nat_item_height;
1809             }
1810           else if (priv->mode == GTK_WRAP_ALLOCATE_FREE)
1811             {
1812               gint min_line_length, nat_line_length;
1813
1814               get_largest_line_length (box, GTK_ORIENTATION_VERTICAL, min_items,
1815                                        &min_line_length, &nat_line_length);
1816
1817               if (nat_items > min_items)
1818                 get_largest_line_length (box, GTK_ORIENTATION_VERTICAL, nat_items,
1819                                          NULL, &nat_line_length);
1820
1821               min_height += min_line_length;
1822               nat_height += nat_line_length;
1823             }
1824           else /* GTK_WRAP_ALLOCATE_ALIGNED */
1825             {
1826               gint min_line_length, nat_line_length;
1827
1828               get_largest_aligned_line_length (box, GTK_ORIENTATION_VERTICAL, min_items,
1829                                                &min_line_length, &nat_line_length);
1830
1831               if (nat_items > min_items)
1832                 get_largest_aligned_line_length (box, GTK_ORIENTATION_VERTICAL, nat_items,
1833                                                  NULL, &nat_line_length);
1834
1835               min_height += min_line_length;
1836               nat_height += nat_line_length;
1837             }
1838
1839         }
1840       else /* In HOMOGENEOUS mode; vertically oriented boxs
1841             * give the same height to all children */
1842         {
1843           get_average_item_size (box, GTK_ORIENTATION_VERTICAL,
1844                                  &min_item_height, &nat_item_height);
1845
1846           min_height += min_item_height * min_items;
1847           min_height += (min_items -1) * priv->vertical_spacing;
1848
1849           nat_height += nat_item_height * nat_items;
1850           nat_height += (nat_items -1) * priv->vertical_spacing;
1851         }
1852     }
1853
1854   if (minimum_size)
1855     *minimum_size = min_height;
1856
1857   if (natural_size)
1858     *natural_size = nat_height;
1859 }
1860
1861 static void
1862 gtk_wrap_box_get_preferred_height_for_width (GtkWidget           *widget,
1863                                              gint                 width,
1864                                              gint                *minimum_height,
1865                                              gint                *natural_height)
1866 {
1867   GtkWrapBox        *box = GTK_WRAP_BOX (widget);
1868   GtkWrapBoxPrivate *priv   = box->priv;
1869   gint               min_item_width, nat_item_width;
1870   gint               min_items;
1871   gint               min_height, nat_height;
1872   gint               avail_size, n_children;
1873
1874   min_items = MAX (1, priv->minimum_line_children);
1875
1876   min_height = 0;
1877   nat_height = 0;
1878
1879   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1880     {
1881       gint min_width;
1882
1883       n_children = get_visible_children (box);
1884
1885       /* Make sure its no smaller than the minimum */
1886       GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
1887
1888       avail_size  = MAX (width, min_width);
1889
1890       if (priv->mode == GTK_WRAP_ALLOCATE_ALIGNED ||
1891           priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
1892         {
1893           gint line_length;
1894           gint item_size, extra_pixels;
1895
1896           get_average_item_size (box, GTK_ORIENTATION_HORIZONTAL, &min_item_width, &nat_item_width);
1897
1898           /* By default wrap at the natural item width */
1899           line_length = avail_size / (nat_item_width + priv->horizontal_spacing);
1900
1901           /* After the above aproximation, check if we cant fit one more on the line */
1902           if (line_length * priv->horizontal_spacing + (line_length + 1) * nat_item_width <= avail_size)
1903             line_length++;
1904
1905           /* Its possible we were allocated just less than the natural width of the
1906            * minimum item wrap length */
1907           line_length = MAX (min_items, line_length);
1908
1909           /* Now we need the real item allocation size */
1910           item_size = (avail_size - (line_length - 1) * priv->horizontal_spacing) / line_length;
1911
1912           /* Cut out the expand space if we're not distributing any */
1913           if (priv->horizontal_spreading != GTK_WRAP_BOX_SPREAD_EXPAND)
1914             {
1915               item_size    = MIN (item_size, nat_item_width);
1916               extra_pixels = 0;
1917             }
1918           else
1919             /* Collect the extra pixels for expand children */
1920             extra_pixels = (avail_size - (line_length - 1) * priv->horizontal_spacing) % line_length;
1921
1922           if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
1923             {
1924               gint min_item_height, nat_item_height;
1925               gint lines;
1926
1927               /* Here we just use the largest height-for-width and
1928                * add up the size accordingly */
1929               get_largest_size_for_opposing_orientation (box, GTK_ORIENTATION_HORIZONTAL, item_size,
1930                                                          &min_item_height, &nat_item_height);
1931
1932               /* Round up how many lines we need to allocate for */
1933               lines      = n_children / line_length;
1934               if ((n_children % line_length) > 0)
1935                 lines++;
1936
1937               min_height = min_item_height * lines;
1938               nat_height = nat_item_height * lines;
1939
1940               min_height += (lines - 1) * priv->vertical_spacing;
1941               nat_height += (lines - 1) * priv->vertical_spacing;
1942             }
1943           else /* GTK_WRAP_ALLOCATE_ALIGNED */
1944             {
1945               GList *list = priv->children;
1946               gint min_line_height, nat_line_height, i;
1947               gboolean first_line = TRUE;
1948               GtkRequestedSize *item_sizes;
1949
1950               /* First get the size each set of items take to span the line
1951                * when aligning the items above and below after wrapping.
1952                */
1953               item_sizes = fit_aligned_item_requests (box, priv->orientation, avail_size,
1954                                                       priv->horizontal_spacing, &line_length, n_children);
1955
1956
1957               /* Get the available remaining size */
1958               avail_size -= (line_length - 1) * priv->horizontal_spacing;
1959               for (i = 0; i < line_length; i++)
1960                 avail_size -= item_sizes[i].minimum_size;
1961
1962               extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes);
1963
1964               while (list != NULL)
1965                 {
1966                   list =
1967                     get_largest_size_for_line_in_opposing_orientation (box, GTK_ORIENTATION_HORIZONTAL,
1968                                                                        list, line_length,
1969                                                                        item_sizes, extra_pixels,
1970                                                                        &min_line_height, &nat_line_height);
1971
1972                   /* Its possible the line only had invisible widgets */
1973                   if (nat_line_height > 0)
1974                     {
1975                       if (first_line)
1976                         first_line = FALSE;
1977                       else
1978                         {
1979                           min_height += priv->vertical_spacing;
1980                           nat_height += priv->vertical_spacing;
1981                         }
1982
1983                       min_height += min_line_height;
1984                       nat_height += nat_line_height;
1985                     }
1986                 }
1987
1988               g_free (item_sizes);
1989             }
1990         }
1991       else /* GTK_WRAP_ALLOCATE_FREE */
1992         {
1993           /* Here we just fit as many children as we can allocate their natural size to
1994            * on each line and add the heights for each of them on each line */
1995           GList *list = priv->children;
1996           gint min_line_height = 0, nat_line_height = 0;
1997           gboolean first_line = TRUE;
1998
1999           while (list != NULL)
2000             {
2001               list =
2002                 get_largest_size_for_free_line_in_opposing_orientation (box, GTK_ORIENTATION_HORIZONTAL,
2003                                                                         list, min_items, avail_size,
2004                                                                         &min_line_height, &nat_line_height,
2005                                                                         NULL, NULL);
2006
2007               /* Its possible the last line only had invisible widgets */
2008               if (nat_line_height > 0)
2009                 {
2010                   if (first_line)
2011                     first_line = FALSE;
2012                   else
2013                     {
2014                       min_height += priv->vertical_spacing;
2015                       nat_height += priv->vertical_spacing;
2016                     }
2017
2018                   min_height += min_line_height;
2019                   nat_height += nat_line_height;
2020                 }
2021             }
2022         }
2023     }
2024   else /* GTK_ORIENTATION_VERTICAL */
2025     {
2026       /* Return the minimum height */
2027       GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, &nat_height);
2028     }
2029
2030   if (minimum_height)
2031     *minimum_height = min_height;
2032
2033   if (natural_height)
2034     *natural_height = nat_height;
2035 }
2036
2037 static void
2038 gtk_wrap_box_get_preferred_width_for_height (GtkWidget           *widget,
2039                                              gint                 height,
2040                                              gint                *minimum_width,
2041                                              gint                *natural_width)
2042 {
2043   GtkWrapBox        *box = GTK_WRAP_BOX (widget);
2044   GtkWrapBoxPrivate *priv   = box->priv;
2045   gint               min_item_height, nat_item_height;
2046   gint               min_items;
2047   gint               min_width, nat_width;
2048   gint               avail_size, n_children;
2049
2050   min_items = MAX (1, priv->minimum_line_children);
2051
2052   min_width = 0;
2053   nat_width = 0;
2054
2055   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2056     {
2057       /* Return the minimum width */
2058       GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, &nat_width);
2059     }
2060   else /* GTK_ORIENTATION_VERTICAL */
2061     {
2062       gint min_height;
2063
2064       n_children = get_visible_children (box);
2065
2066       /* Make sure its no smaller than the minimum */
2067       GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, NULL);
2068
2069       avail_size  = MAX (height, min_height);
2070
2071       if (priv->mode == GTK_WRAP_ALLOCATE_ALIGNED ||
2072           priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
2073         {
2074           gint line_length;
2075           gint item_size, extra_pixels;
2076
2077           get_average_item_size (box, GTK_ORIENTATION_VERTICAL, &min_item_height, &nat_item_height);
2078
2079           /* By default wrap at the natural item width */
2080           line_length = avail_size / (nat_item_height + priv->vertical_spacing);
2081
2082           /* After the above aproximation, check if we cant fit one more on the line */
2083           if (line_length * priv->vertical_spacing + (line_length + 1) * nat_item_height <= avail_size)
2084             line_length++;
2085
2086           /* Its possible we were allocated just less than the natural width of the
2087            * minimum item wrap length */
2088           line_length = MAX (min_items, line_length);
2089
2090           /* Now we need the real item allocation size */
2091           item_size = (avail_size - (line_length - 1) * priv->vertical_spacing) / line_length;
2092
2093           /* Cut out the expand space if we're not distributing any */
2094           if (priv->vertical_spreading != GTK_WRAP_BOX_SPREAD_EXPAND)
2095             {
2096               item_size    = MIN (item_size, nat_item_height);
2097               extra_pixels = 0;
2098             }
2099           else
2100             /* Collect the extra pixels for expand children */
2101             extra_pixels = (avail_size - (line_length - 1) * priv->vertical_spacing) % line_length;
2102
2103           if (priv->mode == GTK_WRAP_ALLOCATE_HOMOGENEOUS)
2104             {
2105               gint min_item_width, nat_item_width;
2106               gint lines;
2107
2108               /* Here we just use the largest height-for-width and
2109                * add up the size accordingly */
2110               get_largest_size_for_opposing_orientation (box, GTK_ORIENTATION_VERTICAL, item_size,
2111                                                          &min_item_width, &nat_item_width);
2112
2113               /* Round up how many lines we need to allocate for */
2114               n_children = get_visible_children (box);
2115               lines      = n_children / line_length;
2116               if ((n_children % line_length) > 0)
2117                 lines++;
2118
2119               min_width = min_item_width * lines;
2120               nat_width = nat_item_width * lines;
2121
2122               min_width += (lines - 1) * priv->horizontal_spacing;
2123               nat_width += (lines - 1) * priv->horizontal_spacing;
2124             }
2125           else /* GTK_WRAP_ALLOCATE_ALIGNED */
2126             {
2127               GList *list = priv->children;
2128               gint min_line_width, nat_line_width, i;
2129               gboolean first_line = TRUE;
2130               GtkRequestedSize *item_sizes;
2131
2132               /* First get the size each set of items take to span the line
2133                * when aligning the items above and below after wrapping.
2134                */
2135               item_sizes = fit_aligned_item_requests (box, priv->orientation, avail_size,
2136                                                       priv->vertical_spacing, &line_length, n_children);
2137
2138               /* Get the available remaining size */
2139               avail_size -= (line_length - 1) * priv->horizontal_spacing;
2140               for (i = 0; i < line_length; i++)
2141                 avail_size -= item_sizes[i].minimum_size;
2142
2143               extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes);
2144
2145               while (list != NULL)
2146                 {
2147                   list =
2148                     get_largest_size_for_line_in_opposing_orientation (box, GTK_ORIENTATION_VERTICAL,
2149                                                                        list, line_length,
2150                                                                        item_sizes, extra_pixels,
2151                                                                        &min_line_width, &nat_line_width);
2152
2153                   /* Its possible the last line only had invisible widgets */
2154                   if (nat_line_width > 0)
2155                     {
2156                       if (first_line)
2157                         first_line = FALSE;
2158                       else
2159                         {
2160                           min_width += priv->horizontal_spacing;
2161                           nat_width += priv->horizontal_spacing;
2162                         }
2163
2164                       min_width += min_line_width;
2165                       nat_width += nat_line_width;
2166                     }
2167                 }
2168               g_free (item_sizes);
2169             }
2170         }
2171       else /* GTK_WRAP_ALLOCATE_FREE */
2172         {
2173           /* Here we just fit as many children as we can allocate their natural size to
2174            * on each line and add the heights for each of them on each line */
2175           GList *list = priv->children;
2176           gint min_line_width = 0, nat_line_width = 0;
2177           gboolean first_line = TRUE;
2178
2179           while (list != NULL)
2180             {
2181               list =
2182                 get_largest_size_for_free_line_in_opposing_orientation (box, GTK_ORIENTATION_VERTICAL,
2183                                                                         list, min_items, avail_size,
2184                                                                         &min_line_width, &nat_line_width,
2185                                                                         NULL, NULL);
2186
2187               /* Its possible the last line only had invisible widgets */
2188               if (nat_line_width > 0)
2189                 {
2190                   if (first_line)
2191                     first_line = FALSE;
2192                   else
2193                     {
2194                       min_width += priv->horizontal_spacing;
2195                       nat_width += priv->horizontal_spacing;
2196                     }
2197
2198                   min_width += min_line_width;
2199                   nat_width += nat_line_width;
2200                 }
2201             }
2202         }
2203     }
2204
2205   if (minimum_width)
2206     *minimum_width = min_width;
2207
2208   if (natural_width)
2209     *natural_width = nat_width;
2210 }
2211
2212 /*****************************************************
2213  *                       API                         *
2214  *****************************************************/
2215
2216 /**
2217  * gtk_wrap_box_new:
2218  * @mode: The #GtkWrapAllocationMode to use
2219  * @horizontal_spreading: The horizontal #GtkWrapBoxSpreading policy to use
2220  * @vertical_spreading: The vertical #GtkWrapBoxSpreading policy to use
2221  * @horizontal_spacing: The horizontal spacing to add between children
2222  * @vertical_spacing: The vertical spacing to add between children
2223  *
2224  * Creates an #GtkWrapBox.
2225  *
2226  * Returns: A new #GtkWrapBox container
2227  */
2228 GtkWidget *
2229 gtk_wrap_box_new (GtkWrapAllocationMode mode,
2230                   GtkWrapBoxSpreading   horizontal_spreading,
2231                   GtkWrapBoxSpreading   vertical_spreading,
2232                   guint                 horizontal_spacing,
2233                   guint                 vertical_spacing)
2234 {
2235   return (GtkWidget *)g_object_new (GTK_TYPE_WRAP_BOX,
2236                                     "allocation-mode", mode,
2237                                     "horizontal-spreading", horizontal_spreading,
2238                                     "vertical-spreading", vertical_spreading,
2239                                     "vertical-spacing", vertical_spacing,
2240                                     "horizontal-spacing", horizontal_spacing,
2241                                     NULL);
2242 }
2243
2244 /**
2245  * gtk_wrap_box_set_allocation_mode:
2246  * @box: An #GtkWrapBox
2247  * @mode: The #GtkWrapAllocationMode to use.
2248  *
2249  * Sets the allocation mode for @box's children.
2250  */
2251 void
2252 gtk_wrap_box_set_allocation_mode (GtkWrapBox           *box,
2253                                   GtkWrapAllocationMode mode)
2254 {
2255   GtkWrapBoxPrivate *priv;
2256
2257   g_return_if_fail (GTK_IS_WRAP_BOX (box));
2258
2259   priv = box->priv;
2260
2261   if (priv->mode != mode)
2262     {
2263       priv->mode = mode;
2264
2265       gtk_widget_queue_resize (GTK_WIDGET (box));
2266
2267       g_object_notify (G_OBJECT (box), "allocation-mode");
2268     }
2269 }
2270
2271 /**
2272  * gtk_wrap_box_get_allocation_mode:
2273  * @box: An #GtkWrapBox
2274  *
2275  * Gets the allocation mode.
2276  *
2277  * Returns: The #GtkWrapAllocationMode for @box.
2278  */
2279 GtkWrapAllocationMode
2280 gtk_wrap_box_get_allocation_mode (GtkWrapBox *box)
2281 {
2282   g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
2283
2284   return box->priv->mode;
2285 }
2286
2287
2288 /**
2289  * gtk_wrap_box_set_horizontal_spreading:
2290  * @box: An #GtkWrapBox
2291  * @spreading: The #GtkWrapBoxSpreading to use.
2292  *
2293  * Sets the horizontal spreading mode for @box's children.
2294  */
2295 void
2296 gtk_wrap_box_set_horizontal_spreading (GtkWrapBox          *box,
2297                                        GtkWrapBoxSpreading  spreading)
2298 {
2299   GtkWrapBoxPrivate *priv;
2300
2301   g_return_if_fail (GTK_IS_WRAP_BOX (box));
2302
2303   priv = box->priv;
2304
2305   if (priv->horizontal_spreading != spreading)
2306     {
2307       priv->horizontal_spreading = spreading;
2308
2309       gtk_widget_queue_resize (GTK_WIDGET (box));
2310
2311       g_object_notify (G_OBJECT (box), "horizontal-spreading");
2312     }
2313 }
2314
2315 /**
2316  * gtk_wrap_box_get_horizontal_spreading:
2317  * @box: An #GtkWrapBox
2318  *
2319  * Gets the horizontal spreading mode.
2320  *
2321  * Returns: The horizontal #GtkWrapBoxSpreading for @box.
2322  */
2323 GtkWrapBoxSpreading
2324 gtk_wrap_box_get_horizontal_spreading (GtkWrapBox *box)
2325 {
2326   g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
2327
2328   return box->priv->horizontal_spreading;
2329 }
2330
2331
2332 /**
2333  * gtk_wrap_box_set_vertical_spreading:
2334  * @box: An #GtkWrapBox
2335  * @spreading: The #GtkWrapBoxSpreading to use.
2336  *
2337  * Sets the vertical spreading mode for @box's children.
2338  */
2339 void
2340 gtk_wrap_box_set_vertical_spreading (GtkWrapBox          *box,
2341                                      GtkWrapBoxSpreading  spreading)
2342 {
2343   GtkWrapBoxPrivate *priv;
2344
2345   g_return_if_fail (GTK_IS_WRAP_BOX (box));
2346
2347   priv = box->priv;
2348
2349   if (priv->vertical_spreading != spreading)
2350     {
2351       priv->vertical_spreading = spreading;
2352
2353       gtk_widget_queue_resize (GTK_WIDGET (box));
2354
2355       g_object_notify (G_OBJECT (box), "vertical-spreading");
2356     }
2357 }
2358
2359 /**
2360  * gtk_wrap_box_get_vertical_spreading:
2361  * @box: An #GtkWrapBox
2362  *
2363  * Gets the vertical spreading mode.
2364  *
2365  * Returns: The vertical #GtkWrapBoxSpreading for @box.
2366  */
2367 GtkWrapBoxSpreading
2368 gtk_wrap_box_get_vertical_spreading (GtkWrapBox *box)
2369 {
2370   g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
2371
2372   return box->priv->vertical_spreading;
2373 }
2374
2375
2376 /**
2377  * gtk_wrap_box_set_vertical_spacing:
2378  * @box: An #GtkWrapBox
2379  * @spacing: The spacing to use.
2380  *
2381  * Sets the vertical space to add between children.
2382  */
2383 void
2384 gtk_wrap_box_set_vertical_spacing  (GtkWrapBox    *box,
2385                                     guint          spacing)
2386 {
2387   GtkWrapBoxPrivate *priv;
2388
2389   g_return_if_fail (GTK_IS_WRAP_BOX (box));
2390
2391   priv = box->priv;
2392
2393   if (priv->vertical_spacing != spacing)
2394     {
2395       priv->vertical_spacing = spacing;
2396
2397       gtk_widget_queue_resize (GTK_WIDGET (box));
2398
2399       g_object_notify (G_OBJECT (box), "vertical-spacing");
2400     }
2401 }
2402
2403 /**
2404  * gtk_wrap_box_get_vertical_spacing:
2405  * @box: An #GtkWrapBox
2406  *
2407  * Gets the vertical spacing.
2408  *
2409  * Returns: The vertical spacing.
2410  */
2411 guint
2412 gtk_wrap_box_get_vertical_spacing  (GtkWrapBox *box)
2413 {
2414   g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
2415
2416   return box->priv->vertical_spacing;
2417 }
2418
2419 /**
2420  * gtk_wrap_box_set_horizontal_spacing:
2421  * @box: An #GtkWrapBox
2422  * @spacing: The spacing to use.
2423  *
2424  * Sets the horizontal space to add between children.
2425  */
2426 void
2427 gtk_wrap_box_set_horizontal_spacing (GtkWrapBox    *box,
2428                                      guint          spacing)
2429 {
2430   GtkWrapBoxPrivate *priv;
2431
2432   g_return_if_fail (GTK_IS_WRAP_BOX (box));
2433
2434   priv = box->priv;
2435
2436   if (priv->horizontal_spacing != spacing)
2437     {
2438       priv->horizontal_spacing = spacing;
2439
2440       gtk_widget_queue_resize (GTK_WIDGET (box));
2441
2442       g_object_notify (G_OBJECT (box), "horizontal-spacing");
2443     }
2444 }
2445
2446 /**
2447  * gtk_wrap_box_get_horizontal_spacing:
2448  * @box: An #GtkWrapBox
2449  *
2450  * Gets the horizontal spacing.
2451  *
2452  * Returns: The horizontal spacing.
2453  */
2454 guint
2455 gtk_wrap_box_get_horizontal_spacing (GtkWrapBox *box)
2456 {
2457   g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
2458
2459   return box->priv->horizontal_spacing;
2460 }
2461
2462 /**
2463  * gtk_wrap_box_set_minimum_line_children:
2464  * @box: An #GtkWrapBox
2465  * @n_children: The minimum amount of children per line.
2466  *
2467  * Sets the minimum amount of children to line up
2468  * in @box's orientation before wrapping.
2469  */
2470 void
2471 gtk_wrap_box_set_minimum_line_children (GtkWrapBox *box,
2472                                         guint       n_children)
2473 {
2474   GtkWrapBoxPrivate *priv;
2475
2476   g_return_if_fail (GTK_IS_WRAP_BOX (box));
2477
2478   priv = box->priv;
2479
2480   if (priv->minimum_line_children != n_children)
2481     {
2482       priv->minimum_line_children = n_children;
2483
2484       gtk_widget_queue_resize (GTK_WIDGET (box));
2485
2486       g_object_notify (G_OBJECT (box), "minimum-line-children");
2487     }
2488 }
2489
2490 /**
2491  * gtk_wrap_box_get_minimum_line_children:
2492  * @box: An #GtkWrapBox
2493  *
2494  * Gets the minimum amount of children per line.
2495  *
2496  * Returns: The minimum amount of children per line.
2497  */
2498 guint
2499 gtk_wrap_box_get_minimum_line_children (GtkWrapBox *box)
2500 {
2501   g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
2502
2503   return box->priv->minimum_line_children;
2504 }
2505
2506 /**
2507  * gtk_wrap_box_set_natural_line_children:
2508  * @box: An #GtkWrapBox
2509  * @n_children: The natural amount of children per line.
2510  *
2511  * Sets the natural length of items to request and
2512  * allocate space for in @box's orientation.
2513  *
2514  * Setting the natural amount of children per line
2515  * limits the overall natural size request to be no more
2516  * than @n_children items long in the given orientation.
2517  */
2518 void
2519 gtk_wrap_box_set_natural_line_children (GtkWrapBox *box,
2520                                         guint       n_children)
2521 {
2522   GtkWrapBoxPrivate *priv;
2523
2524   g_return_if_fail (GTK_IS_WRAP_BOX (box));
2525
2526   priv = box->priv;
2527
2528   if (priv->natural_line_children != n_children)
2529     {
2530       priv->natural_line_children = n_children;
2531
2532       gtk_widget_queue_resize (GTK_WIDGET (box));
2533
2534       g_object_notify (G_OBJECT (box), "natural-line-children");
2535     }
2536 }
2537
2538 /**
2539  * gtk_wrap_box_get_natural_line_children:
2540  * @box: An #GtkWrapBox
2541  *
2542  * Gets the natural amount of children per line.
2543  *
2544  * Returns: The natural amount of children per line.
2545  */
2546 guint
2547 gtk_wrap_box_get_natural_line_children (GtkWrapBox *box)
2548 {
2549   g_return_val_if_fail (GTK_IS_WRAP_BOX (box), FALSE);
2550
2551   return box->priv->natural_line_children;
2552 }
2553
2554
2555 /**
2556  * gtk_wrap_box_insert_child:
2557  * @box: And #GtkWrapBox
2558  * @widget: the child #GtkWidget to add
2559  * @index: the position in the child list to insert, specify -1 to append to the list.
2560  * @packing: The #GtkWrapBoxPacking options to use.
2561  *
2562  * Adds a child to an #GtkWrapBox with its packing options set
2563  *
2564  */
2565 void
2566 gtk_wrap_box_insert_child (GtkWrapBox        *box,
2567                            GtkWidget         *widget,
2568                            gint               index,
2569                            GtkWrapBoxPacking  packing)
2570 {
2571   GtkWrapBoxPrivate *priv;
2572   GtkWrapBoxChild   *child;
2573   GList             *list;
2574
2575   g_return_if_fail (GTK_IS_WRAP_BOX (box));
2576   g_return_if_fail (GTK_IS_WIDGET (widget));
2577
2578   priv = box->priv;
2579
2580   list = g_list_find_custom (priv->children, widget,
2581                              (GCompareFunc)find_child_in_list);
2582   g_return_if_fail (list == NULL);
2583
2584   child           = g_slice_new0 (GtkWrapBoxChild);
2585   child->widget   = widget;
2586   child->packing  = packing;
2587
2588   priv->children = g_list_insert (priv->children, child, index);
2589
2590   gtk_widget_set_parent (widget, GTK_WIDGET (box));
2591 }
2592
2593 /**
2594  * gtk_wrap_box_reorder_child:
2595  * @box: An #GtkWrapBox
2596  * @widget: The child to reorder
2597  * @index: The new child position
2598  *
2599  * Reorders the child @widget in @box's list of children.
2600  */
2601 void
2602 gtk_wrap_box_reorder_child (GtkWrapBox *box,
2603                             GtkWidget  *widget,
2604                             guint       index)
2605 {
2606   GtkWrapBoxPrivate *priv;
2607   GtkWrapBoxChild   *child;
2608   GList             *list;
2609
2610   g_return_if_fail (GTK_IS_WRAP_BOX (box));
2611   g_return_if_fail (GTK_IS_WIDGET (widget));
2612
2613   priv = box->priv;
2614
2615   list = g_list_find_custom (priv->children, widget,
2616                              (GCompareFunc)find_child_in_list);
2617   g_return_if_fail (list != NULL);
2618
2619   if (g_list_position (priv->children, list) != index)
2620     {
2621       child = list->data;
2622       priv->children = g_list_delete_link (priv->children, list);
2623       priv->children = g_list_insert (priv->children, child, index);
2624
2625       gtk_widget_queue_resize (GTK_WIDGET (box));
2626     }
2627 }