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