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