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