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