]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellareaboxcontext.c
Merge branch 'master' into treeview-refactor
[~andy/gtk] / gtk / gtkcellareaboxcontext.c
1 /* gtkcellareaboxcontext.c
2  *
3  * Copyright (C) 2010 Openismus GmbH
4  *
5  * Authors:
6  *      Tristan Van Berkom <tristanvb@openismus.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 #include "gtkintl.h"
26 #include "gtkcellareabox.h"
27 #include "gtkcellareaboxcontext.h"
28 #include "gtkorientable.h"
29
30 /* GObjectClass */
31 static void      gtk_cell_area_box_context_finalize                         (GObject            *object);
32
33 /* GtkCellAreaContextClass */
34 static void      gtk_cell_area_box_context_flush_preferred_width            (GtkCellAreaContext *context);
35 static void      gtk_cell_area_box_context_flush_preferred_height_for_width (GtkCellAreaContext *context,
36                                                                              gint                width);
37 static void      gtk_cell_area_box_context_flush_preferred_height           (GtkCellAreaContext *context);
38 static void      gtk_cell_area_box_context_flush_preferred_width_for_height (GtkCellAreaContext *context,
39                                                                              gint                height);
40 static void      gtk_cell_area_box_context_flush_allocation                 (GtkCellAreaContext *context);
41 static void      gtk_cell_area_box_context_sum_preferred_width              (GtkCellAreaContext *context);
42 static void      gtk_cell_area_box_context_sum_preferred_height_for_width   (GtkCellAreaContext *context,
43                                                                              gint                width);
44 static void      gtk_cell_area_box_context_sum_preferred_height             (GtkCellAreaContext *context);
45 static void      gtk_cell_area_box_context_sum_preferred_width_for_height   (GtkCellAreaContext *context,
46                                                                              gint                height);
47 static void      gtk_cell_area_box_context_allocate_width                   (GtkCellAreaContext *context,
48                                                                              gint                width);
49 static void      gtk_cell_area_box_context_allocate_height                  (GtkCellAreaContext *context,
50                                                                              gint                height);
51
52 static void      free_cache_array                                           (GArray          *array);
53
54 /* CachedSize management */
55 typedef struct {
56   gint     min_size;
57   gint     nat_size;
58 } CachedSize;
59
60 typedef struct {
61   gint     min_size;
62   gint     nat_size;
63   gboolean expand;
64 } BaseSize;
65
66 struct _GtkCellAreaBoxContextPrivate
67 {
68   /* Table of per renderer CachedSizes */
69   GArray *base_widths;
70   GArray *base_heights;
71
72   /* Table of per height/width hash tables of per renderer CachedSizes */
73   GHashTable *widths;
74   GHashTable *heights;
75
76   /* Allocation info for this context if any */
77   gint                      alloc_width;
78   gint                      alloc_height;
79   gint                      n_orientation_allocs;
80   GtkCellAreaBoxAllocation *orientation_allocs;
81 };
82
83 G_DEFINE_TYPE (GtkCellAreaBoxContext, gtk_cell_area_box_context, GTK_TYPE_CELL_AREA_CONTEXT);
84
85 static void
86 free_cache_array (GArray *array)
87 {
88   g_array_free (array, TRUE);
89 }
90
91 static void
92 gtk_cell_area_box_context_init (GtkCellAreaBoxContext *box_context)
93 {
94   GtkCellAreaBoxContextPrivate *priv;
95
96   box_context->priv = G_TYPE_INSTANCE_GET_PRIVATE (box_context,
97                                                    GTK_TYPE_CELL_AREA_BOX_CONTEXT,
98                                                    GtkCellAreaBoxContextPrivate);
99   priv = box_context->priv;
100
101   priv->base_widths  = g_array_new (FALSE, TRUE, sizeof (BaseSize));
102   priv->base_heights = g_array_new (FALSE, TRUE, sizeof (BaseSize));
103
104   priv->widths       = g_hash_table_new_full (g_direct_hash, g_direct_equal,
105                                               NULL, (GDestroyNotify)free_cache_array);
106   priv->heights      = g_hash_table_new_full (g_direct_hash, g_direct_equal,
107                                               NULL, (GDestroyNotify)free_cache_array);
108
109   priv->alloc_width  = 0;
110   priv->alloc_height = 0;
111   priv->orientation_allocs   = NULL;
112   priv->n_orientation_allocs = 0;
113 }
114
115 static void 
116 gtk_cell_area_box_context_class_init (GtkCellAreaBoxContextClass *class)
117 {
118   GObjectClass            *object_class = G_OBJECT_CLASS (class);
119   GtkCellAreaContextClass *context_class   = GTK_CELL_AREA_CONTEXT_CLASS (class);
120
121   /* GObjectClass */
122   object_class->finalize = gtk_cell_area_box_context_finalize;
123
124   context_class->flush_preferred_width            = gtk_cell_area_box_context_flush_preferred_width;
125   context_class->flush_preferred_height_for_width = gtk_cell_area_box_context_flush_preferred_height_for_width;
126   context_class->flush_preferred_height           = gtk_cell_area_box_context_flush_preferred_height;
127   context_class->flush_preferred_width_for_height = gtk_cell_area_box_context_flush_preferred_width_for_height;
128   context_class->flush_allocation                 = gtk_cell_area_box_context_flush_allocation;
129
130   context_class->sum_preferred_width            = gtk_cell_area_box_context_sum_preferred_width;
131   context_class->sum_preferred_height_for_width = gtk_cell_area_box_context_sum_preferred_height_for_width;
132   context_class->sum_preferred_height           = gtk_cell_area_box_context_sum_preferred_height;
133   context_class->sum_preferred_width_for_height = gtk_cell_area_box_context_sum_preferred_width_for_height;
134
135   context_class->allocate_width  = gtk_cell_area_box_context_allocate_width;
136   context_class->allocate_height = gtk_cell_area_box_context_allocate_height;
137
138   g_type_class_add_private (object_class, sizeof (GtkCellAreaBoxContextPrivate));
139 }
140
141 /*************************************************************
142  *                      GObjectClass                         *
143  *************************************************************/
144 static void
145 gtk_cell_area_box_context_finalize (GObject *object)
146 {
147   GtkCellAreaBoxContext        *box_context = GTK_CELL_AREA_BOX_CONTEXT (object);
148   GtkCellAreaBoxContextPrivate *priv        = box_context->priv;
149
150   g_array_free (priv->base_widths, TRUE);
151   g_array_free (priv->base_heights, TRUE);
152   g_hash_table_destroy (priv->widths);
153   g_hash_table_destroy (priv->heights);
154
155   g_free (priv->orientation_allocs);
156
157   G_OBJECT_CLASS (gtk_cell_area_box_context_parent_class)->finalize (object);
158 }
159
160 /*************************************************************
161  *                    GtkCellAreaContextClass                   *
162  *************************************************************/
163 static void
164 gtk_cell_area_box_context_flush_preferred_width (GtkCellAreaContext *context)
165 {
166   GtkCellAreaBoxContext        *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
167   GtkCellAreaBoxContextPrivate *priv        = box_context->priv;
168   gint                          i;
169
170   for (i = 0; i < priv->base_widths->len; i++)
171     {
172       BaseSize *size = &g_array_index (priv->base_widths, BaseSize, i);
173
174       size->min_size = 0;
175       size->nat_size = 0;
176     }
177
178   GTK_CELL_AREA_CONTEXT_CLASS
179     (gtk_cell_area_box_context_parent_class)->flush_preferred_width (context);
180 }
181
182 static void
183 gtk_cell_area_box_context_flush_preferred_height_for_width (GtkCellAreaContext *context,
184                                                             gint                width)
185 {
186   GtkCellAreaBoxContext        *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
187   GtkCellAreaBoxContextPrivate *priv        = box_context->priv;
188
189   /* Flush all sizes for special -1 value */
190   if (width < 0)
191     g_hash_table_remove_all (priv->heights);
192   else
193     g_hash_table_remove (priv->heights, GINT_TO_POINTER (width));
194
195   GTK_CELL_AREA_CONTEXT_CLASS
196     (gtk_cell_area_box_context_parent_class)->flush_preferred_height_for_width (context, width);
197 }
198
199 static void
200 gtk_cell_area_box_context_flush_preferred_height (GtkCellAreaContext *context)
201 {
202   GtkCellAreaBoxContext        *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
203   GtkCellAreaBoxContextPrivate *priv        = box_context->priv;
204   gint                          i;
205
206   for (i = 0; i < priv->base_heights->len; i++)
207     {
208       BaseSize *size = &g_array_index (priv->base_heights, BaseSize, i);
209
210       size->min_size = 0;
211       size->nat_size = 0;
212     }
213
214   GTK_CELL_AREA_CONTEXT_CLASS
215     (gtk_cell_area_box_context_parent_class)->flush_preferred_height (context);
216 }
217
218 static void
219 gtk_cell_area_box_context_flush_preferred_width_for_height (GtkCellAreaContext *context,
220                                                             gint                height)
221 {
222   GtkCellAreaBoxContext        *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
223   GtkCellAreaBoxContextPrivate *priv        = box_context->priv;
224
225   /* Flush all sizes for special -1 value */
226   if (height < 0)
227     g_hash_table_remove_all (priv->widths);
228   else
229     g_hash_table_remove (priv->widths, GINT_TO_POINTER (height));
230
231   GTK_CELL_AREA_CONTEXT_CLASS
232     (gtk_cell_area_box_context_parent_class)->flush_preferred_width_for_height (context, height);
233 }
234
235 static void
236 gtk_cell_area_box_context_flush_allocation (GtkCellAreaContext *context)
237 {
238   GtkCellAreaBoxContext        *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
239   GtkCellAreaBoxContextPrivate *priv        = box_context->priv;
240
241   g_free (priv->orientation_allocs);
242   priv->orientation_allocs   = NULL;
243   priv->n_orientation_allocs = 0;
244 }
245
246 static void
247 gtk_cell_area_box_context_sum_preferred_width (GtkCellAreaContext *context)
248 {
249   GtkCellAreaBoxContext        *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
250   GtkCellAreaBoxContextPrivate *priv        = box_context->priv;
251   GtkCellArea                  *area;
252   GtkOrientation                orientation;
253   gint                          spacing, i;
254   gint                          min_size = 0, nat_size = 0;
255
256   area        = gtk_cell_area_context_get_area (context);
257   spacing     = gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (area));
258   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area));
259
260   for (i = 0; i < priv->base_widths->len; i++)
261     {
262       BaseSize *size = &g_array_index (priv->base_widths, BaseSize, i);
263
264       if (orientation == GTK_ORIENTATION_HORIZONTAL)
265         {
266           /* Dont add spacing for 0 size groups, they can be 0 size because
267            * they contain only invisible cells for this round of requests 
268            */
269           if (min_size > 0 && size->nat_size > 0)
270             {
271               min_size += spacing;
272               nat_size += spacing;
273             }
274           
275           min_size += size->min_size;
276           nat_size += size->nat_size;
277         }
278       else
279         {
280           min_size = MAX (min_size, size->min_size);
281           nat_size = MAX (nat_size, size->nat_size);
282         }
283     }
284
285   gtk_cell_area_context_push_preferred_width (context, min_size, nat_size);
286 }
287
288 static void
289 gtk_cell_area_box_context_sum_preferred_height_for_width (GtkCellAreaContext *context,
290                                                           gint                width)
291 {
292   GtkCellAreaBoxContext        *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
293   GtkCellAreaBoxContextPrivate *priv        = box_context->priv;
294   GArray                    *group_array;
295   GtkCellArea               *area;
296   GtkOrientation             orientation;
297   gint                       spacing, i;
298   gint                       min_size = 0, nat_size = 0;
299
300   group_array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (width));
301
302   if (group_array)
303     {
304       area        = gtk_cell_area_context_get_area (context);
305       spacing     = gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (area));
306       orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area));
307       
308       for (i = 0; i < group_array->len; i++)
309         {
310           CachedSize *size = &g_array_index (group_array, CachedSize, i);
311           
312           if (orientation == GTK_ORIENTATION_VERTICAL)
313             {
314               /* Dont add spacing for 0 size groups, they can be 0 size because
315                * they contain only invisible cells for this round of requests 
316                */
317               if (min_size > 0 && size->nat_size > 0)
318                 {
319                   min_size += spacing;
320                   nat_size += spacing;
321                 }
322               
323               min_size += size->min_size;
324               nat_size += size->nat_size;
325             }
326           else
327             {
328               min_size = MAX (min_size, size->min_size);
329               nat_size = MAX (nat_size, size->nat_size);
330             }
331         }
332
333       gtk_cell_area_context_push_preferred_height_for_width (context, width, min_size, nat_size);
334     }
335 }
336
337 static void
338 gtk_cell_area_box_context_sum_preferred_height (GtkCellAreaContext *context)
339 {
340   GtkCellAreaBoxContext        *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
341   GtkCellAreaBoxContextPrivate *priv     = box_context->priv;
342   GtkCellArea                  *area;
343   GtkOrientation                orientation;
344   gint                          spacing, i;
345   gint                          min_size = 0, nat_size = 0;
346
347   area        = gtk_cell_area_context_get_area (context);
348   spacing     = gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (area));
349   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area));
350
351   for (i = 0; i < priv->base_heights->len; i++)
352     {
353       BaseSize *size = &g_array_index (priv->base_heights, BaseSize, i);
354
355       if (orientation == GTK_ORIENTATION_VERTICAL)
356         {
357           /* Dont add spacing for 0 size groups, they can be 0 size because
358            * they contain only invisible cells for this round of requests 
359            */
360           if (min_size > 0 && size->nat_size > 0)
361             {
362               min_size += spacing;
363               nat_size += spacing;
364             }
365           
366           min_size += size->min_size;
367           nat_size += size->nat_size;
368         }
369       else
370         {
371           min_size = MAX (min_size, size->min_size);
372           nat_size = MAX (nat_size, size->nat_size);
373         }
374     }
375
376   gtk_cell_area_context_push_preferred_height (context, min_size, nat_size);
377 }
378
379 static void
380 gtk_cell_area_box_context_sum_preferred_width_for_height (GtkCellAreaContext *context,
381                                                           gint                height)
382 {
383   GtkCellAreaBoxContext        *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
384   GtkCellAreaBoxContextPrivate *priv        = box_context->priv;
385   GArray                    *group_array;
386   GtkCellArea               *area;
387   GtkOrientation             orientation;
388   gint                       spacing, i;
389   gint                       min_size = 0, nat_size = 0;
390
391   group_array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (height));
392
393   if (group_array)
394     {
395       area        = gtk_cell_area_context_get_area (context);
396       spacing     = gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (area));
397       orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area));
398       
399       for (i = 0; i < group_array->len; i++)
400         {
401           CachedSize *size = &g_array_index (group_array, CachedSize, i);
402           
403           if (orientation == GTK_ORIENTATION_HORIZONTAL)
404             {
405               /* Dont add spacing for 0 size groups, they can be 0 size because
406                * they contain only invisible cells for this round of requests 
407                */
408               if (min_size > 0 && size->nat_size > 0)
409                 {
410                   min_size += spacing;
411                   nat_size += spacing;
412                 }
413               
414               min_size += size->min_size;
415               nat_size += size->nat_size;
416             }
417           else
418             {
419               min_size = MAX (min_size, size->min_size);
420               nat_size = MAX (nat_size, size->nat_size);
421             }
422         }
423
424       gtk_cell_area_context_push_preferred_width_for_height (context, height, min_size, nat_size);
425     }
426 }
427
428 static GtkRequestedSize *
429 gtk_cell_area_box_context_get_requests (GtkCellAreaBoxContext *box_context,
430                                         GtkOrientation         orientation,
431                                         gint                  *n_requests)
432 {
433   GtkCellAreaBoxContextPrivate *priv;
434   GtkRequestedSize             *requests;
435   GArray                       *base_array;
436   BaseSize                     *size;
437   gint                          visible_groups = 0;
438   gint                          i, j;
439
440   g_return_val_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context), NULL);
441
442   priv = box_context->priv;
443
444   if (orientation == GTK_ORIENTATION_HORIZONTAL)
445     base_array = priv->base_widths;
446   else
447     base_array = priv->base_heights;
448
449   for (i = 0; i < base_array->len; i++)
450     {
451       size = &g_array_index (base_array, BaseSize, i);
452
453       if (size->nat_size > 0)
454         visible_groups++;
455     }
456
457   requests = g_new (GtkRequestedSize, visible_groups);
458
459   for (j = 0, i = 0; i < base_array->len; i++)
460     {
461       size = &g_array_index (base_array, BaseSize, i);
462
463       if (size->nat_size > 0)
464         {
465           requests[j].data         = GINT_TO_POINTER (i);
466           requests[j].minimum_size = size->min_size;
467           requests[j].natural_size = size->nat_size;
468           j++;
469         }
470     }
471
472   if (n_requests)
473     *n_requests = visible_groups;
474
475   return requests;
476 }
477
478 static GtkCellAreaBoxAllocation *
479 allocate_for_orientation (GtkCellAreaBoxContext *context,
480                           GtkOrientation         orientation,
481                           gint                   spacing,
482                           gint                   size,
483                           gint                  *n_allocs)
484 {
485   GtkCellAreaBoxContextPrivate *priv = context->priv;
486   GtkRequestedSize             *orientation_sizes;
487   GtkCellAreaBoxAllocation     *allocs;
488   gint                          n_expand_groups = 0;
489   gint                          i, n_groups, position;
490   gint                          extra_size, extra_extra;
491   gint                          avail_size = size;
492
493   orientation_sizes =
494     gtk_cell_area_box_context_get_requests (context, orientation, &n_groups);
495
496   /* Count groups that expand */
497   for (i = 0; i < n_groups; i++)
498     {
499       BaseSize *size;
500       gint      group_idx = GPOINTER_TO_INT (orientation_sizes[i].data);
501
502       if (orientation == GTK_ORIENTATION_HORIZONTAL)
503         size = &g_array_index (priv->base_widths, BaseSize, group_idx);
504       else
505         size = &g_array_index (priv->base_heights, BaseSize, group_idx);
506
507       if (size->expand)
508         n_expand_groups++;
509     }
510
511   /* First start by naturally allocating space among groups */
512   avail_size -= (n_groups - 1) * spacing;
513   for (i = 0; i < n_groups; i++)
514     avail_size -= orientation_sizes[i].minimum_size;
515
516   avail_size = gtk_distribute_natural_allocation (avail_size, n_groups, orientation_sizes);
517
518   /* Calculate/distribute expand for groups */
519   if (n_expand_groups > 0)
520     {
521       extra_size  = avail_size / n_expand_groups;
522       extra_extra = avail_size % n_expand_groups;
523     }
524   else
525     extra_size = extra_extra = 0;
526
527   allocs = g_new (GtkCellAreaBoxAllocation, n_groups);
528
529   for (position = 0, i = 0; i < n_groups; i++)
530     {
531       BaseSize *base_size = &g_array_index (priv->base_widths, BaseSize, i);
532
533       allocs[i].group_idx = GPOINTER_TO_INT (orientation_sizes[i].data);
534       allocs[i].position  = position;
535       allocs[i].size      = orientation_sizes[i].minimum_size;
536
537       if (base_size->expand)
538         {
539           allocs[i].size += extra_size;
540           if (extra_extra)
541             {
542               allocs[i].size++;
543               extra_extra--;
544             }
545         }
546
547       position += allocs[i].size;
548       position += spacing;
549     }
550
551   if (n_allocs)
552     *n_allocs = n_groups;
553
554   g_free (orientation_sizes);
555
556   return allocs;
557 }
558
559 static void
560 gtk_cell_area_box_context_allocate_width (GtkCellAreaContext *context,
561                                           gint                width)
562 {
563   GtkCellAreaBoxContext        *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
564   GtkCellAreaBoxContextPrivate *priv        = box_context->priv;
565   GtkCellArea               *area;
566   GtkOrientation             orientation;
567
568   area        = gtk_cell_area_context_get_area (context);
569   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area));
570
571   if (orientation == GTK_ORIENTATION_HORIZONTAL)
572     {
573       gint spacing = gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (area));
574
575       g_free (priv->orientation_allocs);
576       priv->orientation_allocs = allocate_for_orientation (box_context, orientation, spacing, width,
577                                                            &priv->n_orientation_allocs);
578     }
579
580   GTK_CELL_AREA_CONTEXT_CLASS (gtk_cell_area_box_context_parent_class)->allocate_width (context, width);
581 }
582
583 static void
584 gtk_cell_area_box_context_allocate_height (GtkCellAreaContext *context,
585                                            gint                height)
586 {
587   GtkCellAreaBoxContext        *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
588   GtkCellAreaBoxContextPrivate *priv     = box_context->priv;
589   GtkCellArea                  *area;
590   GtkOrientation                orientation;
591
592   area        = gtk_cell_area_context_get_area (context);
593   orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area));
594
595   if (orientation == GTK_ORIENTATION_VERTICAL)
596     {
597       gint spacing = gtk_cell_area_box_get_spacing (GTK_CELL_AREA_BOX (area));
598
599       g_free (priv->orientation_allocs);
600       priv->orientation_allocs = allocate_for_orientation (box_context, orientation, spacing, height,
601                                                            &priv->n_orientation_allocs);
602     }
603
604   GTK_CELL_AREA_CONTEXT_CLASS (gtk_cell_area_box_context_parent_class)->allocate_height (context, height);
605 }
606
607 /*************************************************************
608  *                            API                            *
609  *************************************************************/
610 void
611 gtk_cell_area_box_init_groups (GtkCellAreaBoxContext *box_context,
612                                guint                  n_groups,
613                                gboolean              *expand_groups)
614 {
615   GtkCellAreaBoxContextPrivate *priv;
616   gint                          i;
617
618   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
619   g_return_if_fail (n_groups == 0 || expand_groups != NULL);
620
621   /* When the group dimensions change, all info must be flushed 
622    * Note this already clears the min/nat values on the BaseSizes
623    */
624   gtk_cell_area_context_flush (GTK_CELL_AREA_CONTEXT (box_context));
625
626   priv = box_context->priv;
627   g_array_set_size (priv->base_widths,  n_groups);
628   g_array_set_size (priv->base_heights, n_groups);
629
630   /* Now set the expand info */
631   for (i = 0; i < n_groups; i++)
632     {
633       BaseSize *base_width  = &g_array_index (priv->base_widths,  BaseSize, i);
634       BaseSize *base_height = &g_array_index (priv->base_heights, BaseSize, i);
635
636       base_width->expand  = expand_groups[i];
637       base_height->expand = expand_groups[i];
638     }
639 }
640
641 void
642 gtk_cell_area_box_context_push_group_width (GtkCellAreaBoxContext *box_context,
643                                             gint                   group_idx,
644                                             gint                   minimum_width,
645                                             gint                   natural_width)
646 {
647   GtkCellAreaBoxContextPrivate *priv;
648   BaseSize                     *size;
649
650   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
651
652   priv = box_context->priv;
653   g_return_if_fail (group_idx < priv->base_widths->len);
654
655   size = &g_array_index (priv->base_widths, BaseSize, group_idx);
656   size->min_size = MAX (size->min_size, minimum_width);
657   size->nat_size = MAX (size->nat_size, natural_width);
658 }
659
660 void
661 gtk_cell_area_box_context_push_group_height_for_width  (GtkCellAreaBoxContext *box_context,
662                                                         gint                   group_idx,
663                                                         gint                   for_width,
664                                                         gint                   minimum_height,
665                                                         gint                   natural_height)
666 {
667   GtkCellAreaBoxContextPrivate *priv;
668   GArray                       *group_array;
669   CachedSize                   *size;
670
671   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
672
673   priv = box_context->priv;
674   g_return_if_fail (group_idx < priv->base_widths->len);
675
676   group_array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width));
677   if (!group_array)
678     {
679       group_array = g_array_new (FALSE, TRUE, sizeof (CachedSize));
680       g_array_set_size (group_array, priv->base_heights->len);
681
682       g_hash_table_insert (priv->heights, GINT_TO_POINTER (for_width), group_array);
683     }
684
685   size = &g_array_index (group_array, CachedSize, group_idx);
686   size->min_size = MAX (size->min_size, minimum_height);
687   size->nat_size = MAX (size->nat_size, natural_height);
688 }
689
690 void
691 gtk_cell_area_box_context_push_group_height (GtkCellAreaBoxContext *box_context,
692                                              gint                   group_idx,
693                                              gint                   minimum_height,
694                                              gint                   natural_height)
695 {
696   GtkCellAreaBoxContextPrivate *priv;
697   BaseSize                     *size;
698
699   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
700
701   priv = box_context->priv;
702   g_return_if_fail (group_idx < priv->base_heights->len);
703
704   size = &g_array_index (priv->base_heights, BaseSize, group_idx);
705   size->min_size = MAX (size->min_size, minimum_height);
706   size->nat_size = MAX (size->nat_size, natural_height);
707 }
708
709 void
710 gtk_cell_area_box_context_push_group_width_for_height (GtkCellAreaBoxContext *box_context,
711                                                        gint                   group_idx,
712                                                        gint                   for_height,
713                                                        gint                   minimum_width,
714                                                        gint                   natural_width)
715 {
716   GtkCellAreaBoxContextPrivate *priv;
717   GArray                    *group_array;
718   CachedSize                *size;
719
720   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
721
722   priv        = box_context->priv;
723   g_return_if_fail (group_idx < priv->base_widths->len);
724
725   group_array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height));
726   if (!group_array)
727     {
728       group_array = g_array_new (FALSE, TRUE, sizeof (CachedSize));
729       g_array_set_size (group_array, priv->base_heights->len);
730
731       g_hash_table_insert (priv->widths, GINT_TO_POINTER (for_height), group_array);
732     }
733
734   size = &g_array_index (group_array, CachedSize, group_idx);
735   size->min_size = MAX (size->min_size, minimum_width);
736   size->nat_size = MAX (size->nat_size, natural_width);
737 }
738
739 void
740 gtk_cell_area_box_context_get_group_width (GtkCellAreaBoxContext *box_context,
741                                            gint                   group_idx,
742                                            gint                  *minimum_width,
743                                            gint                  *natural_width)
744 {
745   GtkCellAreaBoxContextPrivate *priv;
746   BaseSize                     *size;
747
748   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
749
750   priv = box_context->priv;
751   g_return_if_fail (group_idx < priv->base_widths->len);
752
753   size = &g_array_index (priv->base_widths, BaseSize, group_idx);
754
755   if (minimum_width)
756     *minimum_width = size->min_size;
757   
758   if (natural_width)
759     *natural_width = size->nat_size;
760 }
761
762 void
763 gtk_cell_area_box_context_get_group_height_for_width (GtkCellAreaBoxContext *box_context,
764                                                       gint                   group_idx,
765                                                       gint                   for_width,
766                                                       gint                  *minimum_height,
767                                                       gint                  *natural_height)
768 {
769   GtkCellAreaBoxContextPrivate *priv;
770   GArray                    *group_array;
771
772   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
773
774   priv        = box_context->priv;
775   g_return_if_fail (group_idx < priv->base_widths->len);
776
777   group_array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width));
778
779   if (group_array)
780     {
781       CachedSize *size = &g_array_index (group_array, CachedSize, group_idx);
782
783       if (minimum_height)
784         *minimum_height = size->min_size;
785
786       if (natural_height)
787         *natural_height = size->nat_size;
788     }
789   else
790     {
791       if (minimum_height)
792         *minimum_height = -1;
793
794       if (natural_height)
795         *natural_height = -1;      
796     }
797 }
798
799 void
800 gtk_cell_area_box_context_get_group_height (GtkCellAreaBoxContext *box_context,
801                                             gint                   group_idx,
802                                             gint                  *minimum_height,
803                                             gint                  *natural_height)
804 {
805   GtkCellAreaBoxContextPrivate *priv;
806   BaseSize                     *size;
807
808   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
809
810   priv = box_context->priv;
811   g_return_if_fail (group_idx < priv->base_heights->len);
812
813   size = &g_array_index (priv->base_heights, BaseSize, group_idx);
814
815   if (minimum_height)
816     *minimum_height = size->min_size;
817   
818   if (natural_height)
819     *natural_height = size->nat_size;
820 }
821
822 void
823 gtk_cell_area_box_context_get_group_width_for_height (GtkCellAreaBoxContext *box_context,
824                                                       gint                   group_idx,
825                                                       gint                   for_height,
826                                                       gint                  *minimum_width,
827                                                       gint                  *natural_width)
828 {
829   GtkCellAreaBoxContextPrivate *priv;
830   GArray                       *group_array;
831
832   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
833
834   priv = box_context->priv;
835   g_return_if_fail (group_idx < priv->base_widths->len);
836
837   group_array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height));
838
839   if (group_array)
840     {
841       CachedSize *size = &g_array_index (group_array, CachedSize, group_idx);
842
843       if (minimum_width)
844         *minimum_width = size->min_size;
845
846       if (natural_width)
847         *natural_width = size->nat_size;
848     }
849   else
850     {
851       if (minimum_width)
852         *minimum_width = -1;
853
854       if (natural_width)
855         *natural_width = -1;      
856     }
857 }
858
859 GtkRequestedSize *
860 gtk_cell_area_box_context_get_widths (GtkCellAreaBoxContext *box_context,
861                                       gint                  *n_widths)
862 {
863   return gtk_cell_area_box_context_get_requests (box_context, GTK_ORIENTATION_HORIZONTAL, n_widths);
864 }
865
866 GtkRequestedSize *
867 gtk_cell_area_box_context_get_heights (GtkCellAreaBoxContext *box_context,
868                                        gint                  *n_heights)
869 {
870   return gtk_cell_area_box_context_get_requests (box_context, GTK_ORIENTATION_VERTICAL, n_heights);
871 }
872
873 G_CONST_RETURN GtkCellAreaBoxAllocation *
874 gtk_cell_area_box_context_get_orientation_allocs (GtkCellAreaBoxContext *context,
875                                                   gint                  *n_allocs)
876 {
877   GtkCellAreaBoxContextPrivate *priv;
878
879   g_return_val_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context), NULL);
880   
881   priv = context->priv;
882
883   *n_allocs = priv->n_orientation_allocs;
884
885   return priv->orientation_allocs;
886 }