]> Pileus Git - ~andy/gtk/blob - gtk/gtksizerequest.c
sizerequestcache: Move commit function
[~andy/gtk] / gtk / gtksizerequest.c
1 /* gtksizerequest.c
2  * Copyright (C) 2007-2010 Openismus GmbH
3  *
4  * Authors:
5  *      Mathias Hasselmann <mathias@openismus.com>
6  *      Tristan Van Berkom <tristan.van.berkom@gmail.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <config.h>
23
24 #include "gtksizerequest.h"
25
26 #include "gtkdebug.h"
27 #include "gtkintl.h"
28 #include "gtkprivate.h"
29 #include "gtksizegroup-private.h"
30 #include "gtksizerequestcacheprivate.h"
31 #include "gtkwidgetprivate.h"
32 #include "deprecated/gtkstyle.h"
33
34
35 #ifndef G_DISABLE_CHECKS
36 static GQuark recursion_check_quark = 0;
37 #endif /* G_DISABLE_CHECKS */
38
39 static void
40 push_recursion_check (GtkWidget       *widget,
41                       GtkSizeGroupMode orientation,
42                       gint             for_size)
43 {
44 #ifndef G_DISABLE_CHECKS
45   const char *previous_method;
46   const char *method;
47
48   if (recursion_check_quark == 0)
49     recursion_check_quark = g_quark_from_static_string ("gtk-size-request-in-progress");
50
51   previous_method = g_object_get_qdata (G_OBJECT (widget), recursion_check_quark);
52
53   if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
54     {
55       method = for_size < 0 ? "get_width" : "get_width_for_height";
56     }
57   else
58     {
59       method = for_size < 0 ? "get_height" : "get_height_for_width";
60     }
61
62   if (previous_method != NULL)
63     {
64       g_warning ("%s %p: widget tried to gtk_widget_%s inside "
65                  " GtkWidget     ::%s implementation. "
66                  "Should just invoke GTK_WIDGET_GET_CLASS(widget)->%s "
67                  "directly rather than using gtk_widget_%s",
68                  G_OBJECT_TYPE_NAME (widget), widget,
69                  method, previous_method,
70                  method, method);
71     }
72
73   g_object_set_qdata (G_OBJECT (widget), recursion_check_quark, (char*) method);
74 #endif /* G_DISABLE_CHECKS */
75 }
76
77 static void
78 pop_recursion_check (GtkWidget       *widget,
79                      GtkSizeGroupMode orientation)
80 {
81 #ifndef G_DISABLE_CHECKS
82   g_object_set_qdata (G_OBJECT (widget), recursion_check_quark, NULL);
83 #endif
84 }
85
86
87 /* looks for a cached size request for this for_size. If not
88  * found, returns the oldest entry so it can be overwritten
89  *
90  * Note that this caching code was originally derived from
91  * the Clutter toolkit but has evolved for other GTK+ requirements.
92  */
93 static gboolean
94 get_cached_size (GtkWidget         *widget,
95                  GtkSizeGroupMode   orientation,
96                  gint               for_size,
97                  CachedSize       **result)
98 {
99   SizeRequestCache  *cache;
100   SizeRequest      **cached_sizes;
101   guint              i, n_sizes;
102
103   cache = _gtk_widget_peek_request_cache (widget);
104
105   if (for_size < 0)
106     {
107       if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
108         {
109           *result = &cache->cached_width;
110           return cache->cached_base_width;
111         }
112       else
113         {
114           *result = &cache->cached_height;
115           return cache->cached_base_height;
116         }
117     }
118
119   if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
120     {
121       cached_sizes = cache->widths;
122       n_sizes      = cache->cached_widths;
123     }
124   else
125     {
126       cached_sizes = cache->heights;
127       n_sizes      = cache->cached_heights;
128     }
129
130   /* Search for an already cached size */
131   for (i = 0; i < n_sizes; i++)
132     {
133       if (cached_sizes[i]->lower_for_size <= for_size &&
134           cached_sizes[i]->upper_for_size >= for_size)
135         {
136           *result = &cached_sizes[i]->cached_size;
137           return TRUE;
138         }
139     }
140
141   return FALSE;
142 }
143
144 static const char *
145 get_vfunc_name (GtkSizeGroupMode orientation,
146                 gint             for_size)
147 {
148   if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
149     return for_size < 0 ? "get_preferred_width" : "get_preferred_width_for_height";
150   else
151     return for_size < 0 ? "get_preferred_height" : "get_preferred_height_for_width";
152 }
153
154 static void
155 gtk_widget_query_size_for_orientation (GtkWidget        *widget,
156                                        GtkSizeGroupMode  orientation,
157                                        gint              for_size,
158                                        gint             *minimum_size,
159                                        gint             *natural_size)
160 {
161   CachedSize *cached_size;
162   gboolean    found_in_cache = FALSE;
163   gint        min_size = 0;
164   gint        nat_size = 0;
165
166   found_in_cache = get_cached_size (widget, orientation, for_size, &cached_size);
167
168   if (!found_in_cache)
169     {
170       gint adjusted_min, adjusted_natural, adjusted_for_size = for_size;
171
172       gtk_widget_ensure_style (widget);
173
174       if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
175         {
176           if (for_size < 0 || gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_CONSTANT_SIZE)
177             {
178               push_recursion_check (widget, orientation, for_size);
179               GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_size, &nat_size);
180               pop_recursion_check (widget, orientation);
181             }
182           else
183             {
184               gint ignored_position = 0;
185               gint minimum_height;
186               gint natural_height;
187
188               /* Pull the base natural height from the cache as it's needed to adjust
189                * the proposed 'for_size' */
190               gtk_widget_get_preferred_height (widget, &minimum_height, &natural_height);
191
192               /* convert for_size to unadjusted height (for_size is a proposed allocation) */
193               GTK_WIDGET_GET_CLASS (widget)->adjust_size_allocation (widget,
194                                                                      GTK_ORIENTATION_VERTICAL,
195                                                                      &minimum_height,
196                                                                      &natural_height,
197                                                                      &ignored_position,
198                                                                      &adjusted_for_size);
199
200               push_recursion_check (widget, orientation, for_size);
201               GTK_WIDGET_GET_CLASS (widget)->get_preferred_width_for_height (widget, 
202                                                                              MAX (adjusted_for_size, minimum_height),
203                                                                              &min_size, &nat_size);
204               pop_recursion_check (widget, orientation);
205             }
206         }
207       else
208         {
209           if (for_size < 0 || gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_CONSTANT_SIZE)
210             {
211               push_recursion_check (widget, orientation, for_size);
212               GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_size, &nat_size);
213               pop_recursion_check (widget, orientation);
214             }
215           else
216             {
217               gint ignored_position = 0;
218               gint minimum_width;
219               gint natural_width;
220
221               /* Pull the base natural width from the cache as it's needed to adjust
222                * the proposed 'for_size' */
223               gtk_widget_get_preferred_width (widget, &minimum_width, &natural_width);
224
225               /* convert for_size to unadjusted width (for_size is a proposed allocation) */
226               GTK_WIDGET_GET_CLASS (widget)->adjust_size_allocation (widget,
227                                                                      GTK_ORIENTATION_HORIZONTAL,
228                                                                      &minimum_width,
229                                                                      &natural_width,
230                                                                      &ignored_position,
231                                                                      &adjusted_for_size);
232
233               push_recursion_check (widget, orientation, for_size);
234               GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, 
235                                                                              MAX (adjusted_for_size, minimum_width),
236                                                                              &min_size, &nat_size);
237               pop_recursion_check (widget, orientation);
238             }
239         }
240
241       if (min_size > nat_size)
242         {
243           g_warning ("%s %p reported min size %d and natural size %d in %s(); natural size must be >= min size",
244                      G_OBJECT_TYPE_NAME (widget), widget, min_size, nat_size, get_vfunc_name (orientation, for_size));
245         }
246
247       adjusted_min     = min_size;
248       adjusted_natural = nat_size;
249       GTK_WIDGET_GET_CLASS (widget)->adjust_size_request (widget,
250                                                           orientation == GTK_SIZE_GROUP_HORIZONTAL ?
251                                                           GTK_ORIENTATION_HORIZONTAL :
252                                                           GTK_ORIENTATION_VERTICAL,
253                                                           &adjusted_min,
254                                                           &adjusted_natural);
255
256       if (adjusted_min < min_size ||
257           adjusted_natural < nat_size)
258         {
259           g_warning ("%s %p adjusted size %s min %d natural %d must not decrease below min %d natural %d",
260                      G_OBJECT_TYPE_NAME (widget), widget,
261                      orientation == GTK_SIZE_GROUP_VERTICAL ? "vertical" : "horizontal",
262                      adjusted_min, adjusted_natural,
263                      min_size, nat_size);
264           /* don't use the adjustment */
265         }
266       else if (adjusted_min > adjusted_natural)
267         {
268           g_warning ("%s %p adjusted size %s min %d natural %d original min %d natural %d has min greater than natural",
269                      G_OBJECT_TYPE_NAME (widget), widget,
270                      orientation == GTK_SIZE_GROUP_VERTICAL ? "vertical" : "horizontal",
271                      adjusted_min, adjusted_natural,
272                      min_size, nat_size);
273           /* don't use the adjustment */
274         }
275       else
276         {
277           /* adjustment looks good */
278           min_size = adjusted_min;
279           nat_size = adjusted_natural;
280         }
281
282       _gtk_size_request_cache_commit (_gtk_widget_peek_request_cache (widget),
283                                       orientation,
284                                       for_size,
285                                       min_size,
286                                       nat_size);
287     }
288   else
289     {
290       min_size = cached_size->minimum_size;
291       nat_size = cached_size->natural_size;
292     }
293
294   if (minimum_size)
295     *minimum_size = min_size;
296
297   if (natural_size)
298     *natural_size = nat_size;
299
300   g_assert (min_size <= nat_size);
301
302   GTK_NOTE (SIZE_REQUEST,
303             g_print ("[%p] %s\t%s: %d is minimum %d and natural: %d (hit cache: %s)\n",
304                      widget, G_OBJECT_TYPE_NAME (widget),
305                      orientation == GTK_SIZE_GROUP_HORIZONTAL ?
306                      "width for height" : "height for width" ,
307                      for_size, min_size, nat_size,
308                      found_in_cache ? "yes" : "no"));
309
310 }
311
312 /* This is the main function that checks for a cached size and
313  * possibly queries the widget class to compute the size if it's
314  * not cached. If the for_size here is -1, then get_preferred_width()
315  * or get_preferred_height() will be used.
316  */
317 void
318 _gtk_widget_compute_size_for_orientation (GtkWidget        *widget,
319                                           GtkSizeGroupMode  mode,
320                                           gint              for_size,
321                                           gint             *minimum,
322                                           gint             *natural)
323 {
324   GHashTable *widgets;
325   GHashTableIter iter;
326   gpointer key;
327   gint    min_result = 0, nat_result = 0;
328
329   if (G_LIKELY (!_gtk_widget_get_sizegroups (widget)))
330     {
331       gtk_widget_query_size_for_orientation (widget, mode, for_size, minimum, natural);
332       return;
333     }
334
335   widgets = _gtk_size_group_get_widget_peers (widget, mode);
336
337   g_hash_table_foreach (widgets, (GHFunc) g_object_ref, NULL);
338   
339   g_hash_table_iter_init (&iter, widgets);
340   while (g_hash_table_iter_next (&iter, &key, NULL))
341     {
342       GtkWidget *tmp_widget = key;
343       gint min_dimension, nat_dimension;
344
345       gtk_widget_query_size_for_orientation (tmp_widget, mode, for_size, &min_dimension, &nat_dimension);
346
347       min_result = MAX (min_result, min_dimension);
348       nat_result = MAX (nat_result, nat_dimension);
349     }
350
351   g_hash_table_foreach (widgets, (GHFunc) g_object_unref, NULL);
352
353   g_hash_table_destroy (widgets);
354
355   if (minimum)
356     *minimum = min_result;
357
358   if (natural)
359     *natural = nat_result;
360 }
361
362 /**
363  * gtk_widget_get_request_mode:
364  * @widget: a #GtkWidget instance
365  *
366  * Gets whether the widget prefers a height-for-width layout
367  * or a width-for-height layout.
368  *
369  * <note><para>#GtkBin widgets generally propagate the preference of
370  * their child, container widgets need to request something either in
371  * context of their children or in context of their allocation
372  * capabilities.</para></note>
373  *
374  * Returns: The #GtkSizeRequestMode preferred by @widget.
375  *
376  * Since: 3.0
377  */
378 GtkSizeRequestMode
379 gtk_widget_get_request_mode (GtkWidget *widget)
380 {
381   SizeRequestCache *cache;
382
383   g_return_val_if_fail (GTK_IS_WIDGET (widget), GTK_SIZE_REQUEST_CONSTANT_SIZE);
384
385   cache = _gtk_widget_peek_request_cache (widget);
386
387   if (!cache->request_mode_valid)
388     {
389       cache->request_mode = GTK_WIDGET_GET_CLASS (widget)->get_request_mode (widget);
390       cache->request_mode_valid = TRUE;
391     }
392
393   return cache->request_mode;
394 }
395
396 /**
397  * gtk_widget_get_preferred_width:
398  * @widget: a #GtkWidget instance
399  * @minimum_width: (out) (allow-none): location to store the minimum width, or %NULL
400  * @natural_width: (out) (allow-none): location to store the natural width, or %NULL
401  *
402  * Retrieves a widget's initial minimum and natural width.
403  *
404  * <note><para>This call is specific to height-for-width
405  * requests.</para></note>
406  *
407  * The returned request will be modified by the
408  * GtkWidgetClass::adjust_size_request virtual method and by any
409  * #GtkSizeGroup<!-- -->s that have been applied. That is, the returned request
410  * is the one that should be used for layout, not necessarily the one
411  * returned by the widget itself.
412  *
413  * Since: 3.0
414  */
415 void
416 gtk_widget_get_preferred_width (GtkWidget *widget,
417                                 gint      *minimum_width,
418                                 gint      *natural_width)
419 {
420   g_return_if_fail (GTK_IS_WIDGET (widget));
421   g_return_if_fail (minimum_width != NULL || natural_width != NULL);
422
423   _gtk_widget_compute_size_for_orientation (widget,
424                                             GTK_SIZE_GROUP_HORIZONTAL,
425                                             -1,
426                                             minimum_width,
427                                             natural_width);
428 }
429
430
431 /**
432  * gtk_widget_get_preferred_height:
433  * @widget: a #GtkWidget instance
434  * @minimum_height: (out) (allow-none): location to store the minimum height, or %NULL
435  * @natural_height: (out) (allow-none): location to store the natural height, or %NULL
436  *
437  * Retrieves a widget's initial minimum and natural height.
438  *
439  * <note><para>This call is specific to width-for-height requests.</para></note>
440  *
441  * The returned request will be modified by the
442  * GtkWidgetClass::adjust_size_request virtual method and by any
443  * #GtkSizeGroup<!-- -->s that have been applied. That is, the returned request
444  * is the one that should be used for layout, not necessarily the one
445  * returned by the widget itself.
446  *
447  * Since: 3.0
448  */
449 void
450 gtk_widget_get_preferred_height (GtkWidget *widget,
451                                  gint      *minimum_height,
452                                  gint      *natural_height)
453 {
454   g_return_if_fail (GTK_IS_WIDGET (widget));
455   g_return_if_fail (minimum_height != NULL || natural_height != NULL);
456
457   _gtk_widget_compute_size_for_orientation (widget,
458                                             GTK_SIZE_GROUP_VERTICAL,
459                                             -1,
460                                             minimum_height,
461                                             natural_height);
462 }
463
464
465
466 /**
467  * gtk_widget_get_preferred_width_for_height:
468  * @widget: a #GtkWidget instance
469  * @height: the height which is available for allocation
470  * @minimum_width: (out) (allow-none): location for storing the minimum width, or %NULL
471  * @natural_width: (out) (allow-none): location for storing the natural width, or %NULL
472  *
473  * Retrieves a widget's minimum and natural width if it would be given
474  * the specified @height.
475  *
476  * The returned request will be modified by the
477  * GtkWidgetClass::adjust_size_request virtual method and by any
478  * #GtkSizeGroup<!-- -->s that have been applied. That is, the returned request
479  * is the one that should be used for layout, not necessarily the one
480  * returned by the widget itself.
481  *
482  * Since: 3.0
483  */
484 void
485 gtk_widget_get_preferred_width_for_height (GtkWidget *widget,
486                                            gint       height,
487                                            gint      *minimum_width,
488                                            gint      *natural_width)
489 {
490   g_return_if_fail (GTK_IS_WIDGET (widget));
491   g_return_if_fail (minimum_width != NULL || natural_width != NULL);
492   g_return_if_fail (height >= 0);
493
494   _gtk_widget_compute_size_for_orientation (widget,
495                                             GTK_SIZE_GROUP_HORIZONTAL,
496                                             height,
497                                             minimum_width,
498                                             natural_width);
499 }
500
501 /**
502  * gtk_widget_get_preferred_height_for_width:
503  * @widget: a #GtkWidget instance
504  * @width: the width which is available for allocation
505  * @minimum_height: (out) (allow-none): location for storing the minimum height, or %NULL
506  * @natural_height: (out) (allow-none): location for storing the natural height, or %NULL
507  *
508  * Retrieves a widget's minimum and natural height if it would be given
509  * the specified @width.
510  *
511  * The returned request will be modified by the
512  * GtkWidgetClass::adjust_size_request virtual method and by any
513  * #GtkSizeGroup<!-- -->s that have been applied. That is, the returned request
514  * is the one that should be used for layout, not necessarily the one
515  * returned by the widget itself.
516  *
517  * Since: 3.0
518  */
519 void
520 gtk_widget_get_preferred_height_for_width (GtkWidget *widget,
521                                            gint       width,
522                                            gint      *minimum_height,
523                                            gint      *natural_height)
524 {
525   g_return_if_fail (GTK_IS_WIDGET (widget));
526   g_return_if_fail (minimum_height != NULL || natural_height != NULL);
527   g_return_if_fail (width >= 0);
528
529   _gtk_widget_compute_size_for_orientation (widget,
530                                             GTK_SIZE_GROUP_VERTICAL,
531                                             width,
532                                             minimum_height,
533                                             natural_height);
534 }
535
536 /**
537  * gtk_widget_get_preferred_size:
538  * @widget: a #GtkWidget instance
539  * @minimum_size: (out) (allow-none): location for storing the minimum size, or %NULL
540  * @natural_size: (out) (allow-none): location for storing the natural size, or %NULL
541  *
542  * Retrieves the minimum and natural size of a widget, taking
543  * into account the widget's preference for height-for-width management.
544  *
545  * This is used to retrieve a suitable size by container widgets which do
546  * not impose any restrictions on the child placement. It can be used
547  * to deduce toplevel window and menu sizes as well as child widgets in
548  * free-form containers such as GtkLayout.
549  *
550  * <note><para>Handle with care. Note that the natural height of a height-for-width
551  * widget will generally be a smaller size than the minimum height, since the required
552  * height for the natural width is generally smaller than the required height for
553  * the minimum width.</para></note>
554  *
555  * Since: 3.0
556  */
557 void
558 gtk_widget_get_preferred_size (GtkWidget      *widget,
559                                GtkRequisition *minimum_size,
560                                GtkRequisition *natural_size)
561 {
562   gint min_width, nat_width;
563   gint min_height, nat_height;
564
565   g_return_if_fail (GTK_IS_WIDGET (widget));
566
567   if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
568     {
569       gtk_widget_get_preferred_width (widget, &min_width, &nat_width);
570
571       if (minimum_size)
572         {
573           minimum_size->width = min_width;
574           gtk_widget_get_preferred_height_for_width (widget, min_width,
575                                                      &minimum_size->height, NULL);
576         }
577
578       if (natural_size)
579         {
580           natural_size->width = nat_width;
581           gtk_widget_get_preferred_height_for_width (widget, nat_width,
582                                                      NULL, &natural_size->height);
583         }
584     }
585   else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT or CONSTANT_SIZE */
586     {
587       gtk_widget_get_preferred_height (widget, &min_height, &nat_height);
588
589       if (minimum_size)
590         {
591           minimum_size->height = min_height;
592           gtk_widget_get_preferred_width_for_height (widget, min_height,
593                                                      &minimum_size->width, NULL);
594         }
595
596       if (natural_size)
597         {
598           natural_size->height = nat_height;
599           gtk_widget_get_preferred_width_for_height (widget, nat_height,
600                                                      NULL, &natural_size->width);
601         }
602     }
603 }
604
605
606 static gint
607 compare_gap (gconstpointer p1,
608              gconstpointer p2,
609              gpointer      data)
610 {
611   GtkRequestedSize *sizes = data;
612   const guint *c1 = p1;
613   const guint *c2 = p2;
614
615   const gint d1 = MAX (sizes[*c1].natural_size -
616                        sizes[*c1].minimum_size,
617                        0);
618   const gint d2 = MAX (sizes[*c2].natural_size -
619                        sizes[*c2].minimum_size,
620                        0);
621
622   gint delta = (d2 - d1);
623
624   if (0 == delta)
625     delta = (*c2 - *c1);
626
627   return delta;
628 }
629
630 /**
631  * gtk_distribute_natural_allocation:
632  * @extra_space: Extra space to redistribute among children after subtracting
633  *               minimum sizes and any child padding from the overall allocation
634  * @n_requested_sizes: Number of requests to fit into the allocation
635  * @sizes: An array of structs with a client pointer and a minimum/natural size
636  *         in the orientation of the allocation.
637  *
638  * Distributes @extra_space to child @sizes by bringing smaller
639  * children up to natural size first.
640  *
641  * The remaining space will be added to the @minimum_size member of the
642  * GtkRequestedSize struct. If all sizes reach their natural size then
643  * the remaining space is returned.
644  *
645  * Returns: The remainder of @extra_space after redistributing space
646  * to @sizes.
647  */
648 gint
649 gtk_distribute_natural_allocation (gint              extra_space,
650                                    guint             n_requested_sizes,
651                                    GtkRequestedSize *sizes)
652 {
653   guint *spreading;
654   gint   i;
655
656   g_return_val_if_fail (extra_space >= 0, 0);
657
658   spreading = g_newa (guint, n_requested_sizes);
659
660   for (i = 0; i < n_requested_sizes; i++)
661     spreading[i] = i;
662
663   /* Distribute the container's extra space c_gap. We want to assign
664    * this space such that the sum of extra space assigned to children
665    * (c^i_gap) is equal to c_cap. The case that there's not enough
666    * space for all children to take their natural size needs some
667    * attention. The goals we want to achieve are:
668    *
669    *   a) Maximize number of children taking their natural size.
670    *   b) The allocated size of children should be a continuous
671    *   function of c_gap.  That is, increasing the container size by
672    *   one pixel should never make drastic changes in the distribution.
673    *   c) If child i takes its natural size and child j doesn't,
674    *   child j should have received at least as much gap as child i.
675    *
676    * The following code distributes the additional space by following
677    * these rules.
678    */
679
680   /* Sort descending by gap and position. */
681   g_qsort_with_data (spreading,
682                      n_requested_sizes, sizeof (guint),
683                      compare_gap, sizes);
684
685   /* Distribute available space.
686    * This master piece of a loop was conceived by Behdad Esfahbod.
687    */
688   for (i = n_requested_sizes - 1; extra_space > 0 && i >= 0; --i)
689     {
690       /* Divide remaining space by number of remaining children.
691        * Sort order and reducing remaining space by assigned space
692        * ensures that space is distributed equally.
693        */
694       gint glue = (extra_space + i) / (i + 1);
695       gint gap = sizes[(spreading[i])].natural_size
696         - sizes[(spreading[i])].minimum_size;
697
698       gint extra = MIN (glue, gap);
699
700       sizes[spreading[i]].minimum_size += extra;
701
702       extra_space -= extra;
703     }
704
705   return extra_space;
706 }