]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellareabox.c
Committing new (and simplified) focus handling approach for GtkCellArea.
[~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 gboolean  gtk_cell_area_box_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->focus = gtk_cell_area_box_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   CellGroup             *group_ptr;
406   GList                 *cells, *l;
407   guint                  id = 0;
408
409   cell_groups_clear (box);
410
411   if (!priv->cells)
412     return;
413
414   cells = list_consecutive_cells (box);
415
416   /* First group is implied */
417   g_array_append_val (priv->groups, group);
418   group_ptr = &g_array_index (priv->groups, CellGroup, id);
419
420   for (l = cells; l; l = l->next)
421     {
422       CellInfo *info = l->data;
423
424       /* A new group starts with any aligned cell, the first group is implied */
425       if (info->align && l != cells)
426         {
427           memset (&group, 0x0, sizeof (CellGroup));
428           group.id = ++id;
429
430           g_array_append_val (priv->groups, group);
431           group_ptr = &g_array_index (priv->groups, CellGroup, id);
432         }
433
434       group_ptr->cells = g_list_prepend (group_ptr->cells, info);
435       group_ptr->n_cells++;
436
437       /* A group expands if it contains any expand cells */
438       if (info->expand)
439         group_ptr->expand_cells++;
440     }
441
442   g_list_free (cells);
443
444   for (id = 0; id < priv->groups->len; id++)
445     {
446       group_ptr = &g_array_index (priv->groups, CellGroup, id);
447
448       group_ptr->cells = g_list_reverse (group_ptr->cells);
449     }
450
451   /* Iters need to be updated with the new grouping information */
452   init_iter_groups (box);
453 }
454
455 static gint
456 count_visible_cells (CellGroup *group, 
457                      gint      *expand_cells)
458 {
459   GList *l;
460   gint   visible_cells = 0;
461   gint   n_expand_cells = 0;
462
463   for (l = group->cells; l; l = l->next)
464     {
465       CellInfo *info = l->data;
466
467       if (gtk_cell_renderer_get_visible (info->renderer))
468         {
469           visible_cells++;
470
471           if (info->expand)
472             n_expand_cells++;
473         }
474     }
475
476   if (expand_cells)
477     *expand_cells = n_expand_cells;
478
479   return visible_cells;
480 }
481
482 static gint
483 count_expand_groups (GtkCellAreaBox  *box)
484 {
485   GtkCellAreaBoxPrivate *priv = box->priv;
486   gint                   i;
487   gint                   expand_groups = 0;
488
489   for (i = 0; i < priv->groups->len; i++)
490     {
491       CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
492
493       if (group->expand_cells > 0)
494         expand_groups++;
495     }
496
497   return expand_groups;
498 }
499
500 static void 
501 iter_weak_notify (GtkCellAreaBox     *box,
502                   GtkCellAreaBoxIter *dead_iter)
503 {
504   GtkCellAreaBoxPrivate *priv = box->priv;
505
506   priv->iters = g_slist_remove (priv->iters, dead_iter);
507 }
508
509 static void
510 init_iter_group (GtkCellAreaBox     *box,
511                  GtkCellAreaBoxIter *iter)
512 {
513   GtkCellAreaBoxPrivate *priv = box->priv;
514   gint                  *expand_groups, i;
515
516   expand_groups = g_new (gboolean, priv->groups->len);
517
518   for (i = 0; i < priv->groups->len; i++)
519     {
520       CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
521
522       expand_groups[i] = (group->expand_cells > 0);
523     }
524
525   /* This call implies flushing the request info */
526   gtk_cell_area_box_init_groups (iter, priv->groups->len, expand_groups);
527   g_free (expand_groups);
528 }
529
530 static void
531 init_iter_groups (GtkCellAreaBox *box)
532 {
533   GtkCellAreaBoxPrivate *priv = box->priv;
534   GSList                *l;
535
536   /* When the box's groups are reconstructed, iters need to
537    * be reinitialized.
538    */
539   for (l = priv->iters; l; l = l->next)
540     {
541       GtkCellAreaBoxIter *iter = l->data;
542
543       init_iter_group (box, iter);
544     }
545 }
546
547 static void
548 flush_iters (GtkCellAreaBox *box)
549 {
550   GtkCellAreaBoxPrivate *priv = box->priv;
551   GSList                *l;
552
553   /* When the box layout changes, iters need to
554    * be flushed and sizes for the box get requested again
555    */
556   for (l = priv->iters; l; l = l->next)
557     {
558       GtkCellAreaIter *iter = l->data;
559
560       gtk_cell_area_iter_flush (iter);
561     }
562 }
563
564 /* Returns an allocation for each cell in the orientation of the box,
565  * used in ->render()/->event() implementations to get a straight-forward
566  * list of allocated cells to operate on.
567  */
568 static GSList *
569 get_allocated_cells (GtkCellAreaBox     *box,
570                      GtkCellAreaBoxIter *iter,
571                      GtkWidget          *widget)
572 {
573   const GtkCellAreaBoxAllocation *group_allocs;
574   GtkCellArea                    *area = GTK_CELL_AREA (box);
575   GtkCellAreaBoxPrivate          *priv = box->priv;
576   GList                          *cell_list;
577   GSList                         *allocated_cells = NULL;
578   gint                            i, j, n_allocs;
579
580   group_allocs = gtk_cell_area_box_iter_get_orientation_allocs (iter, &n_allocs);
581   if (!group_allocs)
582     {
583       g_warning ("Trying to operate on an unallocated GtkCellAreaIter, "
584                  "GtkCellAreaBox requires that the iter be allocated at least "
585                  "in the orientation of the box");
586       return NULL;
587     }
588
589   for (i = 0; i < n_allocs; i++)
590     {
591       /* We dont always allocate all groups, sometimes the requested group has only invisible
592        * cells for every row, hence the usage of group_allocs[i].group_idx here
593        */
594       CellGroup *group = &g_array_index (priv->groups, CellGroup, group_allocs[i].group_idx);
595
596       /* Exception for single cell groups */
597       if (group->n_cells == 1)
598         {
599           CellInfo      *info = group->cells->data;
600           AllocatedCell *cell = 
601             allocated_cell_new (info->renderer, group_allocs[i].position, group_allocs[i].size);
602
603           allocated_cells = g_slist_prepend (allocated_cells, cell);
604         }
605       else
606         {
607           GtkRequestedSize *sizes;
608           gint              avail_size, position;
609           gint              visible_cells, expand_cells;
610           gint              extra_size, extra_extra;
611
612           visible_cells = count_visible_cells (group, &expand_cells);
613
614           /* If this row has no visible cells in this group, just
615            * skip the allocation */
616           if (visible_cells == 0)
617             continue;
618
619           /* Offset the allocation to the group position and allocate into 
620            * the group's available size */
621           position   = group_allocs[i].position;
622           avail_size = group_allocs[i].size;
623
624           sizes = g_new (GtkRequestedSize, visible_cells);
625
626           for (j = 0, cell_list = group->cells; cell_list; cell_list = cell_list->next)
627             {
628               CellInfo *info = cell_list->data;
629
630               if (!gtk_cell_renderer_get_visible (info->renderer))
631                 continue;
632
633               gtk_cell_area_request_renderer (area, info->renderer,
634                                               priv->orientation,
635                                               widget, -1,
636                                               &sizes[j].minimum_size,
637                                               &sizes[j].natural_size);
638
639               sizes[j].data = info;
640               avail_size   -= sizes[j].minimum_size;
641
642               j++;
643             }
644
645           /* Distribute cells naturally within the group */
646           avail_size -= (visible_cells - 1) * priv->spacing;
647           avail_size = gtk_distribute_natural_allocation (avail_size, visible_cells, sizes);
648
649           /* Calculate/distribute expand for cells */
650           if (expand_cells > 0)
651             {
652               extra_size  = avail_size / expand_cells;
653               extra_extra = avail_size % expand_cells;
654             }
655           else
656             extra_size = extra_extra = 0;
657
658           /* Create the allocated cells (loop only over visible cells here) */
659           for (j = 0; j < visible_cells; j++)
660             {
661               CellInfo      *info = sizes[j].data;
662               AllocatedCell *cell;
663
664               if (info->expand)
665                 {
666                   sizes[j].minimum_size += extra_size;
667                   if (extra_extra)
668                     {
669                       sizes[j].minimum_size++;
670                       extra_extra--;
671                     }
672                 }
673               
674               cell = allocated_cell_new (info->renderer, position, sizes[j].minimum_size);
675
676               allocated_cells = g_slist_prepend (allocated_cells, cell);
677               
678               position += sizes[j].minimum_size;
679               position += priv->spacing;
680             }
681
682           g_free (sizes);
683         }
684     }
685
686   /* Note it might not be important to reverse the list here at all,
687    * we have the correct positions, no need to allocate from left to right */
688   return g_slist_reverse (allocated_cells);
689 }
690
691 /*************************************************************
692  *                      GObjectClass                         *
693  *************************************************************/
694 static void
695 gtk_cell_area_box_finalize (GObject *object)
696 {
697   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (object);
698   GtkCellAreaBoxPrivate *priv = box->priv;
699   GSList                *l;
700
701   /* Unref/free the iter list */
702   for (l = priv->iters; l; l = l->next)
703     g_object_weak_unref (G_OBJECT (l->data), (GWeakNotify)iter_weak_notify, box);
704
705   g_slist_free (priv->iters);
706   priv->iters = NULL;
707
708   /* Free the cell grouping info */
709   cell_groups_clear (box);
710   g_array_free (priv->groups, TRUE);
711   
712   G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->finalize (object);
713 }
714
715 static void
716 gtk_cell_area_box_dispose (GObject *object)
717 {
718   G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->dispose (object);
719 }
720
721 static void
722 gtk_cell_area_box_set_property (GObject       *object,
723                                 guint          prop_id,
724                                 const GValue  *value,
725                                 GParamSpec    *pspec)
726 {
727   GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
728
729   switch (prop_id)
730     {
731     case PROP_ORIENTATION:
732       box->priv->orientation = g_value_get_enum (value);
733
734       /* Notify that size needs to be requested again */
735       flush_iters (box);
736       break;
737     case PROP_SPACING:
738       gtk_cell_area_box_set_spacing (box, g_value_get_int (value));
739       break;
740     default:
741       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
742       break;
743     }
744 }
745
746 static void
747 gtk_cell_area_box_get_property (GObject     *object,
748                                 guint        prop_id,
749                                 GValue      *value,
750                                 GParamSpec  *pspec)
751 {
752   GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
753
754   switch (prop_id)
755     {
756     case PROP_ORIENTATION:
757       g_value_set_enum (value, box->priv->orientation);
758       break;
759     case PROP_SPACING:
760       g_value_set_int (value, gtk_cell_area_box_get_spacing (box));
761       break;
762     default:
763       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
764       break;
765     }
766 }
767
768 /*************************************************************
769  *                    GtkCellAreaClass                       *
770  *************************************************************/
771 static void      
772 gtk_cell_area_box_add (GtkCellArea        *area,
773                        GtkCellRenderer    *renderer)
774 {
775   gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area),
776                                 renderer, FALSE, TRUE);
777 }
778
779 static void
780 gtk_cell_area_box_remove (GtkCellArea        *area,
781                           GtkCellRenderer    *renderer)
782 {
783   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
784   GtkCellAreaBoxPrivate *priv = box->priv;
785   GList                 *node;
786
787   node = g_list_find_custom (priv->cells, renderer, 
788                              (GCompareFunc)cell_info_find);
789
790   if (node)
791     {
792       CellInfo *info = node->data;
793
794       cell_info_free (info);
795
796       priv->cells = g_list_delete_link (priv->cells, node);
797
798       /* Reconstruct cell groups */
799       cell_groups_rebuild (box);
800     }
801   else
802     g_warning ("Trying to remove a cell renderer that is not present GtkCellAreaBox");
803 }
804
805 static void
806 gtk_cell_area_box_forall (GtkCellArea        *area,
807                           GtkCellCallback     callback,
808                           gpointer            callback_data)
809 {
810   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
811   GtkCellAreaBoxPrivate *priv = box->priv;
812   GList                 *list;
813
814   for (list = priv->cells; list; list = list->next)
815     {
816       CellInfo *info = list->data;
817
818       callback (info->renderer, callback_data);
819     }
820 }
821
822 static void
823 gtk_cell_area_box_get_cell_allocation (GtkCellArea          *area,
824                                        GtkCellAreaIter      *iter,      
825                                        GtkWidget            *widget,
826                                        GtkCellRenderer      *renderer,
827                                        const GdkRectangle   *cell_area,
828                                        GdkRectangle         *allocation)
829 {
830   GtkCellAreaBox        *box      = GTK_CELL_AREA_BOX (area);
831   GtkCellAreaBoxPrivate *priv     = box->priv;
832   GtkCellAreaBoxIter    *box_iter = GTK_CELL_AREA_BOX_ITER (iter);
833   GSList                *allocated_cells, *l;
834
835   *allocation = *cell_area;
836
837   /* Get a list of cells with allocation sizes decided regardless
838    * of alignments and pack order etc. */
839   allocated_cells = get_allocated_cells (box, box_iter, widget);
840
841   for (l = allocated_cells; l; l = l->next)
842     {
843       AllocatedCell *cell = l->data;
844
845       if (cell->renderer == renderer)
846         {
847           if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
848             {
849               allocation->x     = cell_area->x + cell->position;
850               allocation->width = cell->size;
851             }
852           else
853             {
854               allocation->y      = cell_area->y + cell->position;
855               allocation->height = cell->size;
856             }
857
858           break;
859         }
860     }
861
862   g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL);
863   g_slist_free (allocated_cells);
864 }
865
866 enum {
867   FOCUS_NONE,
868   FOCUS_PREV,
869   FOCUS_NEXT
870 };
871
872 static gint
873 gtk_cell_area_box_event (GtkCellArea          *area,
874                          GtkCellAreaIter      *iter,
875                          GtkWidget            *widget,
876                          GdkEvent             *event,
877                          const GdkRectangle   *cell_area,
878                          GtkCellRendererState  flags)
879 {
880   gint retval;
881
882   /* First let the parent class handle activation of cells via keystrokes */
883   retval = 
884     GTK_CELL_AREA_CLASS (gtk_cell_area_box_parent_class)->event (area, iter, widget,
885                                                                  event, cell_area, flags);
886   
887   if (retval)
888     return retval;
889
890   /* Also detect mouse events, for mouse events we need to allocate the renderers
891    * and find which renderer needs to be activated.
892    */
893
894
895   return 0;
896 }
897
898 static void
899 gtk_cell_area_box_render (GtkCellArea          *area,
900                           GtkCellAreaIter      *iter,
901                           GtkWidget            *widget,
902                           cairo_t              *cr,
903                           const GdkRectangle   *cell_area,
904                           GtkCellRendererState  flags)
905 {
906   GtkCellAreaBox        *box      = GTK_CELL_AREA_BOX (area);
907   GtkCellAreaBoxPrivate *priv     = box->priv;
908   GtkCellAreaBoxIter    *box_iter = GTK_CELL_AREA_BOX_ITER (iter);
909   GSList                *allocated_cells, *l;
910   GdkRectangle           background_area, inner_area;
911   GtkCellRenderer       *focus_cell = NULL;
912
913   if (flags & GTK_CELL_RENDERER_FOCUSED)
914     {
915       focus_cell = gtk_cell_area_get_focus_cell (area);
916       flags &= ~GTK_CELL_RENDERER_FOCUSED;
917     }
918
919   background_area = *cell_area;
920
921   /* Get a list of cells with allocation sizes decided regardless
922    * of alignments and pack order etc. */
923   allocated_cells = get_allocated_cells (box, box_iter, widget);
924
925   for (l = allocated_cells; l; l = l->next)
926     {
927       AllocatedCell       *cell = l->data;
928       GtkCellRendererState cell_fields = 0;
929
930       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
931         {
932           background_area.x     = cell_area->x + cell->position;
933           background_area.width = cell->size;
934         }
935       else
936         {
937           background_area.y      = cell_area->y + cell->position;
938           background_area.height = cell->size;
939         }
940
941       if (cell->renderer == focus_cell)
942         {
943           g_print ("Rendering a cell with the focus flag !\n");
944           
945           cell_fields |= GTK_CELL_RENDERER_FOCUSED;
946         }
947
948       /* Remove margins from the background area to produce the cell area
949        */
950       gtk_cell_area_inner_cell_area (area, &background_area, &inner_area);
951
952       /* We have to do some per-cell considerations for the 'flags'
953        * for focus handling */
954       gtk_cell_renderer_render (cell->renderer, cr, widget,
955                                 &background_area, &inner_area,
956                                 flags | cell_fields);
957     }
958
959   g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL);
960   g_slist_free (allocated_cells);
961 }
962
963 static void
964 gtk_cell_area_box_set_cell_property (GtkCellArea        *area,
965                                      GtkCellRenderer    *renderer,
966                                      guint               prop_id,
967                                      const GValue       *value,
968                                      GParamSpec         *pspec)
969 {
970   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area); 
971   GtkCellAreaBoxPrivate *priv = box->priv;
972   GList                 *node;
973   CellInfo              *info;
974   gboolean               rebuild = FALSE;
975   gboolean               val;
976   GtkPackType            pack_type;
977
978   node = g_list_find_custom (priv->cells, renderer, 
979                              (GCompareFunc)cell_info_find);
980   if (!node)
981     return;
982
983   info = node->data;
984
985   switch (prop_id)
986     {
987     case CELL_PROP_EXPAND:
988       val = g_value_get_boolean (value);
989
990       if (info->expand != val)
991         {
992           info->expand = val;
993           rebuild      = TRUE;
994         }
995       break;
996
997     case CELL_PROP_ALIGN:
998       val = g_value_get_boolean (value);
999
1000       if (info->align != val)
1001         {
1002           info->align = val;
1003           rebuild     = TRUE;
1004         }
1005       break;
1006
1007     case CELL_PROP_PACK_TYPE:
1008       pack_type = g_value_get_enum (value);
1009
1010       if (info->pack != pack_type)
1011         {
1012           info->pack = pack_type;
1013           rebuild    = TRUE;
1014         }
1015       break;
1016     default:
1017       GTK_CELL_AREA_WARN_INVALID_CHILD_PROPERTY_ID (area, prop_id, pspec);
1018       break;
1019     }
1020
1021   /* Groups need to be rebuilt */
1022   if (rebuild)
1023     cell_groups_rebuild (box);
1024 }
1025
1026 static void
1027 gtk_cell_area_box_get_cell_property (GtkCellArea        *area,
1028                                      GtkCellRenderer    *renderer,
1029                                      guint               prop_id,
1030                                      GValue             *value,
1031                                      GParamSpec         *pspec)
1032 {
1033   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area); 
1034   GtkCellAreaBoxPrivate *priv = box->priv;
1035   GList                 *node;
1036   CellInfo              *info;
1037
1038   node = g_list_find_custom (priv->cells, renderer, 
1039                              (GCompareFunc)cell_info_find);
1040   if (!node)
1041     return;
1042
1043   info = node->data;
1044
1045   switch (prop_id)
1046     {
1047     case CELL_PROP_EXPAND:
1048       g_value_set_boolean (value, info->expand);
1049       break;
1050
1051     case CELL_PROP_ALIGN:
1052       g_value_set_boolean (value, info->align);
1053       break;
1054
1055     case CELL_PROP_PACK_TYPE:
1056       g_value_set_enum (value, info->pack);
1057       break;
1058     default:
1059       GTK_CELL_AREA_WARN_INVALID_CHILD_PROPERTY_ID (area, prop_id, pspec);
1060       break;
1061     }
1062 }
1063
1064
1065 static GtkCellAreaIter *
1066 gtk_cell_area_box_create_iter (GtkCellArea *area)
1067 {
1068   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
1069   GtkCellAreaBoxPrivate *priv = box->priv;
1070   GtkCellAreaIter       *iter =
1071     (GtkCellAreaIter *)g_object_new (GTK_TYPE_CELL_AREA_BOX_ITER, 
1072                                      "area", area, NULL);
1073
1074   priv->iters = g_slist_prepend (priv->iters, iter);
1075
1076   g_object_weak_ref (G_OBJECT (iter), (GWeakNotify)iter_weak_notify, box);
1077
1078   /* Tell the new group about our cell layout */
1079   init_iter_group (box, GTK_CELL_AREA_BOX_ITER (iter));
1080
1081   return iter;
1082 }
1083
1084 static GtkSizeRequestMode 
1085 gtk_cell_area_box_get_request_mode (GtkCellArea *area)
1086 {
1087   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
1088   GtkCellAreaBoxPrivate *priv = box->priv;
1089
1090   return (priv->orientation) == GTK_ORIENTATION_HORIZONTAL ?
1091     GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH :
1092     GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
1093 }
1094
1095 static void
1096 compute_size (GtkCellAreaBox     *box,
1097               GtkOrientation      orientation,
1098               GtkCellAreaBoxIter *iter,
1099               GtkWidget          *widget,
1100               gint                for_size,
1101               gint               *minimum_size,
1102               gint               *natural_size)
1103 {
1104   GtkCellAreaBoxPrivate *priv = box->priv;
1105   GtkCellArea           *area = GTK_CELL_AREA (box);
1106   GList                 *list;
1107   gint                   i;
1108   gint                   min_size = 0;
1109   gint                   nat_size = 0;
1110   
1111   for (i = 0; i < priv->groups->len; i++)
1112     {
1113       CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
1114       gint       group_min_size = 0;
1115       gint       group_nat_size = 0;
1116
1117       for (list = group->cells; list; list = list->next)
1118         {
1119           CellInfo *info = list->data;
1120           gint      renderer_min_size, renderer_nat_size;
1121
1122           if (!gtk_cell_renderer_get_visible (info->renderer))
1123               continue;
1124           
1125           gtk_cell_area_request_renderer (area, info->renderer, orientation, widget, for_size, 
1126                                           &renderer_min_size, &renderer_nat_size);
1127
1128           if (orientation == priv->orientation)
1129             {
1130               if (min_size > 0)
1131                 {
1132                   min_size += priv->spacing;
1133                   nat_size += priv->spacing;
1134                 }
1135               
1136               if (group_min_size > 0)
1137                 {
1138                   group_min_size += priv->spacing;
1139                   group_nat_size += priv->spacing;
1140                 }
1141               
1142               min_size       += renderer_min_size;
1143               nat_size       += renderer_nat_size;
1144               group_min_size += renderer_min_size;
1145               group_nat_size += renderer_nat_size;
1146             }
1147           else
1148             {
1149               min_size       = MAX (min_size, renderer_min_size);
1150               nat_size       = MAX (nat_size, renderer_nat_size);
1151               group_min_size = MAX (group_min_size, renderer_min_size);
1152               group_nat_size = MAX (group_nat_size, renderer_nat_size);
1153             }
1154         }
1155
1156       if (orientation == GTK_ORIENTATION_HORIZONTAL)
1157         {
1158           if (for_size < 0)
1159             gtk_cell_area_box_iter_push_group_width (iter, group->id, group_min_size, group_nat_size);
1160           else
1161             gtk_cell_area_box_iter_push_group_width_for_height (iter, group->id, for_size,
1162                                                                 group_min_size, group_nat_size);
1163         }
1164       else
1165         {
1166           if (for_size < 0)
1167             gtk_cell_area_box_iter_push_group_height (iter, group->id, group_min_size, group_nat_size);
1168           else
1169             gtk_cell_area_box_iter_push_group_height_for_width (iter, group->id, for_size,
1170                                                                 group_min_size, group_nat_size);
1171         }
1172     }
1173
1174   *minimum_size = min_size;
1175   *natural_size = nat_size;
1176 }
1177
1178 GtkRequestedSize *
1179 get_group_sizes (GtkCellArea    *area,
1180                  CellGroup      *group,
1181                  GtkOrientation  orientation,
1182                  GtkWidget      *widget,
1183                  gint           *n_sizes)
1184 {
1185   GtkRequestedSize *sizes;
1186   GList            *l;
1187   gint              i;
1188
1189   *n_sizes = count_visible_cells (group, NULL);
1190   sizes    = g_new (GtkRequestedSize, *n_sizes);
1191
1192   for (l = group->cells, i = 0; l; l = l->next)
1193     {
1194       CellInfo *info = l->data;
1195
1196       if (!gtk_cell_renderer_get_visible (info->renderer))
1197         continue;
1198
1199       sizes[i].data = info;
1200       
1201       gtk_cell_area_request_renderer (area, info->renderer,
1202                                       orientation, widget, -1,
1203                                       &sizes[i].minimum_size,
1204                                       &sizes[i].natural_size);
1205
1206       i++;
1207     }
1208
1209   return sizes;
1210 }
1211
1212 static void
1213 compute_group_size_for_opposing_orientation (GtkCellAreaBox     *box,
1214                                              CellGroup          *group,
1215                                              GtkWidget          *widget, 
1216                                              gint                for_size,
1217                                              gint               *minimum_size, 
1218                                              gint               *natural_size)
1219 {
1220   GtkCellAreaBoxPrivate *priv = box->priv;
1221   GtkCellArea           *area = GTK_CELL_AREA (box);
1222
1223   /* Exception for single cell groups */
1224   if (group->n_cells == 1)
1225     {
1226       CellInfo *info = group->cells->data;
1227
1228       gtk_cell_area_request_renderer (area, info->renderer,
1229                                       OPPOSITE_ORIENTATION (priv->orientation),
1230                                       widget, for_size, minimum_size, natural_size);
1231     }
1232   else
1233     {
1234       GtkRequestedSize *orientation_sizes;
1235       CellInfo         *info;
1236       gint              n_sizes, i;
1237       gint              avail_size     = for_size;
1238       gint              extra_size, extra_extra;
1239       gint              min_size = 0, nat_size = 0;
1240
1241       orientation_sizes = get_group_sizes (area, group, priv->orientation, widget, &n_sizes);
1242
1243       /* First naturally allocate the cells in the group into the for_size */
1244       avail_size -= (n_sizes - 1) * priv->spacing;
1245       for (i = 0; i < n_sizes; i++)
1246         avail_size -= orientation_sizes[i].minimum_size;
1247
1248       avail_size = gtk_distribute_natural_allocation (avail_size, n_sizes, orientation_sizes);
1249
1250       /* Calculate/distribute expand for cells */
1251       if (group->expand_cells > 0)
1252         {
1253           extra_size  = avail_size / group->expand_cells;
1254           extra_extra = avail_size % group->expand_cells;
1255         }
1256       else
1257         extra_size = extra_extra = 0;
1258
1259       for (i = 0; i < n_sizes; i++)
1260         {
1261           gint cell_min, cell_nat;
1262
1263           info = orientation_sizes[i].data;
1264
1265           if (info->expand)
1266             {
1267               orientation_sizes[i].minimum_size += extra_size;
1268               if (extra_extra)
1269                 {
1270                   orientation_sizes[i].minimum_size++;
1271                   extra_extra--;
1272                 }
1273             }
1274
1275           gtk_cell_area_request_renderer (area, info->renderer,
1276                                           OPPOSITE_ORIENTATION (priv->orientation),
1277                                           widget, 
1278                                           orientation_sizes[i].minimum_size,
1279                                           &cell_min, &cell_nat);
1280
1281           min_size = MAX (min_size, cell_min);
1282           nat_size = MAX (nat_size, cell_nat);
1283         }
1284
1285       *minimum_size = min_size;
1286       *natural_size = nat_size;
1287
1288       g_free (orientation_sizes);
1289     }
1290 }
1291
1292 static void
1293 compute_size_for_opposing_orientation (GtkCellAreaBox     *box, 
1294                                        GtkCellAreaBoxIter *iter, 
1295                                        GtkWidget          *widget, 
1296                                        gint                for_size,
1297                                        gint               *minimum_size, 
1298                                        gint               *natural_size)
1299 {
1300   GtkCellAreaBoxPrivate *priv = box->priv;
1301   CellGroup             *group;
1302   GtkRequestedSize      *orientation_sizes;
1303   gint                   n_groups, n_expand_groups, i;
1304   gint                   avail_size = for_size;
1305   gint                   extra_size, extra_extra;
1306   gint                   min_size = 0, nat_size = 0;
1307
1308   n_expand_groups = count_expand_groups (box);
1309
1310   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1311     orientation_sizes = gtk_cell_area_box_iter_get_widths (iter, &n_groups);
1312   else
1313     orientation_sizes = gtk_cell_area_box_iter_get_heights (iter, &n_groups);
1314
1315   /* First start by naturally allocating space among groups of cells */
1316   avail_size -= (n_groups - 1) * priv->spacing;
1317   for (i = 0; i < n_groups; i++)
1318     avail_size -= orientation_sizes[i].minimum_size;
1319
1320   avail_size = gtk_distribute_natural_allocation (avail_size, n_groups, orientation_sizes);
1321
1322   /* Calculate/distribute expand for groups */
1323   if (n_expand_groups > 0)
1324     {
1325       extra_size  = avail_size / n_expand_groups;
1326       extra_extra = avail_size % n_expand_groups;
1327     }
1328   else
1329     extra_size = extra_extra = 0;
1330
1331   /* Now we need to naturally allocate sizes for cells in each group
1332    * and push the height-for-width for each group accordingly while accumulating
1333    * the overall height-for-width for this row.
1334    */
1335   for (i = 0; i < n_groups; i++)
1336     {
1337       gint group_min, group_nat;
1338       gint group_idx = GPOINTER_TO_INT (orientation_sizes[i].data);
1339       
1340       group = &g_array_index (priv->groups, CellGroup, group_idx);
1341
1342       if (group->expand_cells > 0)
1343         {
1344           orientation_sizes[i].minimum_size += extra_size;
1345           if (extra_extra)
1346             {
1347               orientation_sizes[i].minimum_size++;
1348               extra_extra--;
1349             }
1350         }
1351
1352       /* Now we have the allocation for the group, request it's height-for-width */
1353       compute_group_size_for_opposing_orientation (box, group, widget,
1354                                                    orientation_sizes[i].minimum_size,
1355                                                    &group_min, &group_nat);
1356
1357       min_size = MAX (min_size, group_min);
1358       nat_size = MAX (nat_size, group_nat);
1359
1360       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1361         {
1362           gtk_cell_area_box_iter_push_group_height_for_width (iter, group_idx, for_size,
1363                                                               group_min, group_nat);
1364         }
1365       else
1366         {
1367           gtk_cell_area_box_iter_push_group_width_for_height (iter, group_idx, for_size,
1368                                                               group_min, group_nat);
1369         }
1370     }
1371
1372   *minimum_size = min_size;
1373   *natural_size = nat_size;
1374
1375   g_free (orientation_sizes);
1376 }
1377
1378
1379
1380 static void
1381 gtk_cell_area_box_get_preferred_width (GtkCellArea        *area,
1382                                        GtkCellAreaIter    *iter,
1383                                        GtkWidget          *widget,
1384                                        gint               *minimum_width,
1385                                        gint               *natural_width)
1386 {
1387   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
1388   GtkCellAreaBoxIter    *box_iter;
1389   gint                   min_width, nat_width;
1390
1391   g_return_if_fail (GTK_IS_CELL_AREA_BOX_ITER (iter));
1392
1393   box_iter = GTK_CELL_AREA_BOX_ITER (iter);
1394
1395   /* Compute the size of all renderers for current row data, 
1396    * bumping cell alignments in the iter along the way */
1397   compute_size (box, GTK_ORIENTATION_HORIZONTAL,
1398                 box_iter, widget, -1, &min_width, &nat_width);
1399
1400   if (minimum_width)
1401     *minimum_width = min_width;
1402
1403   if (natural_width)
1404     *natural_width = nat_width;
1405 }
1406
1407 static void
1408 gtk_cell_area_box_get_preferred_height (GtkCellArea        *area,
1409                                         GtkCellAreaIter    *iter,
1410                                         GtkWidget          *widget,
1411                                         gint               *minimum_height,
1412                                         gint               *natural_height)
1413 {
1414   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
1415   GtkCellAreaBoxIter    *box_iter;
1416   gint                   min_height, nat_height;
1417
1418   g_return_if_fail (GTK_IS_CELL_AREA_BOX_ITER (iter));
1419
1420   box_iter = GTK_CELL_AREA_BOX_ITER (iter);
1421
1422   /* Compute the size of all renderers for current row data, 
1423    * bumping cell alignments in the iter along the way */
1424   compute_size (box, GTK_ORIENTATION_VERTICAL,
1425                 box_iter, widget, -1, &min_height, &nat_height);
1426
1427   if (minimum_height)
1428     *minimum_height = min_height;
1429
1430   if (natural_height)
1431     *natural_height = nat_height;
1432 }
1433
1434 static void
1435 gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea        *area,
1436                                                   GtkCellAreaIter    *iter,
1437                                                   GtkWidget          *widget,
1438                                                   gint                width,
1439                                                   gint               *minimum_height,
1440                                                   gint               *natural_height)
1441 {
1442   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
1443   GtkCellAreaBoxIter    *box_iter;
1444   GtkCellAreaBoxPrivate *priv;
1445   gint                   min_height, nat_height;
1446
1447   g_return_if_fail (GTK_IS_CELL_AREA_BOX_ITER (iter));
1448
1449   box_iter = GTK_CELL_AREA_BOX_ITER (iter);
1450   priv     = box->priv;
1451
1452   if (priv->orientation == GTK_ORIENTATION_VERTICAL)
1453     {
1454       /* Add up vertical requests of height for width and push the overall 
1455        * cached sizes for alignments */
1456       compute_size (box, priv->orientation, box_iter, widget, width, &min_height, &nat_height);
1457     }
1458   else
1459     {
1460       /* Juice: virtually allocate cells into the for_width using the 
1461        * alignments and then return the overall height for that width, and cache it */
1462       compute_size_for_opposing_orientation (box, box_iter, widget, width, &min_height, &nat_height);
1463     }
1464
1465   if (minimum_height)
1466     *minimum_height = min_height;
1467
1468   if (natural_height)
1469     *natural_height = nat_height;
1470 }
1471
1472 static void
1473 gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea        *area,
1474                                                   GtkCellAreaIter    *iter,
1475                                                   GtkWidget          *widget,
1476                                                   gint                height,
1477                                                   gint               *minimum_width,
1478                                                   gint               *natural_width)
1479 {
1480   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
1481   GtkCellAreaBoxIter    *box_iter;
1482   GtkCellAreaBoxPrivate *priv;
1483   gint                   min_width, nat_width;
1484
1485   g_return_if_fail (GTK_IS_CELL_AREA_BOX_ITER (iter));
1486
1487   box_iter = GTK_CELL_AREA_BOX_ITER (iter);
1488   priv     = box->priv;
1489
1490   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1491     {
1492       /* Add up horizontal requests of width for height and push the overall 
1493        * cached sizes for alignments */
1494       compute_size (box, priv->orientation, box_iter, widget, height, &min_width, &nat_width);
1495     }
1496   else
1497     {
1498       /* Juice: horizontally allocate cells into the for_height using the 
1499        * alignments and then return the overall width for that height, and cache it */
1500       compute_size_for_opposing_orientation (box, box_iter, widget, height, &min_width, &nat_width);
1501     }
1502
1503   if (minimum_width)
1504     *minimum_width = min_width;
1505
1506   if (natural_width)
1507     *natural_width = nat_width;
1508 }
1509
1510 static gboolean
1511 gtk_cell_area_box_focus (GtkCellArea      *area,
1512                          GtkDirectionType  direction)
1513 {
1514   GtkCellAreaBox        *box   = GTK_CELL_AREA_BOX (area);
1515   GtkCellAreaBoxPrivate *priv  = box->priv;
1516   gint                   cycle = FOCUS_NONE;
1517   gboolean               cycled_focus = FALSE;
1518   GtkCellRenderer       *focus_cell;
1519
1520   focus_cell = gtk_cell_area_get_focus_cell (area);
1521
1522   switch (direction)
1523     {
1524     case GTK_DIR_TAB_FORWARD:
1525       cycle = FOCUS_NEXT;
1526       break;
1527     case GTK_DIR_TAB_BACKWARD:
1528       cycle = FOCUS_PREV;
1529       break;
1530     case GTK_DIR_UP: 
1531       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1532         return FALSE;
1533       else
1534         cycle = FOCUS_PREV;
1535       break;
1536     case GTK_DIR_DOWN:
1537       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1538         return FALSE;
1539       else
1540         cycle = FOCUS_NEXT;
1541       break;
1542     case GTK_DIR_LEFT:
1543       if (priv->orientation == GTK_ORIENTATION_VERTICAL)
1544         return FALSE;
1545       else
1546         cycle = FOCUS_PREV;
1547       break;
1548     case GTK_DIR_RIGHT:
1549       if (priv->orientation == GTK_ORIENTATION_VERTICAL)
1550         return FALSE;
1551       else
1552         cycle = FOCUS_NEXT;
1553       break;
1554     default:
1555       break;
1556     }
1557
1558   if (cycle != FOCUS_NONE)
1559     {
1560       gboolean  found_cell = FALSE;
1561       GList    *list;
1562       gint      i;
1563
1564       /* If there is no focused cell, focus on the first one in the list */
1565       if (!focus_cell)
1566         found_cell = TRUE;
1567
1568       for (i = (cycle == FOCUS_NEXT) ? 0 : priv->groups->len -1; 
1569            i >= 0 && i < priv->groups->len;
1570            i = (cycle == FOCUS_NEXT) ? i + 1 : i - 1)
1571         {
1572           CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
1573           
1574           for (list = (cycle == FOCUS_NEXT) ? g_list_first (group->cells) : g_list_last (group->cells); 
1575                list; list = (cycle == FOCUS_NEXT) ? list->next : list->prev)
1576             {
1577               CellInfo *info = list->data;
1578
1579               if (!found_cell && info->renderer == focus_cell)
1580                 found_cell = TRUE;
1581               else if (found_cell)
1582                 {
1583                   if (gtk_cell_renderer_can_focus (info->renderer))
1584                     {
1585                       gtk_cell_area_set_focus_cell (area, info->renderer);
1586
1587                       cycled_focus = TRUE;
1588                       break;
1589                     }
1590                 }
1591             }
1592         }
1593     }
1594   return cycled_focus;
1595 }
1596
1597
1598 /*************************************************************
1599  *                    GtkCellLayoutIface                     *
1600  *************************************************************/
1601 static void
1602 gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface)
1603 {
1604   iface->pack_start = gtk_cell_area_box_layout_pack_start;
1605   iface->pack_end   = gtk_cell_area_box_layout_pack_end;
1606   iface->reorder    = gtk_cell_area_box_layout_reorder;
1607 }
1608
1609 static void
1610 gtk_cell_area_box_layout_pack_start (GtkCellLayout      *cell_layout,
1611                                      GtkCellRenderer    *renderer,
1612                                      gboolean            expand)
1613 {
1614   gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, TRUE);
1615 }
1616
1617 static void
1618 gtk_cell_area_box_layout_pack_end (GtkCellLayout      *cell_layout,
1619                                    GtkCellRenderer    *renderer,
1620                                    gboolean            expand)
1621 {
1622   gtk_cell_area_box_pack_end (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, TRUE);
1623 }
1624
1625 static void
1626 gtk_cell_area_box_layout_reorder (GtkCellLayout      *cell_layout,
1627                                   GtkCellRenderer    *renderer,
1628                                   gint                position)
1629 {
1630   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (cell_layout);
1631   GtkCellAreaBoxPrivate *priv = box->priv;
1632   GList                 *node;
1633   CellInfo              *info;
1634   
1635   node = g_list_find_custom (priv->cells, renderer, 
1636                              (GCompareFunc)cell_info_find);
1637
1638   if (node)
1639     {
1640       info = node->data;
1641
1642       priv->cells = g_list_delete_link (priv->cells, node);
1643       priv->cells = g_list_insert (priv->cells, info, position);
1644
1645       cell_groups_rebuild (box);
1646     }
1647 }
1648
1649 /*************************************************************
1650  *                            API                            *
1651  *************************************************************/
1652 GtkCellArea *
1653 gtk_cell_area_box_new (void)
1654 {
1655   return (GtkCellArea *)g_object_new (GTK_TYPE_CELL_AREA_BOX, NULL);
1656 }
1657
1658 void
1659 gtk_cell_area_box_pack_start  (GtkCellAreaBox  *box,
1660                                GtkCellRenderer *renderer,
1661                                gboolean         expand,
1662                                gboolean         align)
1663 {
1664   GtkCellAreaBoxPrivate *priv;
1665   CellInfo              *info;
1666
1667   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
1668   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1669
1670   priv = box->priv;
1671
1672   if (g_list_find_custom (priv->cells, renderer, 
1673                           (GCompareFunc)cell_info_find))
1674     {
1675       g_warning ("Refusing to add the same cell renderer to a GtkCellAreaBox twice");
1676       return;
1677     }
1678
1679   info = cell_info_new (renderer, GTK_PACK_START, expand, align);
1680
1681   priv->cells = g_list_append (priv->cells, info);
1682
1683   cell_groups_rebuild (box);
1684 }
1685
1686 void
1687 gtk_cell_area_box_pack_end (GtkCellAreaBox  *box,
1688                             GtkCellRenderer *renderer,
1689                             gboolean         expand, 
1690                             gboolean         align)
1691 {
1692   GtkCellAreaBoxPrivate *priv;
1693   CellInfo              *info;
1694
1695   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
1696   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1697
1698   priv = box->priv;
1699
1700   if (g_list_find_custom (priv->cells, renderer, 
1701                           (GCompareFunc)cell_info_find))
1702     {
1703       g_warning ("Refusing to add the same cell renderer to a GtkCellArea twice");
1704       return;
1705     }
1706
1707   info = cell_info_new (renderer, GTK_PACK_END, expand, align);
1708
1709   priv->cells = g_list_append (priv->cells, info);
1710
1711   cell_groups_rebuild (box);
1712 }
1713
1714 gint
1715 gtk_cell_area_box_get_spacing (GtkCellAreaBox  *box)
1716 {
1717   g_return_val_if_fail (GTK_IS_CELL_AREA_BOX (box), 0);
1718
1719   return box->priv->spacing;
1720 }
1721
1722 void
1723 gtk_cell_area_box_set_spacing (GtkCellAreaBox  *box,
1724                                gint             spacing)
1725 {
1726   GtkCellAreaBoxPrivate *priv;
1727
1728   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
1729
1730   priv = box->priv;
1731
1732   if (priv->spacing != spacing)
1733     {
1734       priv->spacing = spacing;
1735
1736       g_object_notify (G_OBJECT (box), "spacing");
1737
1738       /* Notify that size needs to be requested again */
1739       flush_iters (box);
1740     }
1741 }