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