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