]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellareabox.c
Fix background area calculation in RTL mode
[~andy/gtk] / gtk / gtkcellareabox.c
1 /* gtkcellareabox.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
25 /**
26  * SECTION:gtkcellareabox
27  * @Short_Description: A cell area that renders #GtkCellRenderers into a row or a column
28  * @Title: GtkCellAreaBox
29  *
30  * The #GtkCellAreaBox renders cell renderers into a row or a column depending on
31  * its #GtkOrientation.
32  *
33  * GtkCellAreaBox uses a notion of <emphasis>packing</emphasis>. Packing
34  * refers to adding cell renderers with reference to a particular position 
35  * in a #GtkCellAreaBox. There are two reference positions: the
36  * <emphasis>start</emphasis> and the <emphasis>end</emphasis> of the box.
37  * When the #GtkCellAreaBox is oriented in the %GTK_ORIENTATION_VERTICAL orientation, 
38  * the start is defined as the top of the box and the end is defined as the bottom.
39  * In the %GTK_ORIENTATION_HORIZONTAL orientation start is defined as the
40  * left side and the end is defined as the right side.
41  *
42  * Alignments of #GtkCellRenderers rendered in adjacent rows can be configured
43  * by configuring the #GtkCellAreaBox:align child cell property with
44  * gtk_cell_area_cell_set_property() or by specifying the "align" argument
45  * to gtk_cell_area_box_pack_start() and gtk_cell_area_box_pack_end().
46  */
47
48 #include "config.h"
49 #include "gtkintl.h"
50 #include "gtkorientable.h"
51 #include "gtkcelllayout.h"
52 #include "gtkcellareabox.h"
53 #include "gtkcellareaboxcontext.h"
54 #include "gtkprivate.h"
55
56
57 /* GObjectClass */
58 static void      gtk_cell_area_box_finalize                       (GObject              *object);
59 static void      gtk_cell_area_box_dispose                        (GObject              *object);
60 static void      gtk_cell_area_box_set_property                   (GObject              *object,
61                                                                    guint                 prop_id,
62                                                                    const GValue         *value,
63                                                                    GParamSpec           *pspec);
64 static void      gtk_cell_area_box_get_property                   (GObject              *object,
65                                                                    guint                 prop_id,
66                                                                    GValue               *value,
67                                                                    GParamSpec           *pspec);
68
69 /* GtkCellAreaClass */
70 static void      gtk_cell_area_box_add                            (GtkCellArea          *area,
71                                                                    GtkCellRenderer      *renderer);
72 static void      gtk_cell_area_box_remove                         (GtkCellArea          *area,
73                                                                    GtkCellRenderer      *renderer);
74 static void      gtk_cell_area_box_foreach                        (GtkCellArea          *area,
75                                                                    GtkCellCallback       callback,
76                                                                    gpointer              callback_data);
77 static void      gtk_cell_area_box_foreach_alloc                  (GtkCellArea          *area,
78                                                                    GtkCellAreaContext   *context,
79                                                                    GtkWidget            *widget,
80                                                                    const GdkRectangle   *cell_area,
81                                                                    const GdkRectangle   *background_area,
82                                                                    GtkCellAllocCallback  callback,
83                                                                    gpointer              callback_data);
84 static void      gtk_cell_area_box_set_cell_property              (GtkCellArea          *area,
85                                                                    GtkCellRenderer      *renderer,
86                                                                    guint                 prop_id,
87                                                                    const GValue         *value,
88                                                                    GParamSpec           *pspec);
89 static void      gtk_cell_area_box_get_cell_property              (GtkCellArea          *area,
90                                                                    GtkCellRenderer      *renderer,
91                                                                    guint                 prop_id,
92                                                                    GValue               *value,
93                                                                    GParamSpec           *pspec);
94 static GtkCellAreaContext *gtk_cell_area_box_create_context       (GtkCellArea          *area);
95 static GtkCellAreaContext *gtk_cell_area_box_copy_context         (GtkCellArea          *area,
96                                                                    GtkCellAreaContext   *context);
97 static GtkSizeRequestMode  gtk_cell_area_box_get_request_mode     (GtkCellArea          *area);
98 static void      gtk_cell_area_box_get_preferred_width            (GtkCellArea          *area,
99                                                                    GtkCellAreaContext   *context,
100                                                                    GtkWidget            *widget,
101                                                                    gint                 *minimum_width,
102                                                                    gint                 *natural_width);
103 static void      gtk_cell_area_box_get_preferred_height           (GtkCellArea          *area,
104                                                                    GtkCellAreaContext   *context,
105                                                                    GtkWidget            *widget,
106                                                                    gint                 *minimum_height,
107                                                                    gint                 *natural_height);
108 static void      gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea          *area,
109                                                                    GtkCellAreaContext   *context,
110                                                                    GtkWidget            *widget,
111                                                                    gint                  width,
112                                                                    gint                 *minimum_height,
113                                                                    gint                 *natural_height);
114 static void      gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea          *area,
115                                                                    GtkCellAreaContext   *context,
116                                                                    GtkWidget            *widget,
117                                                                    gint                  height,
118                                                                    gint                 *minimum_width,
119                                                                    gint                 *natural_width);
120 static gboolean  gtk_cell_area_box_focus                          (GtkCellArea          *area,
121                                                                    GtkDirectionType      direction);
122
123 /* GtkCellLayoutIface */
124 static void      gtk_cell_area_box_cell_layout_init               (GtkCellLayoutIface *iface);
125 static void      gtk_cell_area_box_layout_pack_start              (GtkCellLayout      *cell_layout,
126                                                                    GtkCellRenderer    *renderer,
127                                                                    gboolean            expand);
128 static void      gtk_cell_area_box_layout_pack_end                (GtkCellLayout      *cell_layout,
129                                                                    GtkCellRenderer    *renderer,
130                                                                    gboolean            expand);
131 static void      gtk_cell_area_box_layout_reorder                 (GtkCellLayout      *cell_layout,
132                                                                    GtkCellRenderer    *renderer,
133                                                                    gint                position);
134 static void      gtk_cell_area_box_focus_changed                  (GtkCellArea        *area,
135                                                                    GParamSpec         *pspec,
136                                                                    GtkCellAreaBox     *box);
137
138
139 /* CellInfo/CellGroup metadata handling and convenience functions */
140 typedef struct {
141   GtkCellRenderer *renderer;
142
143   guint            expand : 1; /* Whether the cell expands */
144   guint            pack   : 1; /* Whether the cell is packed from the start or end */
145   guint            align  : 1; /* Whether to align this cell's position with adjacent rows */
146 } CellInfo;
147
148 typedef struct {
149   GList *cells;
150
151   guint  id           : 8;
152   guint  n_cells      : 8;
153   guint  expand_cells : 8;
154 } CellGroup;
155
156 typedef struct {
157   GtkCellRenderer *renderer;
158
159   gint             position;
160   gint             size;
161 } AllocatedCell;
162
163 static CellInfo      *cell_info_new          (GtkCellRenderer       *renderer, 
164                                               GtkPackType            pack,
165                                               gboolean               expand,
166                                               gboolean               align);
167 static void           cell_info_free         (CellInfo              *info);
168 static gint           cell_info_find         (CellInfo              *info,
169                                               GtkCellRenderer       *renderer);
170
171 static AllocatedCell *allocated_cell_new     (GtkCellRenderer       *renderer,
172                                               gint                   position,
173                                               gint                   size);
174 static void           allocated_cell_free    (AllocatedCell         *cell);
175 static GList         *list_consecutive_cells (GtkCellAreaBox        *box);
176 static gint           count_expand_groups    (GtkCellAreaBox        *box);
177 static void           context_weak_notify    (GtkCellAreaBox        *box,
178                                               GtkCellAreaBoxContext *dead_context);
179 static void           reset_contexts         (GtkCellAreaBox        *box);
180 static void           init_context_groups    (GtkCellAreaBox        *box);
181 static void           init_context_group     (GtkCellAreaBox        *box,
182                                               GtkCellAreaBoxContext *context);
183 static GSList        *get_allocated_cells    (GtkCellAreaBox        *box,
184                                               GtkCellAreaBoxContext *context,
185                                               GtkWidget             *widget,
186                                               gint                   width,
187                                               gint                   height);
188
189
190 struct _GtkCellAreaBoxPrivate
191 {
192   GtkOrientation   orientation;
193
194   /* We hold on to the previously focused cell when navigating
195    * up and down in a horizontal box (or left and right on a vertical one)
196    * this way we always re-enter the last focused cell. */
197   GtkCellRenderer *last_focus_cell;
198   gulong           focus_cell_id;
199
200   GList           *cells;
201   GArray          *groups;
202
203   GSList          *contexts;
204
205   gint             spacing;
206
207   /* We hold on to the rtl state from a widget we are requested for
208    * so that we can navigate focus correctly */
209   gboolean         rtl;
210 };
211
212 enum {
213   PROP_0,
214   PROP_ORIENTATION,
215   PROP_SPACING
216 };
217
218 enum {
219   CELL_PROP_0,
220   CELL_PROP_EXPAND,
221   CELL_PROP_ALIGN,
222   CELL_PROP_PACK_TYPE
223 };
224
225 G_DEFINE_TYPE_WITH_CODE (GtkCellAreaBox, gtk_cell_area_box, GTK_TYPE_CELL_AREA,
226                          G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
227                                                 gtk_cell_area_box_cell_layout_init)
228                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL));
229
230 #define OPPOSITE_ORIENTATION(orientation)                       \
231   ((orientation) == GTK_ORIENTATION_HORIZONTAL ?                \
232    GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL)
233
234 static void
235 gtk_cell_area_box_init (GtkCellAreaBox *box)
236 {
237   GtkCellAreaBoxPrivate *priv;
238
239   box->priv = G_TYPE_INSTANCE_GET_PRIVATE (box,
240                                            GTK_TYPE_CELL_AREA_BOX,
241                                            GtkCellAreaBoxPrivate);
242   priv = box->priv;
243
244   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
245   priv->groups      = g_array_new (FALSE, TRUE, sizeof (CellGroup));
246   priv->cells       = NULL;
247   priv->contexts    = NULL;
248   priv->spacing     = 0;
249   priv->rtl         = FALSE;
250
251   /* Watch whenever focus is given to a cell, even if it's not with keynav,
252    * this way we remember upon entry of the area where focus was last time around */
253   priv->focus_cell_id = g_signal_connect (box, "notify::focus-cell", 
254                                           G_CALLBACK (gtk_cell_area_box_focus_changed), box);
255 }
256
257 static void 
258 gtk_cell_area_box_class_init (GtkCellAreaBoxClass *class)
259 {
260   GObjectClass     *object_class = G_OBJECT_CLASS (class);
261   GtkCellAreaClass *area_class   = GTK_CELL_AREA_CLASS (class);
262
263   /* GObjectClass */
264   object_class->finalize     = gtk_cell_area_box_finalize;
265   object_class->dispose      = gtk_cell_area_box_dispose;
266   object_class->set_property = gtk_cell_area_box_set_property;
267   object_class->get_property = gtk_cell_area_box_get_property;
268
269   /* GtkCellAreaClass */
270   area_class->add                 = gtk_cell_area_box_add;
271   area_class->remove              = gtk_cell_area_box_remove;
272   area_class->foreach             = gtk_cell_area_box_foreach;
273   area_class->foreach_alloc       = gtk_cell_area_box_foreach_alloc;
274   area_class->set_cell_property   = gtk_cell_area_box_set_cell_property;
275   area_class->get_cell_property   = gtk_cell_area_box_get_cell_property;
276   
277   area_class->create_context                 = gtk_cell_area_box_create_context;
278   area_class->copy_context                   = gtk_cell_area_box_copy_context;
279   area_class->get_request_mode               = gtk_cell_area_box_get_request_mode;
280   area_class->get_preferred_width            = gtk_cell_area_box_get_preferred_width;
281   area_class->get_preferred_height           = gtk_cell_area_box_get_preferred_height;
282   area_class->get_preferred_height_for_width = gtk_cell_area_box_get_preferred_height_for_width;
283   area_class->get_preferred_width_for_height = gtk_cell_area_box_get_preferred_width_for_height;
284
285   area_class->focus = gtk_cell_area_box_focus;
286
287   /* Properties */
288   g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
289
290   /**
291    * GtkCellAreaBox:spacing:
292    *
293    * The amount of space to reserve between cells.
294    *
295    * Since: 3.0
296    */
297   g_object_class_install_property (object_class,
298                                    PROP_SPACING,
299                                    g_param_spec_int ("spacing",
300                                                      P_("Spacing"),
301                                                      P_("Space which is inserted between cells"),
302                                                      0,
303                                                      G_MAXINT,
304                                                      0,
305                                                      GTK_PARAM_READWRITE));
306
307   /* Cell Properties */
308   /**
309    * GtkCellAreaBox:expand:
310    *
311    * Whether the cell renderer should receive extra space when the area receives
312    * more than its natural size.
313    *
314    * Since: 3.0
315    */
316   gtk_cell_area_class_install_cell_property (area_class,
317                                              CELL_PROP_EXPAND,
318                                              g_param_spec_boolean 
319                                              ("expand",
320                                               P_("Expand"),
321                                               P_("Whether the cell expands"),
322                                               FALSE,
323                                               GTK_PARAM_READWRITE));
324   
325   /**
326    * GtkCellAreaBox:align:
327    *
328    * Whether the cell renderer should be aligned in adjacent rows.
329    *
330    * Since: 3.0
331    */
332   gtk_cell_area_class_install_cell_property (area_class,
333                                              CELL_PROP_ALIGN,
334                                              g_param_spec_boolean
335                                              ("align",
336                                               P_("Align"),
337                                               P_("Whether cell should align with adjacent rows"),
338                                               TRUE,
339                                               GTK_PARAM_READWRITE));
340
341   /**
342    * GtkCellAreaBox:pack-type:
343    *
344    * A GtkPackType indicating whether the cell renderer is packed with reference to the 
345    * start or end of the area.
346    *
347    * Since: 3.0
348    */
349   gtk_cell_area_class_install_cell_property (area_class,
350                                              CELL_PROP_PACK_TYPE,
351                                              g_param_spec_enum
352                                              ("pack-type",
353                                               P_("Pack Type"),
354                                               P_("A GtkPackType indicating whether the cell is packed with "
355                                                  "reference to the start or end of the cell area"),
356                                               GTK_TYPE_PACK_TYPE, GTK_PACK_START,
357                                               GTK_PARAM_READWRITE));
358
359   g_type_class_add_private (object_class, sizeof (GtkCellAreaBoxPrivate));
360 }
361
362
363 /*************************************************************
364  *    CellInfo/CellGroup basics and convenience functions    *
365  *************************************************************/
366 static CellInfo *
367 cell_info_new  (GtkCellRenderer *renderer, 
368                 GtkPackType      pack,
369                 gboolean         expand,
370                 gboolean         align)
371 {
372   CellInfo *info = g_slice_new (CellInfo);
373   
374   info->renderer = g_object_ref_sink (renderer);
375   info->pack     = pack;
376   info->expand   = expand;
377   info->align    = align;
378
379   return info;
380 }
381
382 static void
383 cell_info_free (CellInfo *info)
384 {
385   g_object_unref (info->renderer);
386
387   g_slice_free (CellInfo, info);
388 }
389
390 static gint
391 cell_info_find (CellInfo        *info,
392                 GtkCellRenderer *renderer)
393 {
394   return (info->renderer == renderer) ? 0 : -1;
395 }
396
397 static AllocatedCell *
398 allocated_cell_new (GtkCellRenderer *renderer,
399                     gint             position,
400                     gint             size)
401 {
402   AllocatedCell *cell = g_slice_new (AllocatedCell);
403
404   cell->renderer = renderer;
405   cell->position = position;
406   cell->size     = size;
407
408   return cell;
409 }
410
411 static void
412 allocated_cell_free (AllocatedCell *cell)
413 {
414   g_slice_free (AllocatedCell, cell);
415 }
416
417 static GList *
418 list_consecutive_cells (GtkCellAreaBox *box)
419 {
420   GtkCellAreaBoxPrivate *priv = box->priv;
421   GList                 *l, *consecutive_cells = NULL, *pack_end_cells = NULL;
422   CellInfo              *info;
423
424   /* List cells in consecutive order taking their 
425    * PACK_START/PACK_END options into account 
426    */
427   for (l = priv->cells; l; l = l->next)
428     {
429       info = l->data;
430       
431       if (info->pack == GTK_PACK_START)
432         consecutive_cells = g_list_prepend (consecutive_cells, info);
433     }
434
435   for (l = priv->cells; l; l = l->next)
436     {
437       info = l->data;
438       
439       if (info->pack == GTK_PACK_END)
440         pack_end_cells = g_list_prepend (pack_end_cells, info);
441     }
442
443   consecutive_cells = g_list_reverse (consecutive_cells);
444   consecutive_cells = g_list_concat (consecutive_cells, pack_end_cells);
445
446   return consecutive_cells;
447 }
448
449 static void
450 cell_groups_clear (GtkCellAreaBox     *box)
451 {
452   GtkCellAreaBoxPrivate *priv  = box->priv;
453   gint                   i;
454
455   for (i = 0; i < priv->groups->len; i++)
456     {
457       CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
458
459       g_list_free (group->cells);
460     }
461
462   g_array_set_size (priv->groups, 0);
463 }
464
465 static void
466 cell_groups_rebuild (GtkCellAreaBox *box)
467 {
468   GtkCellAreaBoxPrivate *priv  = box->priv;
469   CellGroup              group = { 0, };
470   CellGroup             *group_ptr;
471   GList                 *cells, *l;
472   guint                  id = 0;
473
474   cell_groups_clear (box);
475
476   if (!priv->cells)
477     return;
478
479   cells = list_consecutive_cells (box);
480
481   /* First group is implied */
482   g_array_append_val (priv->groups, group);
483   group_ptr = &g_array_index (priv->groups, CellGroup, id);
484
485   for (l = cells; l; l = l->next)
486     {
487       CellInfo *info = l->data;
488
489       /* A new group starts with any aligned cell, the first group is implied */
490       if (info->align && l != cells)
491         {
492           memset (&group, 0x0, sizeof (CellGroup));
493           group.id = ++id;
494
495           g_array_append_val (priv->groups, group);
496           group_ptr = &g_array_index (priv->groups, CellGroup, id);
497         }
498
499       group_ptr->cells = g_list_prepend (group_ptr->cells, info);
500       group_ptr->n_cells++;
501
502       /* A group expands if it contains any expand cells */
503       if (info->expand)
504         group_ptr->expand_cells++;
505     }
506
507   g_list_free (cells);
508
509   for (id = 0; id < priv->groups->len; id++)
510     {
511       group_ptr = &g_array_index (priv->groups, CellGroup, id);
512
513       group_ptr->cells = g_list_reverse (group_ptr->cells);
514     }
515
516   /* Contexts need to be updated with the new grouping information */
517   init_context_groups (box);
518 }
519
520 static gint
521 count_visible_cells (CellGroup *group, 
522                      gint      *expand_cells)
523 {
524   GList *l;
525   gint   visible_cells = 0;
526   gint   n_expand_cells = 0;
527
528   for (l = group->cells; l; l = l->next)
529     {
530       CellInfo *info = l->data;
531
532       if (gtk_cell_renderer_get_visible (info->renderer))
533         {
534           visible_cells++;
535
536           if (info->expand)
537             n_expand_cells++;
538         }
539     }
540
541   if (expand_cells)
542     *expand_cells = n_expand_cells;
543
544   return visible_cells;
545 }
546
547 static gint
548 count_expand_groups (GtkCellAreaBox  *box)
549 {
550   GtkCellAreaBoxPrivate *priv = box->priv;
551   gint                   i;
552   gint                   expand_groups = 0;
553
554   for (i = 0; i < priv->groups->len; i++)
555     {
556       CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
557
558       if (group->expand_cells > 0)
559         expand_groups++;
560     }
561
562   return expand_groups;
563 }
564
565 static void 
566 context_weak_notify (GtkCellAreaBox        *box,
567                      GtkCellAreaBoxContext *dead_context)
568 {
569   GtkCellAreaBoxPrivate *priv = box->priv;
570
571   priv->contexts = g_slist_remove (priv->contexts, dead_context);
572 }
573
574 static void
575 init_context_group (GtkCellAreaBox        *box,
576                     GtkCellAreaBoxContext *context)
577 {
578   GtkCellAreaBoxPrivate *priv = box->priv;
579   gint                  *expand_groups, i;
580
581   expand_groups = g_new (gboolean, priv->groups->len);
582
583   for (i = 0; i < priv->groups->len; i++)
584     {
585       CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
586
587       expand_groups[i] = (group->expand_cells > 0);
588     }
589
590   /* This call implies reseting the request info */
591   gtk_cell_area_box_init_groups (context, priv->groups->len, expand_groups);
592   g_free (expand_groups);
593 }
594
595 static void
596 init_context_groups (GtkCellAreaBox *box)
597 {
598   GtkCellAreaBoxPrivate *priv = box->priv;
599   GSList                *l;
600
601   /* When the box's groups are reconstructed, contexts need to
602    * be reinitialized.
603    */
604   for (l = priv->contexts; l; l = l->next)
605     {
606       GtkCellAreaBoxContext *context = l->data;
607
608       init_context_group (box, context);
609     }
610 }
611
612 static void
613 reset_contexts (GtkCellAreaBox *box)
614 {
615   GtkCellAreaBoxPrivate *priv = box->priv;
616   GSList                *l;
617
618   /* When the box layout changes, contexts need to
619    * be reset and sizes for the box get requested again
620    */
621   for (l = priv->contexts; l; l = l->next)
622     {
623       GtkCellAreaContext *context = l->data;
624
625       gtk_cell_area_context_reset (context);
626     }
627 }
628
629 /* Fall back on a completely unaligned dynamic allocation of cells
630  * when not allocated for the said orientation, alignment of cells
631  * is not done when each area gets a different size in the orientation
632  * of the box.
633  */
634 static GSList *
635 allocate_cells_manually (GtkCellAreaBox        *box,
636                          GtkWidget             *widget,
637                          gint                   width,
638                          gint                   height)
639 {
640   GtkCellAreaBoxPrivate    *priv = box->priv;
641   GList                    *cells, *l;
642   GSList                   *allocated_cells = NULL;
643   GtkRequestedSize         *sizes;
644   gint                      i;
645   gint                      nvisible = 0, nexpand = 0, group_expand;
646   gint                      avail_size, extra_size, extra_extra, full_size;
647   gint                      position = 0, for_size;
648   gboolean                  rtl;
649
650   if (!priv->cells)
651     return NULL;
652
653   /* For vertical oriented boxes, we just let the cell renderers realign themselves for rtl */
654   rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
655          gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
656
657   cells = list_consecutive_cells (box);
658
659   /* Count the visible and expand cells */
660   for (i = 0; i < priv->groups->len; i++)
661     {
662       CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
663
664       nvisible += count_visible_cells (group, &group_expand);
665       nexpand  += group_expand;
666     }
667
668   if (nvisible <= 0)
669     {
670       g_list_free (cells);
671       return NULL;
672     }
673
674   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
675     {
676       full_size = avail_size = width;
677       for_size  = height;
678     }
679   else
680     {
681       full_size = avail_size = height;
682       for_size  = width;
683     }
684
685   /* Go ahead and collect the requests on the fly */
686   sizes = g_new0 (GtkRequestedSize, nvisible);
687   for (l = cells, i = 0; l; l = l->next)
688     {
689       CellInfo *info = l->data;
690
691       if (!gtk_cell_renderer_get_visible (info->renderer))
692         continue;
693
694       gtk_cell_area_request_renderer (GTK_CELL_AREA (box), info->renderer,
695                                       priv->orientation,
696                                       widget, for_size,
697                                       &sizes[i].minimum_size,
698                                       &sizes[i].natural_size);
699
700       avail_size -= sizes[i].minimum_size;
701
702       sizes[i].data = info;
703
704       i++;
705     }
706
707   /* Naturally distribute the allocation */
708   avail_size -= (nvisible - 1) * priv->spacing;
709   if (avail_size > 0)
710     avail_size = gtk_distribute_natural_allocation (avail_size, nvisible, sizes);
711   else
712     avail_size = 0;
713
714   /* Calculate/distribute expand for cells */
715   if (nexpand > 0)
716     {
717       extra_size  = avail_size / nexpand;
718       extra_extra = avail_size % nexpand;
719     }
720   else
721     extra_size = extra_extra = 0;
722
723   /* Create the allocated cells */
724   for (i = 0; i < nvisible; i++)
725     {
726       CellInfo      *info = sizes[i].data;
727       AllocatedCell *cell;
728
729       if (info->expand)
730         {
731           sizes[i].minimum_size += extra_size;
732           if (extra_extra)
733             {
734               sizes[i].minimum_size++;
735               extra_extra--;
736             }
737         }
738       
739       if (rtl)
740         cell = allocated_cell_new (info->renderer, 
741                                    full_size - (position + sizes[i].minimum_size),
742                                    sizes[i].minimum_size);
743       else
744         cell = allocated_cell_new (info->renderer, position, sizes[i].minimum_size);
745
746       allocated_cells = g_slist_prepend (allocated_cells, cell);
747               
748       position += sizes[i].minimum_size;
749       position += priv->spacing;
750     }
751
752   g_free (sizes);
753   g_list_free (cells);
754
755   /* Note it might not be important to reverse the list here at all,
756    * we have the correct positions, no need to allocate from left to right */
757   return g_slist_reverse (allocated_cells);
758 }
759
760 /* Returns an allocation for each cell in the orientation of the box,
761  * used in ->render()/->event() implementations to get a straight-forward
762  * list of allocated cells to operate on.
763  */
764 static GSList *
765 get_allocated_cells (GtkCellAreaBox        *box,
766                      GtkCellAreaBoxContext *context,
767                      GtkWidget             *widget,
768                      gint                   width,
769                      gint                   height)
770 {
771   GtkCellAreaBoxAllocation *group_allocs;
772   GtkCellArea              *area = GTK_CELL_AREA (box);
773   GtkCellAreaBoxPrivate    *priv = box->priv;
774   GList                    *cell_list;
775   GSList                   *allocated_cells = NULL;
776   gint                      i, j, n_allocs;
777   gint                      for_size, full_size;
778   gboolean                  rtl;
779
780   group_allocs = gtk_cell_area_box_context_get_orientation_allocs (context, &n_allocs);
781   if (!group_allocs)
782     return allocate_cells_manually (box, widget, width, height);
783
784   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
785     {
786       full_size = width;
787       for_size  = height;
788     }
789   else
790     {
791       full_size = height;
792       for_size  = width;
793     }
794
795   /* For vertical oriented boxes, we just let the cell renderers realign themselves for rtl */
796   rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
797          gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
798
799   for (i = 0; i < n_allocs; i++)
800     {
801       /* We dont always allocate all groups, sometimes the requested group has only invisible
802        * cells for every row, hence the usage of group_allocs[i].group_idx here
803        */
804       CellGroup *group = &g_array_index (priv->groups, CellGroup, group_allocs[i].group_idx);
805
806       /* Exception for single cell groups */
807       if (group->n_cells == 1)
808         {
809           CellInfo      *info = group->cells->data;
810           AllocatedCell *cell;
811
812           if (rtl)
813             cell = allocated_cell_new (info->renderer, 
814                                        full_size - (group_allocs[i].position + group_allocs[i].size),
815                                        group_allocs[i].size);
816           else
817             cell = allocated_cell_new (info->renderer, group_allocs[i].position, group_allocs[i].size);
818
819           allocated_cells = g_slist_prepend (allocated_cells, cell);
820         }
821       else
822         {
823           GtkRequestedSize *sizes;
824           gint              avail_size, position;
825           gint              visible_cells, expand_cells;
826           gint              extra_size, extra_extra;
827
828           visible_cells = count_visible_cells (group, &expand_cells);
829
830           /* If this row has no visible cells in this group, just
831            * skip the allocation */
832           if (visible_cells == 0)
833             continue;
834
835           /* Offset the allocation to the group position and allocate into 
836            * the group's available size */
837           position   = group_allocs[i].position;
838           avail_size = group_allocs[i].size;
839
840           sizes = g_new (GtkRequestedSize, visible_cells);
841
842           for (j = 0, cell_list = group->cells; cell_list; cell_list = cell_list->next)
843             {
844               CellInfo *info = cell_list->data;
845
846               if (!gtk_cell_renderer_get_visible (info->renderer))
847                 continue;
848
849               gtk_cell_area_request_renderer (area, info->renderer,
850                                               priv->orientation,
851                                               widget, for_size,
852                                               &sizes[j].minimum_size,
853                                               &sizes[j].natural_size);
854
855               sizes[j].data = info;
856               avail_size   -= sizes[j].minimum_size;
857
858               j++;
859             }
860
861           /* Distribute cells naturally within the group */
862           avail_size -= (visible_cells - 1) * priv->spacing;
863           if (avail_size > 0)
864             avail_size = gtk_distribute_natural_allocation (avail_size, visible_cells, sizes);
865           else
866             avail_size = 0;
867
868           /* Calculate/distribute expand for cells */
869           if (expand_cells > 0)
870             {
871               extra_size  = avail_size / expand_cells;
872               extra_extra = avail_size % expand_cells;
873             }
874           else
875             extra_size = extra_extra = 0;
876
877           /* Create the allocated cells (loop only over visible cells here) */
878           for (j = 0; j < visible_cells; j++)
879             {
880               CellInfo      *info = sizes[j].data;
881               AllocatedCell *cell;
882
883               if (info->expand)
884                 {
885                   sizes[j].minimum_size += extra_size;
886                   if (extra_extra)
887                     {
888                       sizes[j].minimum_size++;
889                       extra_extra--;
890                     }
891                 }
892               
893               if (rtl)
894                 cell = allocated_cell_new (info->renderer, 
895                                            full_size - (position + sizes[j].minimum_size),
896                                            sizes[j].minimum_size);
897               else
898                 cell = allocated_cell_new (info->renderer, position, sizes[j].minimum_size);
899
900               allocated_cells = g_slist_prepend (allocated_cells, cell);
901               
902               position += sizes[j].minimum_size;
903               position += priv->spacing;
904             }
905
906           g_free (sizes);
907         }
908     }
909
910   /* Note it might not be important to reverse the list here at all,
911    * we have the correct positions, no need to allocate from left to right */
912   return g_slist_reverse (allocated_cells);
913 }
914
915
916 static void
917 gtk_cell_area_box_focus_changed (GtkCellArea        *area,
918                                  GParamSpec         *pspec,
919                                  GtkCellAreaBox     *box)
920 {
921   if (gtk_cell_area_get_focus_cell (area))
922     box->priv->last_focus_cell = gtk_cell_area_get_focus_cell (area);
923 }
924
925 /*************************************************************
926  *                      GObjectClass                         *
927  *************************************************************/
928 static void
929 gtk_cell_area_box_finalize (GObject *object)
930 {
931   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (object);
932   GtkCellAreaBoxPrivate *priv = box->priv;
933   GSList                *l;
934
935   /* Unref/free the context list */
936   for (l = priv->contexts; l; l = l->next)
937     g_object_weak_unref (G_OBJECT (l->data), (GWeakNotify)context_weak_notify, box);
938
939   g_slist_free (priv->contexts);
940   priv->contexts = NULL;
941
942   /* Free the cell grouping info */
943   cell_groups_clear (box);
944   g_array_free (priv->groups, TRUE);
945   
946   G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->finalize (object);
947 }
948
949 static void
950 gtk_cell_area_box_dispose (GObject *object)
951 {
952   G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->dispose (object);
953 }
954
955 static void
956 gtk_cell_area_box_set_property (GObject       *object,
957                                 guint          prop_id,
958                                 const GValue  *value,
959                                 GParamSpec    *pspec)
960 {
961   GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
962
963   switch (prop_id)
964     {
965     case PROP_ORIENTATION:
966       box->priv->orientation = g_value_get_enum (value);
967
968       /* Notify that size needs to be requested again */
969       reset_contexts (box);
970       break;
971     case PROP_SPACING:
972       gtk_cell_area_box_set_spacing (box, g_value_get_int (value));
973       break;
974     default:
975       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
976       break;
977     }
978 }
979
980 static void
981 gtk_cell_area_box_get_property (GObject     *object,
982                                 guint        prop_id,
983                                 GValue      *value,
984                                 GParamSpec  *pspec)
985 {
986   GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
987
988   switch (prop_id)
989     {
990     case PROP_ORIENTATION:
991       g_value_set_enum (value, box->priv->orientation);
992       break;
993     case PROP_SPACING:
994       g_value_set_int (value, gtk_cell_area_box_get_spacing (box));
995       break;
996     default:
997       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
998       break;
999     }
1000 }
1001
1002 /*************************************************************
1003  *                    GtkCellAreaClass                       *
1004  *************************************************************/
1005 static void      
1006 gtk_cell_area_box_add (GtkCellArea        *area,
1007                        GtkCellRenderer    *renderer)
1008 {
1009   gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area),
1010                                 renderer, FALSE, TRUE);
1011 }
1012
1013 static void
1014 gtk_cell_area_box_remove (GtkCellArea        *area,
1015                           GtkCellRenderer    *renderer)
1016 {
1017   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
1018   GtkCellAreaBoxPrivate *priv = box->priv;
1019   GList                 *node;
1020
1021   if (priv->last_focus_cell == renderer)
1022     priv->last_focus_cell = NULL;
1023
1024   node = g_list_find_custom (priv->cells, renderer, 
1025                              (GCompareFunc)cell_info_find);
1026
1027   if (node)
1028     {
1029       CellInfo *info = node->data;
1030
1031       cell_info_free (info);
1032
1033       priv->cells = g_list_delete_link (priv->cells, node);
1034
1035       /* Reconstruct cell groups */
1036       cell_groups_rebuild (box);
1037     }
1038   else
1039     g_warning ("Trying to remove a cell renderer that is not present GtkCellAreaBox");
1040 }
1041
1042 static void
1043 gtk_cell_area_box_foreach (GtkCellArea        *area,
1044                            GtkCellCallback     callback,
1045                            gpointer            callback_data)
1046 {
1047   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
1048   GtkCellAreaBoxPrivate *priv = box->priv;
1049   GList                 *list;
1050
1051   for (list = priv->cells; list; list = list->next)
1052     {
1053       CellInfo *info = list->data;
1054
1055       if (callback (info->renderer, callback_data))
1056         break;
1057     }
1058 }
1059
1060 static void
1061 gtk_cell_area_box_foreach_alloc (GtkCellArea          *area,
1062                                  GtkCellAreaContext   *context,
1063                                  GtkWidget            *widget,
1064                                  const GdkRectangle   *cell_area,
1065                                  const GdkRectangle   *background_area,
1066                                  GtkCellAllocCallback  callback,
1067                                  gpointer              callback_data)
1068 {
1069   GtkCellAreaBox        *box      = GTK_CELL_AREA_BOX (area);
1070   GtkCellAreaBoxPrivate *priv     = box->priv;
1071   GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1072   GSList                *allocated_cells, *l;
1073   GdkRectangle           cell_alloc, cell_background;
1074   gboolean               rtl;
1075
1076   rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1077          gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1078
1079   cell_alloc = *cell_area;
1080
1081   /* Get a list of cells with allocation sizes decided regardless
1082    * of alignments and pack order etc. */
1083   allocated_cells = get_allocated_cells (box, box_context, widget, 
1084                                          cell_area->width, cell_area->height);
1085
1086   for (l = allocated_cells; l; l = l->next)
1087     {
1088       AllocatedCell *cell = l->data;
1089
1090       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1091         {
1092           cell_alloc.x     = cell_area->x + cell->position;
1093           cell_alloc.width = cell->size;
1094         }
1095       else
1096         {
1097           cell_alloc.y      = cell_area->y + cell->position;
1098           cell_alloc.height = cell->size;
1099         }
1100
1101       /* Stop iterating over cells if they flow out of the render area,
1102        * this can happen because the render area can actually be
1103        * smaller than the requested area (treeview columns can
1104        * be user resizable and can be resized to be smaller than
1105        * the actual requested area). */
1106       if (cell_alloc.x > cell_area->x + cell_area->width ||
1107           cell_alloc.x + cell_alloc.width < cell_area->x ||
1108           cell_alloc.y > cell_area->y + cell_area->height)
1109         break;
1110
1111       /* Special case for the last cell (or first cell in rtl)... let the last cell consume 
1112        * the remaining space in the area (the last cell is allowed to consume the remaining 
1113        * space if the space given for rendering is actually larger than allocation, this can
1114        * happen in the expander GtkTreeViewColumn where only the deepest depth column
1115        * receives the allocation... shallow columns recieve more width). */
1116       if (!l->next)
1117         {
1118           if (rtl)
1119             {
1120               /* Fill the leading space for the first cell in the area (still last in the list) */
1121               cell_alloc.width = (cell_alloc.x - cell_area->x) + cell_alloc.width;
1122               cell_alloc.x     = cell_area->x;
1123             }
1124           else
1125             {
1126               cell_alloc.width  = cell_area->x + cell_area->width  - cell_alloc.x;
1127               cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y;
1128             }
1129         }
1130       else
1131         {
1132           /* If the cell we are rendering doesnt fit into the remaining space, clip it
1133            * so that the underlying renderer has a chance to deal with it (for instance
1134            * text renderers get a chance to ellipsize).
1135            */
1136           if (cell_alloc.x + cell_alloc.width > cell_area->x + cell_area->width)
1137             cell_alloc.width = cell_area->x + cell_area->width - cell_alloc.x;
1138
1139           if (cell_alloc.y + cell_alloc.height > cell_area->y + cell_area->height)
1140             cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y;
1141         }
1142
1143       /* Add portions of the background_area to the cell_alloc
1144        * to create the cell_background */
1145       cell_background = cell_alloc;
1146
1147       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1148         {
1149           if (l == allocated_cells)
1150             {
1151               /* Add the depth to the first cell */
1152               if (rtl)
1153                 {
1154                   cell_background.width += background_area->width - cell_area->width;
1155                   cell_background.x      = background_area->x + background_area->width - cell_background.width;
1156                 }
1157               else
1158                 {
1159                   cell_background.width += cell_area->x - background_area->x;
1160                   cell_background.x      = background_area->x;
1161                 }
1162             }
1163
1164           if (l->next == NULL)
1165             {
1166               /* Grant this cell the remaining space */
1167               int remain = cell_background.x - background_area->x;
1168
1169               if (rtl)
1170                 cell_background.x -= remain;
1171               else
1172                 cell_background.width = background_area->width - remain;
1173             }
1174
1175           cell_background.y      = background_area->y;
1176           cell_background.height = background_area->height;
1177         }
1178       else
1179         {
1180           if (l == allocated_cells)
1181             {
1182               cell_background.height += cell_background.y - background_area->y;
1183               cell_background.y       = background_area->y;
1184             }
1185
1186           if (l->next == NULL)
1187               cell_background.height = 
1188                 background_area->height - (cell_background.y - background_area->y);
1189
1190           cell_background.x     = background_area->x;
1191           cell_background.width = background_area->width;
1192         }
1193
1194       if (callback (cell->renderer, &cell_alloc, &cell_background, callback_data))
1195         break;
1196     }
1197
1198   g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL);
1199   g_slist_free (allocated_cells);
1200 }
1201
1202 static void
1203 gtk_cell_area_box_set_cell_property (GtkCellArea        *area,
1204                                      GtkCellRenderer    *renderer,
1205                                      guint               prop_id,
1206                                      const GValue       *value,
1207                                      GParamSpec         *pspec)
1208 {
1209   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area); 
1210   GtkCellAreaBoxPrivate *priv = box->priv;
1211   GList                 *node;
1212   CellInfo              *info;
1213   gboolean               rebuild = FALSE;
1214   gboolean               val;
1215   GtkPackType            pack_type;
1216
1217   node = g_list_find_custom (priv->cells, renderer, 
1218                              (GCompareFunc)cell_info_find);
1219   if (!node)
1220     return;
1221
1222   info = node->data;
1223
1224   switch (prop_id)
1225     {
1226     case CELL_PROP_EXPAND:
1227       val = g_value_get_boolean (value);
1228
1229       if (info->expand != val)
1230         {
1231           info->expand = val;
1232           rebuild      = TRUE;
1233         }
1234       break;
1235
1236     case CELL_PROP_ALIGN:
1237       val = g_value_get_boolean (value);
1238
1239       if (info->align != val)
1240         {
1241           info->align = val;
1242           rebuild     = TRUE;
1243         }
1244       break;
1245
1246     case CELL_PROP_PACK_TYPE:
1247       pack_type = g_value_get_enum (value);
1248
1249       if (info->pack != pack_type)
1250         {
1251           info->pack = pack_type;
1252           rebuild    = TRUE;
1253         }
1254       break;
1255     default:
1256       GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID (area, prop_id, pspec);
1257       break;
1258     }
1259
1260   /* Groups need to be rebuilt */
1261   if (rebuild)
1262     cell_groups_rebuild (box);
1263 }
1264
1265 static void
1266 gtk_cell_area_box_get_cell_property (GtkCellArea        *area,
1267                                      GtkCellRenderer    *renderer,
1268                                      guint               prop_id,
1269                                      GValue             *value,
1270                                      GParamSpec         *pspec)
1271 {
1272   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area); 
1273   GtkCellAreaBoxPrivate *priv = box->priv;
1274   GList                 *node;
1275   CellInfo              *info;
1276
1277   node = g_list_find_custom (priv->cells, renderer, 
1278                              (GCompareFunc)cell_info_find);
1279   if (!node)
1280     return;
1281
1282   info = node->data;
1283
1284   switch (prop_id)
1285     {
1286     case CELL_PROP_EXPAND:
1287       g_value_set_boolean (value, info->expand);
1288       break;
1289
1290     case CELL_PROP_ALIGN:
1291       g_value_set_boolean (value, info->align);
1292       break;
1293
1294     case CELL_PROP_PACK_TYPE:
1295       g_value_set_enum (value, info->pack);
1296       break;
1297     default:
1298       GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID (area, prop_id, pspec);
1299       break;
1300     }
1301 }
1302
1303
1304 static GtkCellAreaContext *
1305 gtk_cell_area_box_create_context (GtkCellArea *area)
1306 {
1307   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
1308   GtkCellAreaBoxPrivate *priv = box->priv;
1309   GtkCellAreaContext    *context =
1310     (GtkCellAreaContext *)g_object_new (GTK_TYPE_CELL_AREA_BOX_CONTEXT, 
1311                                      "area", area, NULL);
1312
1313   priv->contexts = g_slist_prepend (priv->contexts, context);
1314
1315   g_object_weak_ref (G_OBJECT (context), (GWeakNotify)context_weak_notify, box);
1316
1317   /* Tell the new group about our cell layout */
1318   init_context_group (box, GTK_CELL_AREA_BOX_CONTEXT (context));
1319
1320   return context;
1321 }
1322
1323 static GtkCellAreaContext *
1324 gtk_cell_area_box_copy_context (GtkCellArea        *area,
1325                                 GtkCellAreaContext *context)
1326 {
1327   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
1328   GtkCellAreaBoxPrivate *priv = box->priv;
1329   GtkCellAreaContext    *copy = 
1330     (GtkCellAreaContext *)gtk_cell_area_box_context_copy (GTK_CELL_AREA_BOX (area), 
1331                                                           GTK_CELL_AREA_BOX_CONTEXT (context));
1332
1333   priv->contexts = g_slist_prepend (priv->contexts, copy);
1334
1335   g_object_weak_ref (G_OBJECT (copy), (GWeakNotify)context_weak_notify, box);
1336
1337   return copy;
1338 }
1339
1340 static GtkSizeRequestMode 
1341 gtk_cell_area_box_get_request_mode (GtkCellArea *area)
1342 {
1343   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
1344   GtkCellAreaBoxPrivate *priv = box->priv;
1345
1346   return (priv->orientation) == GTK_ORIENTATION_HORIZONTAL ?
1347     GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH :
1348     GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
1349 }
1350
1351 static void
1352 compute_size (GtkCellAreaBox        *box,
1353               GtkOrientation         orientation,
1354               GtkCellAreaBoxContext *context,
1355               GtkWidget             *widget,
1356               gint                   for_size,
1357               gint                  *minimum_size,
1358               gint                  *natural_size)
1359 {
1360   GtkCellAreaBoxPrivate *priv = box->priv;
1361   GtkCellArea           *area = GTK_CELL_AREA (box);
1362   GList                 *list;
1363   gint                   i;
1364   gint                   min_size = 0;
1365   gint                   nat_size = 0;
1366   
1367   for (i = 0; i < priv->groups->len; i++)
1368     {
1369       CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
1370       gint       group_min_size = 0;
1371       gint       group_nat_size = 0;
1372
1373       for (list = group->cells; list; list = list->next)
1374         {
1375           CellInfo *info = list->data;
1376           gint      renderer_min_size, renderer_nat_size;
1377
1378           if (!gtk_cell_renderer_get_visible (info->renderer))
1379               continue;
1380           
1381           gtk_cell_area_request_renderer (area, info->renderer, orientation, widget, for_size, 
1382                                           &renderer_min_size, &renderer_nat_size);
1383
1384           if (orientation == priv->orientation)
1385             {
1386               if (min_size > 0)
1387                 {
1388                   min_size += priv->spacing;
1389                   nat_size += priv->spacing;
1390                 }
1391               
1392               if (group_min_size > 0)
1393                 {
1394                   group_min_size += priv->spacing;
1395                   group_nat_size += priv->spacing;
1396                 }
1397               
1398               min_size       += renderer_min_size;
1399               nat_size       += renderer_nat_size;
1400               group_min_size += renderer_min_size;
1401               group_nat_size += renderer_nat_size;
1402             }
1403           else
1404             {
1405               min_size       = MAX (min_size, renderer_min_size);
1406               nat_size       = MAX (nat_size, renderer_nat_size);
1407               group_min_size = MAX (group_min_size, renderer_min_size);
1408               group_nat_size = MAX (group_nat_size, renderer_nat_size);
1409             }
1410         }
1411
1412       if (orientation == GTK_ORIENTATION_HORIZONTAL)
1413         {
1414           if (for_size < 0)
1415             gtk_cell_area_box_context_push_group_width (context, group->id, group_min_size, group_nat_size);
1416           else
1417             gtk_cell_area_box_context_push_group_width_for_height (context, group->id, for_size,
1418                                                                    group_min_size, group_nat_size);
1419         }
1420       else
1421         {
1422           if (for_size < 0)
1423             gtk_cell_area_box_context_push_group_height (context, group->id, group_min_size, group_nat_size);
1424           else
1425             gtk_cell_area_box_context_push_group_height_for_width (context, group->id, for_size,
1426                                                                    group_min_size, group_nat_size);
1427         }
1428     }
1429
1430   *minimum_size = min_size;
1431   *natural_size = nat_size;
1432
1433   /* Update rtl state for focus navigation to work */
1434   priv->rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1435                gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1436 }
1437
1438 GtkRequestedSize *
1439 get_group_sizes (GtkCellArea    *area,
1440                  CellGroup      *group,
1441                  GtkOrientation  orientation,
1442                  GtkWidget      *widget,
1443                  gint           *n_sizes)
1444 {
1445   GtkRequestedSize *sizes;
1446   GList            *l;
1447   gint              i;
1448
1449   *n_sizes = count_visible_cells (group, NULL);
1450   sizes    = g_new (GtkRequestedSize, *n_sizes);
1451
1452   for (l = group->cells, i = 0; l; l = l->next)
1453     {
1454       CellInfo *info = l->data;
1455
1456       if (!gtk_cell_renderer_get_visible (info->renderer))
1457         continue;
1458
1459       sizes[i].data = info;
1460       
1461       gtk_cell_area_request_renderer (area, info->renderer,
1462                                       orientation, widget, -1,
1463                                       &sizes[i].minimum_size,
1464                                       &sizes[i].natural_size);
1465
1466       i++;
1467     }
1468
1469   return sizes;
1470 }
1471
1472 static void
1473 compute_group_size_for_opposing_orientation (GtkCellAreaBox     *box,
1474                                              CellGroup          *group,
1475                                              GtkWidget          *widget, 
1476                                              gint                for_size,
1477                                              gint               *minimum_size, 
1478                                              gint               *natural_size)
1479 {
1480   GtkCellAreaBoxPrivate *priv = box->priv;
1481   GtkCellArea           *area = GTK_CELL_AREA (box);
1482
1483   /* Exception for single cell groups */
1484   if (group->n_cells == 1)
1485     {
1486       CellInfo *info = group->cells->data;
1487
1488       gtk_cell_area_request_renderer (area, info->renderer,
1489                                       OPPOSITE_ORIENTATION (priv->orientation),
1490                                       widget, for_size, minimum_size, natural_size);
1491     }
1492   else
1493     {
1494       GtkRequestedSize *orientation_sizes;
1495       CellInfo         *info;
1496       gint              n_sizes, i;
1497       gint              avail_size     = for_size;
1498       gint              extra_size, extra_extra;
1499       gint              min_size = 0, nat_size = 0;
1500
1501       orientation_sizes = get_group_sizes (area, group, priv->orientation, widget, &n_sizes);
1502
1503       /* First naturally allocate the cells in the group into the for_size */
1504       avail_size -= (n_sizes - 1) * priv->spacing;
1505       for (i = 0; i < n_sizes; i++)
1506         avail_size -= orientation_sizes[i].minimum_size;
1507
1508       if (avail_size > 0)
1509         avail_size = gtk_distribute_natural_allocation (avail_size, n_sizes, orientation_sizes);
1510       else
1511         avail_size = 0;
1512
1513       /* Calculate/distribute expand for cells */
1514       if (group->expand_cells > 0)
1515         {
1516           extra_size  = avail_size / group->expand_cells;
1517           extra_extra = avail_size % group->expand_cells;
1518         }
1519       else
1520         extra_size = extra_extra = 0;
1521
1522       for (i = 0; i < n_sizes; i++)
1523         {
1524           gint cell_min, cell_nat;
1525
1526           info = orientation_sizes[i].data;
1527
1528           if (info->expand)
1529             {
1530               orientation_sizes[i].minimum_size += extra_size;
1531               if (extra_extra)
1532                 {
1533                   orientation_sizes[i].minimum_size++;
1534                   extra_extra--;
1535                 }
1536             }
1537
1538           gtk_cell_area_request_renderer (area, info->renderer,
1539                                           OPPOSITE_ORIENTATION (priv->orientation),
1540                                           widget, 
1541                                           orientation_sizes[i].minimum_size,
1542                                           &cell_min, &cell_nat);
1543
1544           min_size = MAX (min_size, cell_min);
1545           nat_size = MAX (nat_size, cell_nat);
1546         }
1547
1548       *minimum_size = min_size;
1549       *natural_size = nat_size;
1550
1551       g_free (orientation_sizes);
1552     }
1553 }
1554
1555 static void
1556 compute_size_for_opposing_orientation (GtkCellAreaBox        *box, 
1557                                        GtkCellAreaBoxContext *context, 
1558                                        GtkWidget             *widget, 
1559                                        gint                   for_size,
1560                                        gint                  *minimum_size, 
1561                                        gint                  *natural_size)
1562 {
1563   GtkCellAreaBoxPrivate *priv = box->priv;
1564   CellGroup             *group;
1565   GtkRequestedSize      *orientation_sizes;
1566   gint                   n_groups, n_expand_groups, i;
1567   gint                   avail_size = for_size;
1568   gint                   extra_size, extra_extra;
1569   gint                   min_size = 0, nat_size = 0;
1570
1571   n_expand_groups = count_expand_groups (box);
1572
1573   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1574     orientation_sizes = gtk_cell_area_box_context_get_widths (context, &n_groups);
1575   else
1576     orientation_sizes = gtk_cell_area_box_context_get_heights (context, &n_groups);
1577
1578   /* First start by naturally allocating space among groups of cells */
1579   avail_size -= (n_groups - 1) * priv->spacing;
1580   for (i = 0; i < n_groups; i++)
1581     avail_size -= orientation_sizes[i].minimum_size;
1582
1583   if (avail_size > 0)
1584     avail_size = gtk_distribute_natural_allocation (avail_size, n_groups, orientation_sizes);
1585   else
1586     avail_size = 0;
1587
1588   /* Calculate/distribute expand for groups */
1589   if (n_expand_groups > 0)
1590     {
1591       extra_size  = avail_size / n_expand_groups;
1592       extra_extra = avail_size % n_expand_groups;
1593     }
1594   else
1595     extra_size = extra_extra = 0;
1596
1597   /* Now we need to naturally allocate sizes for cells in each group
1598    * and push the height-for-width for each group accordingly while accumulating
1599    * the overall height-for-width for this row.
1600    */
1601   for (i = 0; i < n_groups; i++)
1602     {
1603       gint group_min, group_nat;
1604       gint group_idx = GPOINTER_TO_INT (orientation_sizes[i].data);
1605       
1606       group = &g_array_index (priv->groups, CellGroup, group_idx);
1607
1608       if (group->expand_cells > 0)
1609         {
1610           orientation_sizes[i].minimum_size += extra_size;
1611           if (extra_extra)
1612             {
1613               orientation_sizes[i].minimum_size++;
1614               extra_extra--;
1615             }
1616         }
1617
1618       /* Now we have the allocation for the group, request it's height-for-width */
1619       compute_group_size_for_opposing_orientation (box, group, widget,
1620                                                    orientation_sizes[i].minimum_size,
1621                                                    &group_min, &group_nat);
1622
1623       min_size = MAX (min_size, group_min);
1624       nat_size = MAX (nat_size, group_nat);
1625
1626       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1627         {
1628           gtk_cell_area_box_context_push_group_height_for_width (context, group_idx, for_size,
1629                                                                  group_min, group_nat);
1630         }
1631       else
1632         {
1633           gtk_cell_area_box_context_push_group_width_for_height (context, group_idx, for_size,
1634                                                                  group_min, group_nat);
1635         }
1636     }
1637
1638   *minimum_size = min_size;
1639   *natural_size = nat_size;
1640
1641   g_free (orientation_sizes);
1642
1643   /* Update rtl state for focus navigation to work */
1644   priv->rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1645                gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1646 }
1647
1648
1649
1650 static void
1651 gtk_cell_area_box_get_preferred_width (GtkCellArea        *area,
1652                                        GtkCellAreaContext *context,
1653                                        GtkWidget          *widget,
1654                                        gint               *minimum_width,
1655                                        gint               *natural_width)
1656 {
1657   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
1658   GtkCellAreaBoxContext *box_context;
1659   gint                   min_width, nat_width;
1660
1661   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
1662
1663   box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1664
1665   /* Compute the size of all renderers for current row data, 
1666    * bumping cell alignments in the context along the way */
1667   compute_size (box, GTK_ORIENTATION_HORIZONTAL,
1668                 box_context, widget, -1, &min_width, &nat_width);
1669
1670   if (minimum_width)
1671     *minimum_width = min_width;
1672
1673   if (natural_width)
1674     *natural_width = nat_width;
1675 }
1676
1677 static void
1678 gtk_cell_area_box_get_preferred_height (GtkCellArea        *area,
1679                                         GtkCellAreaContext *context,
1680                                         GtkWidget          *widget,
1681                                         gint               *minimum_height,
1682                                         gint               *natural_height)
1683 {
1684   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
1685   GtkCellAreaBoxContext *box_context;
1686   gint                   min_height, nat_height;
1687
1688   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
1689
1690   box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1691
1692   /* Compute the size of all renderers for current row data, 
1693    * bumping cell alignments in the context along the way */
1694   compute_size (box, GTK_ORIENTATION_VERTICAL,
1695                 box_context, widget, -1, &min_height, &nat_height);
1696
1697   if (minimum_height)
1698     *minimum_height = min_height;
1699
1700   if (natural_height)
1701     *natural_height = nat_height;
1702 }
1703
1704 static void
1705 gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea        *area,
1706                                                   GtkCellAreaContext *context,
1707                                                   GtkWidget          *widget,
1708                                                   gint                width,
1709                                                   gint               *minimum_height,
1710                                                   gint               *natural_height)
1711 {
1712   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
1713   GtkCellAreaBoxContext *box_context;
1714   GtkCellAreaBoxPrivate *priv;
1715   gint                   min_height, nat_height;
1716
1717   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
1718
1719   box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1720   priv        = box->priv;
1721
1722   if (priv->orientation == GTK_ORIENTATION_VERTICAL)
1723     {
1724       /* Add up vertical requests of height for width and push the overall 
1725        * cached sizes for alignments */
1726       compute_size (box, priv->orientation, box_context, widget, width, &min_height, &nat_height);
1727     }
1728   else
1729     {
1730       /* Juice: virtually allocate cells into the for_width using the 
1731        * alignments and then return the overall height for that width, and cache it */
1732       compute_size_for_opposing_orientation (box, box_context, widget, width, &min_height, &nat_height);
1733     }
1734
1735   if (minimum_height)
1736     *minimum_height = min_height;
1737
1738   if (natural_height)
1739     *natural_height = nat_height;
1740 }
1741
1742 static void
1743 gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea        *area,
1744                                                   GtkCellAreaContext *context,
1745                                                   GtkWidget          *widget,
1746                                                   gint                height,
1747                                                   gint               *minimum_width,
1748                                                   gint               *natural_width)
1749 {
1750   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
1751   GtkCellAreaBoxContext *box_context;
1752   GtkCellAreaBoxPrivate *priv;
1753   gint                   min_width, nat_width;
1754
1755   g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
1756
1757   box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1758   priv        = box->priv;
1759
1760   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1761     {
1762       /* Add up horizontal requests of width for height and push the overall 
1763        * cached sizes for alignments */
1764       compute_size (box, priv->orientation, box_context, widget, height, &min_width, &nat_width);
1765     }
1766   else
1767     {
1768       /* Juice: horizontally allocate cells into the for_height using the 
1769        * alignments and then return the overall width for that height, and cache it */
1770       compute_size_for_opposing_orientation (box, box_context, widget, height, &min_width, &nat_width);
1771     }
1772
1773   if (minimum_width)
1774     *minimum_width = min_width;
1775
1776   if (natural_width)
1777     *natural_width = nat_width;
1778 }
1779
1780 enum {
1781   FOCUS_NONE,
1782   FOCUS_PREV,
1783   FOCUS_NEXT,
1784   FOCUS_LAST_CELL
1785 };
1786
1787 static gboolean
1788 gtk_cell_area_box_focus (GtkCellArea      *area,
1789                          GtkDirectionType  direction)
1790 {
1791   GtkCellAreaBox        *box   = GTK_CELL_AREA_BOX (area);
1792   GtkCellAreaBoxPrivate *priv  = box->priv;
1793   gint                   cycle = FOCUS_NONE;
1794   gboolean               cycled_focus = FALSE;
1795   GtkCellRenderer       *focus_cell;
1796
1797   focus_cell = gtk_cell_area_get_focus_cell (area);
1798
1799   /* Special case, when there is no activatable cell, focus
1800    * is painted around the entire area... in this case we
1801    * let focus leave the area directly.
1802    */
1803   if (focus_cell && !gtk_cell_area_is_activatable (area))
1804     {
1805       gtk_cell_area_set_focus_cell (area, NULL);
1806       return FALSE;
1807     }
1808
1809   switch (direction)
1810     {
1811     case GTK_DIR_TAB_FORWARD:
1812       cycle = priv->rtl ? FOCUS_PREV : FOCUS_NEXT;
1813       break;
1814     case GTK_DIR_TAB_BACKWARD:
1815       cycle = priv->rtl ? FOCUS_NEXT : FOCUS_PREV;
1816       break;
1817     case GTK_DIR_UP: 
1818       if (priv->orientation == GTK_ORIENTATION_VERTICAL || !priv->last_focus_cell)
1819         cycle = FOCUS_PREV;
1820       else if (!focus_cell)
1821         cycle = FOCUS_LAST_CELL;
1822       break;
1823     case GTK_DIR_DOWN:
1824       if (priv->orientation == GTK_ORIENTATION_VERTICAL || !priv->last_focus_cell)
1825         cycle = FOCUS_NEXT;
1826       else if (!focus_cell)
1827         cycle = FOCUS_LAST_CELL;
1828       break;
1829     case GTK_DIR_LEFT:
1830       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !priv->last_focus_cell)
1831         cycle = priv->rtl ? FOCUS_NEXT : FOCUS_PREV;
1832       else if (!focus_cell)
1833         cycle = FOCUS_LAST_CELL;
1834       break;
1835     case GTK_DIR_RIGHT:
1836       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !priv->last_focus_cell)
1837         cycle = priv->rtl ? FOCUS_PREV : FOCUS_NEXT;
1838       else if (!focus_cell)
1839         cycle = FOCUS_LAST_CELL;
1840       break;
1841     default:
1842       break;
1843     }
1844
1845   if (cycle == FOCUS_LAST_CELL)
1846     {
1847       gtk_cell_area_set_focus_cell (area, priv->last_focus_cell);
1848       cycled_focus = TRUE;
1849     }
1850   else if (cycle != FOCUS_NONE)
1851     {
1852       gboolean  found_cell = FALSE;
1853       GList    *list;
1854       gint      i;
1855
1856       /* If there is no focused cell, focus on the first (or last) one in the list */
1857       if (!focus_cell)
1858         found_cell = TRUE;
1859
1860       for (i = (cycle == FOCUS_NEXT) ? 0 : priv->groups->len -1; 
1861            cycled_focus == FALSE && i >= 0 && i < priv->groups->len;
1862            i = (cycle == FOCUS_NEXT) ? i + 1 : i - 1)
1863         {
1864           CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
1865           
1866           for (list = (cycle == FOCUS_NEXT) ? g_list_first (group->cells) : g_list_last (group->cells); 
1867                cycled_focus == FALSE && list; list = (cycle == FOCUS_NEXT) ? list->next : list->prev)
1868             {
1869               CellInfo *info = list->data;
1870
1871               if (info->renderer == focus_cell)
1872                 found_cell = TRUE;
1873               else if (found_cell && /* Dont give focus to cells that are siblings to a focus cell */
1874                        gtk_cell_area_get_focus_from_sibling (area, info->renderer) == NULL)
1875                 {
1876                   gtk_cell_area_set_focus_cell (area, info->renderer);
1877                   cycled_focus = TRUE;
1878                 }
1879             }
1880         }
1881     }
1882
1883   if (!cycled_focus)
1884     gtk_cell_area_set_focus_cell (area, NULL);
1885
1886   return cycled_focus;
1887 }
1888
1889
1890 /*************************************************************
1891  *                    GtkCellLayoutIface                     *
1892  *************************************************************/
1893 static void
1894 gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface)
1895 {
1896   iface->pack_start = gtk_cell_area_box_layout_pack_start;
1897   iface->pack_end   = gtk_cell_area_box_layout_pack_end;
1898   iface->reorder    = gtk_cell_area_box_layout_reorder;
1899 }
1900
1901 static void
1902 gtk_cell_area_box_layout_pack_start (GtkCellLayout      *cell_layout,
1903                                      GtkCellRenderer    *renderer,
1904                                      gboolean            expand)
1905 {
1906   gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, TRUE);
1907 }
1908
1909 static void
1910 gtk_cell_area_box_layout_pack_end (GtkCellLayout      *cell_layout,
1911                                    GtkCellRenderer    *renderer,
1912                                    gboolean            expand)
1913 {
1914   gtk_cell_area_box_pack_end (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, TRUE);
1915 }
1916
1917 static void
1918 gtk_cell_area_box_layout_reorder (GtkCellLayout      *cell_layout,
1919                                   GtkCellRenderer    *renderer,
1920                                   gint                position)
1921 {
1922   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (cell_layout);
1923   GtkCellAreaBoxPrivate *priv = box->priv;
1924   GList                 *node;
1925   CellInfo              *info;
1926   
1927   node = g_list_find_custom (priv->cells, renderer, 
1928                              (GCompareFunc)cell_info_find);
1929
1930   if (node)
1931     {
1932       info = node->data;
1933
1934       priv->cells = g_list_delete_link (priv->cells, node);
1935       priv->cells = g_list_insert (priv->cells, info, position);
1936
1937       cell_groups_rebuild (box);
1938     }
1939 }
1940
1941 /*************************************************************
1942  *                            API                            *
1943  *************************************************************/
1944 /**
1945  * gtk_cell_area_box_new:
1946  * 
1947  * Creates a new #GtkCellAreaBox.
1948  *
1949  * Return value: a newly created #GtkCellAreaBox
1950  */
1951 GtkCellArea *
1952 gtk_cell_area_box_new (void)
1953 {
1954   return (GtkCellArea *)g_object_new (GTK_TYPE_CELL_AREA_BOX, NULL);
1955 }
1956
1957 /**
1958  * gtk_cell_area_box_pack_start:
1959  * @box: a #GtkCellAreaBox
1960  * @renderer: the #GtkCellRenderer to add
1961  * @expand: whether @renderer should receive extra space when the area receives
1962  * more than its natural size
1963  * @align: whether @renderer should be aligned in adjacent rows.
1964  *
1965  * Adds @renderer to @box, packed with reference to the start of @box.
1966  *
1967  * The @renderer is packed after any other #GtkCellRenderer packed with reference
1968  * to the start of @box.
1969  *
1970  * Since: 3.0
1971  */
1972 void
1973 gtk_cell_area_box_pack_start  (GtkCellAreaBox  *box,
1974                                GtkCellRenderer *renderer,
1975                                gboolean         expand,
1976                                gboolean         align)
1977 {
1978   GtkCellAreaBoxPrivate *priv;
1979   CellInfo              *info;
1980
1981   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
1982   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1983
1984   priv = box->priv;
1985
1986   if (g_list_find_custom (priv->cells, renderer, 
1987                           (GCompareFunc)cell_info_find))
1988     {
1989       g_warning ("Refusing to add the same cell renderer to a GtkCellAreaBox twice");
1990       return;
1991     }
1992
1993   info = cell_info_new (renderer, GTK_PACK_START, expand, align);
1994
1995   priv->cells = g_list_append (priv->cells, info);
1996
1997   cell_groups_rebuild (box);
1998 }
1999
2000 /**
2001  * gtk_cell_area_box_pack_end:
2002  * @box: a #GtkCellAreaBox
2003  * @renderer: the #GtkCellRenderer to add
2004  * @expand: whether @renderer should receive extra space when the area receives
2005  * more than its natural size
2006  * @align: whether @renderer should be aligned in adjacent rows.
2007  *
2008  * Adds @renderer to @box, packed with reference to the end of @box.
2009  *
2010  * The @renderer is packed after (away from end of) any other #GtkCellRenderer
2011  * packed with reference to the end of @box.
2012  *
2013  * Since: 3.0
2014  */
2015 void
2016 gtk_cell_area_box_pack_end (GtkCellAreaBox  *box,
2017                             GtkCellRenderer *renderer,
2018                             gboolean         expand, 
2019                             gboolean         align)
2020 {
2021   GtkCellAreaBoxPrivate *priv;
2022   CellInfo              *info;
2023
2024   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
2025   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
2026
2027   priv = box->priv;
2028
2029   if (g_list_find_custom (priv->cells, renderer, 
2030                           (GCompareFunc)cell_info_find))
2031     {
2032       g_warning ("Refusing to add the same cell renderer to a GtkCellArea twice");
2033       return;
2034     }
2035
2036   info = cell_info_new (renderer, GTK_PACK_END, expand, align);
2037
2038   priv->cells = g_list_append (priv->cells, info);
2039
2040   cell_groups_rebuild (box);
2041 }
2042
2043 /**
2044  * gtk_cell_area_box_get_spacing:
2045  * @box: a #GtkCellAreaBox
2046  *
2047  * Gets the spacing added between cell renderers.
2048  *
2049  * Return value: the space added between cell renderers in @box.
2050  *
2051  * Since: 3.0
2052  */
2053 gint
2054 gtk_cell_area_box_get_spacing (GtkCellAreaBox  *box)
2055 {
2056   g_return_val_if_fail (GTK_IS_CELL_AREA_BOX (box), 0);
2057
2058   return box->priv->spacing;
2059 }
2060
2061 /**
2062  * gtk_cell_area_box_set_spacing:
2063  * @box: a #GtkCellAreaBox
2064  * @spacing: the space to add between #GtkCellRenderers
2065  *
2066  * Sets the spacing to add between cell renderers in @box.
2067  *
2068  * Since: 3.0
2069  */
2070 void
2071 gtk_cell_area_box_set_spacing (GtkCellAreaBox  *box,
2072                                gint             spacing)
2073 {
2074   GtkCellAreaBoxPrivate *priv;
2075
2076   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
2077
2078   priv = box->priv;
2079
2080   if (priv->spacing != spacing)
2081     {
2082       priv->spacing = spacing;
2083
2084       g_object_notify (G_OBJECT (box), "spacing");
2085
2086       /* Notify that size needs to be requested again */
2087       reset_contexts (box);
2088     }
2089 }