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