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