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