]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellareabox.c
Added GtkCellRendererState flags to GtkCellArea->event/render() methods
[~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
66 static void      gtk_cell_area_box_set_cell_property              (GtkCellArea          *area,
67                                                                    GtkCellRenderer      *renderer,
68                                                                    guint                 prop_id,
69                                                                    const GValue         *value,
70                                                                    GParamSpec           *pspec);
71 static void      gtk_cell_area_box_get_cell_property              (GtkCellArea          *area,
72                                                                    GtkCellRenderer      *renderer,
73                                                                    guint                 prop_id,
74                                                                    GValue               *value,
75                                                                    GParamSpec           *pspec);
76
77 static GtkCellAreaIter    *gtk_cell_area_box_create_iter          (GtkCellArea          *area);
78 static GtkSizeRequestMode  gtk_cell_area_box_get_request_mode     (GtkCellArea          *area);
79 static void      gtk_cell_area_box_get_preferred_width            (GtkCellArea          *area,
80                                                                    GtkCellAreaIter      *iter,
81                                                                    GtkWidget            *widget,
82                                                                    gint                 *minimum_width,
83                                                                    gint                 *natural_width);
84 static void      gtk_cell_area_box_get_preferred_height           (GtkCellArea          *area,
85                                                                    GtkCellAreaIter      *iter,
86                                                                    GtkWidget            *widget,
87                                                                    gint                 *minimum_height,
88                                                                    gint                 *natural_height);
89 static void      gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea          *area,
90                                                                    GtkCellAreaIter      *iter,
91                                                                    GtkWidget            *widget,
92                                                                    gint                  width,
93                                                                    gint                 *minimum_height,
94                                                                    gint                 *natural_height);
95 static void      gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea          *area,
96                                                                    GtkCellAreaIter      *iter,
97                                                                    GtkWidget            *widget,
98                                                                    gint                  height,
99                                                                    gint                 *minimum_width,
100                                                                    gint                 *natural_width);
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 static CellGroup     *cell_group_new         (guint               id);
147 static void           cell_group_free        (CellGroup          *group);
148 static AllocatedCell *allocated_cell_new     (GtkCellRenderer    *renderer,
149                                               gint                position,
150                                               gint                size);
151 static void           allocated_cell_free    (AllocatedCell      *cell);
152 static GList         *list_consecutive_cells (GtkCellAreaBox     *box);
153 static GList         *construct_cell_groups  (GtkCellAreaBox     *box);
154 static gint           count_expand_groups    (GtkCellAreaBox     *box);
155 static void           iter_weak_notify       (GtkCellAreaBox     *box,
156                                               GtkCellAreaBoxIter *dead_iter);
157 static void           flush_iters            (GtkCellAreaBox     *box);
158 static void           init_iter_groups       (GtkCellAreaBox     *box);
159 static void           init_iter_group        (GtkCellAreaBox     *box,
160                                               GtkCellAreaBoxIter *iter);
161 static GSList        *get_allocated_cells    (GtkCellAreaBox     *box,
162                                               GtkCellAreaBoxIter *iter,
163                                               GtkWidget          *widget);
164
165
166 struct _GtkCellAreaBoxPrivate
167 {
168   GtkOrientation  orientation;
169
170   GList          *cells;
171   GList          *groups;
172
173   GSList         *iters;
174
175   gint            spacing;
176 };
177
178 enum {
179   PROP_0,
180   PROP_ORIENTATION,
181   PROP_SPACING
182 };
183
184 enum {
185   CELL_PROP_0,
186   CELL_PROP_EXPAND,
187   CELL_PROP_ALIGN,
188   CELL_PROP_PACK_TYPE
189 };
190
191 G_DEFINE_TYPE_WITH_CODE (GtkCellAreaBox, gtk_cell_area_box, GTK_TYPE_CELL_AREA,
192                          G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
193                                                 gtk_cell_area_box_cell_layout_init)
194                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL));
195
196 #define OPPOSITE_ORIENTATION(orientation)                       \
197   ((orientation) == GTK_ORIENTATION_HORIZONTAL ?                \
198    GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL)
199
200 static void
201 gtk_cell_area_box_init (GtkCellAreaBox *box)
202 {
203   GtkCellAreaBoxPrivate *priv;
204
205   box->priv = G_TYPE_INSTANCE_GET_PRIVATE (box,
206                                            GTK_TYPE_CELL_AREA_BOX,
207                                            GtkCellAreaBoxPrivate);
208   priv = box->priv;
209
210   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
211   priv->cells       = NULL;
212   priv->groups      = NULL;
213   priv->iters       = NULL;
214   priv->spacing     = 0;
215 }
216
217 static void 
218 gtk_cell_area_box_class_init (GtkCellAreaBoxClass *class)
219 {
220   GObjectClass     *object_class = G_OBJECT_CLASS (class);
221   GtkCellAreaClass *area_class   = GTK_CELL_AREA_CLASS (class);
222
223   /* GObjectClass */
224   object_class->finalize     = gtk_cell_area_box_finalize;
225   object_class->dispose      = gtk_cell_area_box_dispose;
226   object_class->set_property = gtk_cell_area_box_set_property;
227   object_class->get_property = gtk_cell_area_box_get_property;
228
229   /* GtkCellAreaClass */
230   area_class->add               = gtk_cell_area_box_add;
231   area_class->remove            = gtk_cell_area_box_remove;
232   area_class->forall            = gtk_cell_area_box_forall;
233   area_class->event             = gtk_cell_area_box_event;
234   area_class->render            = gtk_cell_area_box_render;
235   area_class->set_cell_property = gtk_cell_area_box_set_cell_property;
236   area_class->get_cell_property = gtk_cell_area_box_get_cell_property;
237   
238   area_class->create_iter                    = gtk_cell_area_box_create_iter;
239   area_class->get_request_mode               = gtk_cell_area_box_get_request_mode;
240   area_class->get_preferred_width            = gtk_cell_area_box_get_preferred_width;
241   area_class->get_preferred_height           = gtk_cell_area_box_get_preferred_height;
242   area_class->get_preferred_height_for_width = gtk_cell_area_box_get_preferred_height_for_width;
243   area_class->get_preferred_width_for_height = gtk_cell_area_box_get_preferred_width_for_height;
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 CellGroup *
326 cell_group_new (guint id)
327 {
328   CellGroup *group = g_slice_new0 (CellGroup);
329
330   group->id = id;
331
332   return group;
333 }
334
335 static void
336 cell_group_free (CellGroup *group)
337 {
338   g_list_free (group->cells);
339   g_slice_free (CellGroup, group);  
340 }
341
342 static AllocatedCell *
343 allocated_cell_new (GtkCellRenderer *renderer,
344                     gint             position,
345                     gint             size)
346 {
347   AllocatedCell *cell = g_slice_new (AllocatedCell);
348
349   cell->renderer = renderer;
350   cell->position = position;
351   cell->size     = size;
352
353   return cell;
354 }
355
356 static void
357 allocated_cell_free (AllocatedCell *cell)
358 {
359   g_slice_free (AllocatedCell, cell);
360 }
361
362 static GList *
363 list_consecutive_cells (GtkCellAreaBox *box)
364 {
365   GtkCellAreaBoxPrivate *priv = box->priv;
366   GList                 *l, *consecutive_cells = NULL, *pack_end_cells = NULL;
367   CellInfo              *info;
368
369   /* List cells in consecutive order taking their 
370    * PACK_START/PACK_END options into account 
371    */
372   for (l = priv->cells; l; l = l->next)
373     {
374       info = l->data;
375       
376       if (info->pack == GTK_PACK_START)
377         consecutive_cells = g_list_prepend (consecutive_cells, info);
378     }
379
380   for (l = priv->cells; l; l = l->next)
381     {
382       info = l->data;
383       
384       if (info->pack == GTK_PACK_END)
385         pack_end_cells = g_list_prepend (pack_end_cells, info);
386     }
387
388   consecutive_cells = g_list_reverse (consecutive_cells);
389   consecutive_cells = g_list_concat (consecutive_cells, pack_end_cells);
390
391   return consecutive_cells;
392 }
393
394 static GList *
395 construct_cell_groups (GtkCellAreaBox  *box)
396 {
397   GtkCellAreaBoxPrivate *priv = box->priv;
398   CellGroup             *group;
399   GList                 *cells, *l;
400   GList                 *groups = NULL;
401   guint                  id = 0;
402
403   if (!priv->cells)
404     return NULL;
405
406   cells  = list_consecutive_cells (box);
407   group  = cell_group_new (id++);
408   groups = g_list_prepend (groups, group);
409
410   for (l = cells; l; l = l->next)
411     {
412       CellInfo *info = l->data;
413
414       /* A new group starts with any aligned cell, the first group is implied */
415       if (info->align && l != cells)
416         {
417           group  = cell_group_new (id++);
418           groups = g_list_prepend (groups, group);
419         }
420
421       group->cells = g_list_prepend (group->cells, info);
422       group->n_cells++;
423
424       /* A group expands if it contains any expand cells */
425       if (info->expand)
426         group->expand_cells ++;
427     }
428
429   g_list_free (cells);
430
431   for (l = cells; l; l = l->next)
432     {
433       group = l->data;
434       group->cells = g_list_reverse (group->cells);
435     }
436
437   return g_list_reverse (groups);
438 }
439
440 static gint
441 count_expand_groups (GtkCellAreaBox  *box)
442 {
443   GtkCellAreaBoxPrivate *priv = box->priv;
444   GList                 *l;
445   gint                   expand_groups = 0;
446
447   for (l = priv->groups; l; l = l->next)
448     {
449       CellGroup *group = l->data;
450
451       if (group->expand_cells > 0)
452         expand_groups++;
453     }
454
455   return expand_groups;
456 }
457
458 static void 
459 iter_weak_notify (GtkCellAreaBox     *box,
460                   GtkCellAreaBoxIter *dead_iter)
461 {
462   GtkCellAreaBoxPrivate *priv = box->priv;
463
464   priv->iters = g_slist_remove (priv->iters, dead_iter);
465 }
466
467 static void
468 init_iter_group (GtkCellAreaBox     *box,
469                  GtkCellAreaBoxIter *iter)
470 {
471   GtkCellAreaBoxPrivate *priv = box->priv;
472   gint                   n_groups, *expand_groups, i;
473   GList                 *l;
474
475   n_groups      = g_list_length (priv->groups);
476   expand_groups = g_new (gboolean, n_groups);
477
478   for (i = 0, l = priv->groups; l; l = l->next, i++)
479     {
480       CellGroup *group = l->data;
481
482       expand_groups[i] = (group->expand_cells > 0);
483     }
484
485   gtk_cell_area_box_init_groups (iter, n_groups, expand_groups);
486   g_free (expand_groups);
487 }
488
489 static void
490 init_iter_groups (GtkCellAreaBox *box)
491 {
492   GtkCellAreaBoxPrivate *priv = box->priv;
493   GSList                *l;
494
495   /* When the box's groups are reconstructed, iters need to
496    * be reinitialized.
497    */
498   for (l = priv->iters; l; l = l->next)
499     {
500       GtkCellAreaBoxIter *iter = l->data;
501
502       init_iter_group (box, iter);
503     }
504 }
505
506 static void
507 flush_iters (GtkCellAreaBox *box)
508 {
509   GtkCellAreaBoxPrivate *priv = box->priv;
510   GSList                *l;
511
512   /* When the box layout changes, iters need to
513    * be flushed and sizes for the box get requested again
514    */
515   for (l = priv->iters; l; l = l->next)
516     {
517       GtkCellAreaIter *iter = l->data;
518
519       gtk_cell_area_iter_flush (iter);
520     }
521 }
522
523 /* Returns an allocation for each cell in the orientation of the box,
524  * used in ->render()/->event() implementations to get a straight-forward
525  * list of allocated cells to operate on.
526  */
527 static GSList *
528 get_allocated_cells (GtkCellAreaBox     *box,
529                      GtkCellAreaBoxIter *iter,
530                      GtkWidget          *widget)
531 {
532   const GtkCellAreaBoxAllocation *group_allocs;
533   GtkCellArea                    *area = GTK_CELL_AREA (box);
534   GtkCellAreaBoxPrivate          *priv = box->priv;
535   GList                          *group_list, *cell_list;
536   GSList                         *allocated_cells = NULL;
537   gint                            i, j, n_allocs;
538
539   group_allocs = gtk_cell_area_box_iter_get_orientation_allocs (iter, &n_allocs);
540   if (!group_allocs)
541     {
542       g_warning ("Trying to operate on an unallocated GtkCellAreaIter, "
543                  "GtkCellAreaBox requires that the iter be allocated at least "
544                  "in the orientation of the box");
545       return NULL;
546     }
547
548   for (i = 0, group_list = priv->groups; group_list; i++, group_list = group_list->next)
549     {
550       CellGroup *group = group_list->data;
551
552       /* Exception for single cell groups */
553       if (group->n_cells == 1)
554         {
555           CellInfo      *info = group->cells->data;
556           AllocatedCell *cell = 
557             allocated_cell_new (info->renderer, group_allocs[i].position, group_allocs[i].size);
558
559           allocated_cells = g_slist_prepend (allocated_cells, cell);
560         }
561       else
562         {
563           GtkRequestedSize *sizes      = g_new (GtkRequestedSize, group->n_cells);
564           gint              avail_size = group_allocs[i].size;
565           gint              position   = group_allocs[i].position;
566           gint              extra_size, extra_extra;
567
568           for (j = 0, cell_list = group->cells; cell_list; j++, cell_list = cell_list->next)
569             {
570               CellInfo *info = cell_list->data;
571
572               gtk_cell_area_request_renderer (area, info->renderer,
573                                               priv->orientation,
574                                               widget, -1,
575                                               &sizes[j].minimum_size,
576                                               &sizes[j].natural_size);
577
578               avail_size -= sizes[j].minimum_size;
579             }
580
581           /* Distribute cells naturally within the group */
582           avail_size -= (group->n_cells - 1) * priv->spacing;
583           avail_size = gtk_distribute_natural_allocation (avail_size, group->n_cells, sizes);
584
585           /* Calculate/distribute expand for cells */
586           if (group->expand_cells > 0)
587             {
588               extra_size  = avail_size / group->expand_cells;
589               extra_extra = avail_size % group->expand_cells;
590             }
591           else
592             extra_size = extra_extra = 0;
593
594           /* Create the allocated cells */
595           for (j = 0, cell_list = group->cells; cell_list; j++, cell_list = cell_list->next)
596             {
597               CellInfo      *info = cell_list->data;
598               AllocatedCell *cell;
599
600               if (info->expand)
601                 {
602                   sizes[j].minimum_size += extra_size;
603                   if (extra_extra)
604                     {
605                       sizes[j].minimum_size++;
606                       extra_extra--;
607                     }
608                 }
609               
610               cell = allocated_cell_new (info->renderer, position, sizes[j].minimum_size);
611
612               allocated_cells = g_slist_prepend (allocated_cells, cell);
613               
614               position += sizes[j].minimum_size;
615               position += priv->spacing;
616             }
617
618           g_free (sizes);
619         }
620     }
621
622   return g_slist_reverse (allocated_cells);
623 }
624
625 /*************************************************************
626  *                      GObjectClass                         *
627  *************************************************************/
628 static void
629 gtk_cell_area_box_finalize (GObject *object)
630 {
631   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (object);
632   GtkCellAreaBoxPrivate *priv = box->priv;
633   GSList                *l;
634
635   for (l = priv->iters; l; l = l->next)
636     g_object_weak_unref (G_OBJECT (l->data), (GWeakNotify)iter_weak_notify, box);
637
638   g_slist_free (priv->iters);
639
640   G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->finalize (object);
641 }
642
643 static void
644 gtk_cell_area_box_dispose (GObject *object)
645 {
646   G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->dispose (object);
647 }
648
649 static void
650 gtk_cell_area_box_set_property (GObject       *object,
651                                 guint          prop_id,
652                                 const GValue  *value,
653                                 GParamSpec    *pspec)
654 {
655   GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
656
657   switch (prop_id)
658     {
659     case PROP_ORIENTATION:
660       box->priv->orientation = g_value_get_enum (value);
661
662       /* Notify that size needs to be requested again */
663       flush_iters (box);
664       break;
665     case PROP_SPACING:
666       gtk_cell_area_box_set_spacing (box, g_value_get_int (value));
667       break;
668     default:
669       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
670       break;
671     }
672 }
673
674 static void
675 gtk_cell_area_box_get_property (GObject     *object,
676                                 guint        prop_id,
677                                 GValue      *value,
678                                 GParamSpec  *pspec)
679 {
680   GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
681
682   switch (prop_id)
683     {
684     case PROP_ORIENTATION:
685       g_value_set_enum (value, box->priv->orientation);
686       break;
687     case PROP_SPACING:
688       g_value_set_int (value, gtk_cell_area_box_get_spacing (box));
689       break;
690     default:
691       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
692       break;
693     }
694 }
695
696 /*************************************************************
697  *                    GtkCellAreaClass                       *
698  *************************************************************/
699 static void      
700 gtk_cell_area_box_add (GtkCellArea        *area,
701                        GtkCellRenderer    *renderer)
702 {
703   gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area),
704                                 renderer, FALSE, TRUE);
705 }
706
707 static void
708 gtk_cell_area_box_remove (GtkCellArea        *area,
709                           GtkCellRenderer    *renderer)
710 {
711   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
712   GtkCellAreaBoxPrivate *priv = box->priv;
713   GList                 *node;
714
715   node = g_list_find_custom (priv->cells, renderer, 
716                              (GCompareFunc)cell_info_find);
717
718   if (node)
719     {
720       CellInfo *info = node->data;
721
722       cell_info_free (info);
723
724       priv->cells = g_list_delete_link (priv->cells, node);
725
726       /* Reconstruct cell groups */
727       g_list_foreach (priv->groups, (GFunc)cell_group_free, NULL);
728       g_list_free (priv->groups);
729       priv->groups = construct_cell_groups (box);
730
731       /* Reinitialize groups on iters */
732       init_iter_groups (box);
733     }
734   else
735     g_warning ("Trying to remove a cell renderer that is not present GtkCellAreaBox");
736 }
737
738 static void
739 gtk_cell_area_box_forall (GtkCellArea        *area,
740                           GtkCellCallback     callback,
741                           gpointer            callback_data)
742 {
743   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
744   GtkCellAreaBoxPrivate *priv = box->priv;
745   GList                 *list;
746
747   for (list = priv->cells; list; list = list->next)
748     {
749       CellInfo *info = list->data;
750
751       callback (info->renderer, callback_data);
752     }
753 }
754
755 static gint
756 gtk_cell_area_box_event (GtkCellArea          *area,
757                          GtkCellAreaIter      *iter,
758                          GtkWidget            *widget,
759                          GdkEvent             *event,
760                          const GdkRectangle   *cell_area,
761                          GtkCellRendererState  flags)
762 {
763
764
765   return 0;
766 }
767
768 static void
769 gtk_cell_area_box_render (GtkCellArea          *area,
770                           GtkCellAreaIter      *iter,
771                           GtkWidget            *widget,
772                           cairo_t              *cr,
773                           const GdkRectangle   *cell_area,
774                           GtkCellRendererState  flags)
775 {
776   GtkCellAreaBox        *box      = GTK_CELL_AREA_BOX (area);
777   GtkCellAreaBoxPrivate *priv     = box->priv;
778   GtkCellAreaBoxIter    *box_iter = GTK_CELL_AREA_BOX_ITER (iter);
779   GSList                *allocated_cells, *l;
780   GdkRectangle           background_area, inner_area;
781
782   background_area = *cell_area;
783
784   /* Get a list of cells with allocation sizes decided regardless
785    * of alignments and pack order etc. */
786   allocated_cells = get_allocated_cells (box, box_iter, widget);
787
788   for (l = allocated_cells; l; l = l->next)
789     {
790       AllocatedCell *cell = l->data;
791
792       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
793         {
794           background_area.x     = cell_area->x + cell->position;
795           background_area.width = cell->size;
796         }
797       else
798         {
799           background_area.y      = cell_area->y + cell->position;
800           background_area.height = cell->size;
801         }
802
803       /* Remove margins from the background area to produce the cell area
804        */
805       gtk_cell_area_inner_cell_area (area, &background_area, &inner_area);
806
807       /* XXX We have to do some per-cell considerations for the 'flags'
808        * for focus handling */
809       gtk_cell_renderer_render (cell->renderer, cr, widget,
810                                 &background_area, &inner_area,
811                                 flags);
812
813     }
814
815   g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL);
816   g_slist_free (allocated_cells);
817 }
818
819 static void
820 gtk_cell_area_box_set_cell_property (GtkCellArea        *area,
821                                      GtkCellRenderer    *renderer,
822                                      guint               prop_id,
823                                      const GValue       *value,
824                                      GParamSpec         *pspec)
825 {
826   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area); 
827   GtkCellAreaBoxPrivate *priv = box->priv;
828   GList                 *node;
829   CellInfo              *info;
830   gboolean               rebuild = FALSE;
831   gboolean               flush = FALSE;
832   gboolean               val;
833   GtkPackType            pack_type;
834
835   node = g_list_find_custom (priv->cells, renderer, 
836                              (GCompareFunc)cell_info_find);
837   if (!node)
838     return;
839
840   info = node->data;
841
842   switch (prop_id)
843     {
844     case CELL_PROP_EXPAND:
845       val = g_value_get_boolean (value);
846
847       if (info->expand != val)
848         {
849           info->expand = val;
850           flush        = TRUE;
851         }
852       break;
853
854     case CELL_PROP_ALIGN:
855       val = g_value_get_boolean (value);
856
857       if (info->align != val)
858         {
859           info->align = val;
860           flush       = TRUE;
861         }
862       break;
863
864     case CELL_PROP_PACK_TYPE:
865       pack_type = g_value_get_enum (value);
866
867       if (info->pack != pack_type)
868         {
869           info->pack = pack_type;
870           rebuild    = TRUE;
871         }
872       break;
873     default:
874       GTK_CELL_AREA_WARN_INVALID_CHILD_PROPERTY_ID (area, prop_id, pspec);
875       break;
876     }
877
878   /* Groups need to be rebuilt */
879   if (rebuild)
880     {
881       /* Reconstruct cell groups */
882       g_list_foreach (priv->groups, (GFunc)cell_group_free, NULL);
883       g_list_free (priv->groups);
884       priv->groups = construct_cell_groups (box);
885       
886       /* Reinitialize groups on iters */
887       init_iter_groups (box);
888     }
889   else if (flush)
890     {
891       flush_iters (box);
892     }
893 }
894
895 static void
896 gtk_cell_area_box_get_cell_property (GtkCellArea        *area,
897                                      GtkCellRenderer    *renderer,
898                                      guint               prop_id,
899                                      GValue             *value,
900                                      GParamSpec         *pspec)
901 {
902   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area); 
903   GtkCellAreaBoxPrivate *priv = box->priv;
904   GList                 *node;
905   CellInfo              *info;
906
907   node = g_list_find_custom (priv->cells, renderer, 
908                              (GCompareFunc)cell_info_find);
909   if (!node)
910     return;
911
912   info = node->data;
913
914   switch (prop_id)
915     {
916     case CELL_PROP_EXPAND:
917       g_value_set_boolean (value, info->expand);
918       break;
919
920     case CELL_PROP_ALIGN:
921       g_value_set_boolean (value, info->align);
922       break;
923
924     case CELL_PROP_PACK_TYPE:
925       g_value_set_enum (value, info->pack);
926       break;
927     default:
928       GTK_CELL_AREA_WARN_INVALID_CHILD_PROPERTY_ID (area, prop_id, pspec);
929       break;
930     }
931 }
932
933
934 static GtkCellAreaIter *
935 gtk_cell_area_box_create_iter (GtkCellArea *area)
936 {
937   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
938   GtkCellAreaBoxPrivate *priv = box->priv;
939   GtkCellAreaIter       *iter =
940     (GtkCellAreaIter *)g_object_new (GTK_TYPE_CELL_AREA_BOX_ITER, NULL);
941
942   priv->iters = g_slist_prepend (priv->iters, iter);
943
944   g_object_weak_ref (G_OBJECT (iter), (GWeakNotify)iter_weak_notify, box);
945
946   /* Tell the new group about our cell layout */
947   init_iter_group (box, GTK_CELL_AREA_BOX_ITER (iter));
948
949   return iter;
950 }
951
952 static GtkSizeRequestMode 
953 gtk_cell_area_box_get_request_mode (GtkCellArea *area)
954 {
955   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
956   GtkCellAreaBoxPrivate *priv = box->priv;
957
958   return (priv->orientation) == GTK_ORIENTATION_HORIZONTAL ?
959     GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH :
960     GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
961 }
962
963 static void
964 compute_size (GtkCellAreaBox     *box,
965               GtkOrientation      orientation,
966               GtkCellAreaBoxIter *iter,
967               GtkWidget          *widget,
968               gint                for_size,
969               gint               *minimum_size,
970               gint               *natural_size)
971 {
972   GtkCellAreaBoxPrivate *priv = box->priv;
973   GtkCellArea           *area = GTK_CELL_AREA (box);
974   CellGroup             *group;
975   CellInfo              *info;
976   GList                 *cell_list, *group_list;
977   gint                   min_size = 0;
978   gint                   nat_size = 0;
979   
980   for (group_list = priv->groups; group_list; group_list = group_list->next)
981     {
982       gint group_min_size = 0;
983       gint group_nat_size = 0;
984
985       group = group_list->data;
986
987       for (cell_list = group->cells; cell_list; cell_list = cell_list->next)
988         {
989           gint renderer_min_size, renderer_nat_size;
990           
991           info = cell_list->data;
992           
993           gtk_cell_area_request_renderer (area, info->renderer, orientation, widget, for_size, 
994                                           &renderer_min_size, &renderer_nat_size);
995
996           if (orientation == priv->orientation)
997             {
998               if (min_size > 0)
999                 {
1000                   min_size += priv->spacing;
1001                   nat_size += priv->spacing;
1002                 }
1003               
1004               if (group_min_size > 0)
1005                 {
1006                   group_min_size += priv->spacing;
1007                   group_nat_size += priv->spacing;
1008                 }
1009               
1010               min_size       += renderer_min_size;
1011               nat_size       += renderer_nat_size;
1012               group_min_size += renderer_min_size;
1013               group_nat_size += renderer_nat_size;
1014             }
1015           else
1016             {
1017               min_size       = MAX (min_size, renderer_min_size);
1018               nat_size       = MAX (nat_size, renderer_nat_size);
1019               group_min_size = MAX (group_min_size, renderer_min_size);
1020               group_nat_size = MAX (group_nat_size, renderer_nat_size);
1021             }
1022         }
1023
1024       if (orientation == GTK_ORIENTATION_HORIZONTAL)
1025         {
1026           if (for_size < 0)
1027             gtk_cell_area_box_iter_push_group_width (iter, group->id, group_min_size, group_nat_size);
1028           else
1029             gtk_cell_area_box_iter_push_group_width_for_height (iter, group->id, for_size,
1030                                                                 group_min_size, group_nat_size);
1031         }
1032       else
1033         {
1034           if (for_size < 0)
1035             gtk_cell_area_box_iter_push_group_height (iter, group->id, group_min_size, group_nat_size);
1036           else
1037             gtk_cell_area_box_iter_push_group_height_for_width (iter, group->id, for_size,
1038                                                                 group_min_size, group_nat_size);
1039         }
1040     }
1041
1042   *minimum_size = min_size;
1043   *natural_size = nat_size;
1044 }
1045
1046 GtkRequestedSize *
1047 get_group_sizes (GtkCellArea    *area,
1048                  CellGroup      *group,
1049                  GtkOrientation  orientation,
1050                  GtkWidget      *widget,
1051                  gint           *n_sizes)
1052 {
1053   GtkRequestedSize *sizes;
1054   GList            *l;
1055   gint              i;
1056
1057   *n_sizes = g_list_length (group->cells);
1058   sizes    = g_new (GtkRequestedSize, *n_sizes);
1059
1060   for (l = group->cells, i = 0; l; l = l->next, i++)
1061     {
1062       CellInfo *info = l->data;
1063
1064       sizes[i].data = info;
1065       
1066       gtk_cell_area_request_renderer (area, info->renderer,
1067                                       orientation, widget, -1,
1068                                       &sizes[i].minimum_size,
1069                                       &sizes[i].natural_size);
1070     }
1071
1072   return sizes;
1073 }
1074
1075 static void
1076 compute_group_size_for_opposing_orientation (GtkCellAreaBox     *box,
1077                                              CellGroup          *group,
1078                                              GtkWidget          *widget, 
1079                                              gint                for_size,
1080                                              gint               *minimum_size, 
1081                                              gint               *natural_size)
1082 {
1083   GtkCellAreaBoxPrivate *priv = box->priv;
1084   GtkCellArea           *area = GTK_CELL_AREA (box);
1085
1086   /* Exception for single cell groups */
1087   if (group->n_cells == 1)
1088     {
1089       CellInfo *info = group->cells->data;
1090
1091       gtk_cell_area_request_renderer (area, info->renderer,
1092                                       OPPOSITE_ORIENTATION (priv->orientation),
1093                                       widget, for_size, minimum_size, natural_size);
1094     }
1095   else
1096     {
1097       GtkRequestedSize *orientation_sizes;
1098       CellInfo         *info;
1099       gint              n_sizes, i;
1100       gint              avail_size     = for_size;
1101       gint              extra_size, extra_extra;
1102       gint              min_size = 0, nat_size = 0;
1103
1104       orientation_sizes = get_group_sizes (area, group, priv->orientation, widget, &n_sizes);
1105
1106       /* First naturally allocate the cells in the group into the for_size */
1107       avail_size -= (n_sizes - 1) * priv->spacing;
1108       for (i = 0; i < n_sizes; i++)
1109         avail_size -= orientation_sizes[i].minimum_size;
1110
1111       avail_size = gtk_distribute_natural_allocation (avail_size, n_sizes, orientation_sizes);
1112
1113       /* Calculate/distribute expand for cells */
1114       if (group->expand_cells > 0)
1115         {
1116           extra_size  = avail_size / group->expand_cells;
1117           extra_extra = avail_size % group->expand_cells;
1118         }
1119       else
1120         extra_size = extra_extra = 0;
1121
1122       for (i = 0; i < n_sizes; i++)
1123         {
1124           gint cell_min, cell_nat;
1125
1126           info = orientation_sizes[i].data;
1127
1128           if (info->expand)
1129             {
1130               orientation_sizes[i].minimum_size += extra_size;
1131               if (extra_extra)
1132                 {
1133                   orientation_sizes[i].minimum_size++;
1134                   extra_extra--;
1135                 }
1136             }
1137
1138           gtk_cell_area_request_renderer (area, info->renderer,
1139                                           OPPOSITE_ORIENTATION (priv->orientation),
1140                                           widget, 
1141                                           orientation_sizes[i].minimum_size,
1142                                           &cell_min, &cell_nat);
1143
1144           min_size = MAX (min_size, cell_min);
1145           nat_size = MAX (nat_size, cell_nat);
1146         }
1147
1148       *minimum_size = min_size;
1149       *natural_size = nat_size;
1150
1151       g_free (orientation_sizes);
1152     }
1153 }
1154
1155 static void
1156 compute_size_for_opposing_orientation (GtkCellAreaBox     *box, 
1157                                        GtkCellAreaBoxIter *iter, 
1158                                        GtkWidget          *widget, 
1159                                        gint                for_size,
1160                                        gint               *minimum_size, 
1161                                        gint               *natural_size)
1162 {
1163   GtkCellAreaBoxPrivate *priv = box->priv;
1164   CellGroup             *group;
1165   GList                 *group_list;
1166   GtkRequestedSize      *orientation_sizes;
1167   gint                   n_groups, n_expand_groups, i;
1168   gint                   avail_size = for_size;
1169   gint                   extra_size, extra_extra;
1170   gint                   min_size = 0, nat_size = 0;
1171
1172   n_expand_groups = count_expand_groups (box);
1173
1174   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1175     orientation_sizes = gtk_cell_area_box_iter_get_widths (iter, &n_groups);
1176   else
1177     orientation_sizes = gtk_cell_area_box_iter_get_heights (iter, &n_groups);
1178
1179   /* First start by naturally allocating space among groups of cells */
1180   avail_size -= (n_groups - 1) * priv->spacing;
1181   for (i = 0; i < n_groups; i++)
1182     avail_size -= orientation_sizes[i].minimum_size;
1183
1184   avail_size = gtk_distribute_natural_allocation (avail_size, n_groups, orientation_sizes);
1185
1186   /* Calculate/distribute expand for groups */
1187   if (n_expand_groups > 0)
1188     {
1189       extra_size  = avail_size / n_expand_groups;
1190       extra_extra = avail_size % n_expand_groups;
1191     }
1192   else
1193     extra_size = extra_extra = 0;
1194
1195   /* Now we need to naturally allocate sizes for cells in each group
1196    * and push the height-for-width for each group accordingly while accumulating
1197    * the overall height-for-width for this row.
1198    */
1199   for (group_list = priv->groups; group_list; group_list = group_list->next)
1200     {
1201       gint group_min, group_nat;
1202
1203       group = group_list->data;
1204
1205       if (group->expand_cells > 0)
1206         {
1207           orientation_sizes[group->id].minimum_size += extra_size;
1208           if (extra_extra)
1209             {
1210               orientation_sizes[group->id].minimum_size++;
1211               extra_extra--;
1212             }
1213         }
1214
1215       /* Now we have the allocation for the group, request it's height-for-width */
1216       compute_group_size_for_opposing_orientation (box, group, widget,
1217                                                    orientation_sizes[group->id].minimum_size,
1218                                                    &group_min, &group_nat);
1219
1220       min_size = MAX (min_size, group_min);
1221       nat_size = MAX (nat_size, group_nat);
1222
1223       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1224         {
1225           gtk_cell_area_box_iter_push_group_height_for_width (iter, group->id, for_size,
1226                                                               group_min, group_nat);
1227         }
1228       else
1229         {
1230           gtk_cell_area_box_iter_push_group_width_for_height (iter, group->id, for_size,
1231                                                               group_min, group_nat);
1232         }
1233     }
1234
1235   *minimum_size = min_size;
1236   *natural_size = nat_size;
1237
1238   g_free (orientation_sizes);
1239 }
1240
1241
1242
1243 static void
1244 gtk_cell_area_box_get_preferred_width (GtkCellArea        *area,
1245                                        GtkCellAreaIter    *iter,
1246                                        GtkWidget          *widget,
1247                                        gint               *minimum_width,
1248                                        gint               *natural_width)
1249 {
1250   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
1251   GtkCellAreaBoxIter    *box_iter;
1252   gint                   min_width, nat_width;
1253
1254   g_return_if_fail (GTK_IS_CELL_AREA_BOX_ITER (iter));
1255
1256   box_iter = GTK_CELL_AREA_BOX_ITER (iter);
1257
1258   /* Compute the size of all renderers for current row data, 
1259    * bumping cell alignments in the iter along the way */
1260   compute_size (box, GTK_ORIENTATION_HORIZONTAL,
1261                 box_iter, widget, -1, &min_width, &nat_width);
1262
1263   if (minimum_width)
1264     *minimum_width = min_width;
1265
1266   if (natural_width)
1267     *natural_width = nat_width;
1268 }
1269
1270 static void
1271 gtk_cell_area_box_get_preferred_height (GtkCellArea        *area,
1272                                         GtkCellAreaIter    *iter,
1273                                         GtkWidget          *widget,
1274                                         gint               *minimum_height,
1275                                         gint               *natural_height)
1276 {
1277   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
1278   GtkCellAreaBoxIter    *box_iter;
1279   gint                   min_height, nat_height;
1280
1281   g_return_if_fail (GTK_IS_CELL_AREA_BOX_ITER (iter));
1282
1283   box_iter = GTK_CELL_AREA_BOX_ITER (iter);
1284
1285   /* Compute the size of all renderers for current row data, 
1286    * bumping cell alignments in the iter along the way */
1287   compute_size (box, GTK_ORIENTATION_VERTICAL,
1288                 box_iter, widget, -1, &min_height, &nat_height);
1289
1290   if (minimum_height)
1291     *minimum_height = min_height;
1292
1293   if (natural_height)
1294     *natural_height = nat_height;
1295 }
1296
1297 static void
1298 gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea        *area,
1299                                                   GtkCellAreaIter    *iter,
1300                                                   GtkWidget          *widget,
1301                                                   gint                width,
1302                                                   gint               *minimum_height,
1303                                                   gint               *natural_height)
1304 {
1305   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
1306   GtkCellAreaBoxIter    *box_iter;
1307   GtkCellAreaBoxPrivate *priv;
1308   gint                   min_height, nat_height;
1309
1310   g_return_if_fail (GTK_IS_CELL_AREA_BOX_ITER (iter));
1311
1312   box_iter = GTK_CELL_AREA_BOX_ITER (iter);
1313   priv     = box->priv;
1314
1315   if (priv->orientation == GTK_ORIENTATION_VERTICAL)
1316     {
1317       /* Add up vertical requests of height for width and push the overall 
1318        * cached sizes for alignments */
1319       compute_size (box, priv->orientation, box_iter, widget, width, &min_height, &nat_height);
1320     }
1321   else
1322     {
1323       /* Juice: virtually allocate cells into the for_width using the 
1324        * alignments and then return the overall height for that width, and cache it */
1325       compute_size_for_opposing_orientation (box, box_iter, widget, width, &min_height, &nat_height);
1326     }
1327
1328   if (minimum_height)
1329     *minimum_height = min_height;
1330
1331   if (natural_height)
1332     *natural_height = nat_height;
1333 }
1334
1335 static void
1336 gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea        *area,
1337                                                   GtkCellAreaIter    *iter,
1338                                                   GtkWidget          *widget,
1339                                                   gint                height,
1340                                                   gint               *minimum_width,
1341                                                   gint               *natural_width)
1342 {
1343   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
1344   GtkCellAreaBoxIter    *box_iter;
1345   GtkCellAreaBoxPrivate *priv;
1346   gint                   min_width, nat_width;
1347
1348   g_return_if_fail (GTK_IS_CELL_AREA_BOX_ITER (iter));
1349
1350   box_iter = GTK_CELL_AREA_BOX_ITER (iter);
1351   priv     = box->priv;
1352
1353   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1354     {
1355       /* Add up horizontal requests of width for height and push the overall 
1356        * cached sizes for alignments */
1357       compute_size (box, priv->orientation, box_iter, widget, height, &min_width, &nat_width);
1358     }
1359   else
1360     {
1361       /* Juice: horizontally allocate cells into the for_height using the 
1362        * alignments and then return the overall width for that height, and cache it */
1363       compute_size_for_opposing_orientation (box, box_iter, widget, height, &min_width, &nat_width);
1364     }
1365
1366   if (minimum_width)
1367     *minimum_width = min_width;
1368
1369   if (natural_width)
1370     *natural_width = nat_width;
1371 }
1372
1373
1374 /*************************************************************
1375  *                    GtkCellLayoutIface                     *
1376  *************************************************************/
1377 static void
1378 gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface)
1379 {
1380   iface->pack_start = gtk_cell_area_box_layout_pack_start;
1381   iface->pack_end   = gtk_cell_area_box_layout_pack_end;
1382   iface->reorder    = gtk_cell_area_box_layout_reorder;
1383 }
1384
1385 static void
1386 gtk_cell_area_box_layout_pack_start (GtkCellLayout      *cell_layout,
1387                                      GtkCellRenderer    *renderer,
1388                                      gboolean            expand)
1389 {
1390   gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, TRUE);
1391 }
1392
1393 static void
1394 gtk_cell_area_box_layout_pack_end (GtkCellLayout      *cell_layout,
1395                                    GtkCellRenderer    *renderer,
1396                                    gboolean            expand)
1397 {
1398   gtk_cell_area_box_pack_end (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, TRUE);
1399 }
1400
1401 static void
1402 gtk_cell_area_box_layout_reorder (GtkCellLayout      *cell_layout,
1403                                   GtkCellRenderer    *renderer,
1404                                   gint                position)
1405 {
1406   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (cell_layout);
1407   GtkCellAreaBoxPrivate *priv = box->priv;
1408   GList                 *node;
1409   CellInfo              *info;
1410   
1411   node = g_list_find_custom (priv->cells, renderer, 
1412                              (GCompareFunc)cell_info_find);
1413
1414   if (node)
1415     {
1416       info = node->data;
1417
1418       priv->cells = g_list_delete_link (priv->cells, node);
1419       priv->cells = g_list_insert (priv->cells, info, position);
1420
1421       /* Reconstruct cell groups */
1422       g_list_foreach (priv->groups, (GFunc)cell_group_free, NULL);
1423       g_list_free (priv->groups);
1424       priv->groups = construct_cell_groups (box);
1425       
1426       /* Reinitialize groups on iters */
1427       init_iter_groups (box);
1428     }
1429 }
1430
1431 /*************************************************************
1432  *                            API                            *
1433  *************************************************************/
1434 GtkCellArea *
1435 gtk_cell_area_box_new (void)
1436 {
1437   return (GtkCellArea *)g_object_new (GTK_TYPE_CELL_AREA_BOX, NULL);
1438 }
1439
1440 void
1441 gtk_cell_area_box_pack_start  (GtkCellAreaBox  *box,
1442                                GtkCellRenderer *renderer,
1443                                gboolean         expand,
1444                                gboolean         align)
1445 {
1446   GtkCellAreaBoxPrivate *priv;
1447   CellInfo              *info;
1448
1449   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
1450   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1451
1452   priv = box->priv;
1453
1454   if (g_list_find_custom (priv->cells, renderer, 
1455                           (GCompareFunc)cell_info_find))
1456     {
1457       g_warning ("Refusing to add the same cell renderer to a GtkCellAreaBox twice");
1458       return;
1459     }
1460
1461   info = cell_info_new (renderer, GTK_PACK_START, expand, align);
1462
1463   priv->cells = g_list_append (priv->cells, info);
1464
1465   /* Reconstruct cell groups */
1466   g_list_foreach (priv->groups, (GFunc)cell_group_free, NULL);
1467   g_list_free (priv->groups);
1468   priv->groups = construct_cell_groups (box);
1469
1470   /* Reinitialize groups on iters */
1471   init_iter_groups (box);
1472 }
1473
1474 void
1475 gtk_cell_area_box_pack_end (GtkCellAreaBox  *box,
1476                             GtkCellRenderer *renderer,
1477                             gboolean         expand, 
1478                             gboolean         align)
1479 {
1480   GtkCellAreaBoxPrivate *priv;
1481   CellInfo              *info;
1482
1483   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
1484   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1485
1486   priv = box->priv;
1487
1488   if (g_list_find_custom (priv->cells, renderer, 
1489                           (GCompareFunc)cell_info_find))
1490     {
1491       g_warning ("Refusing to add the same cell renderer to a GtkCellArea twice");
1492       return;
1493     }
1494
1495   info = cell_info_new (renderer, GTK_PACK_END, expand, align);
1496
1497   priv->cells = g_list_append (priv->cells, info);
1498
1499   /* Reconstruct cell groups */
1500   g_list_foreach (priv->groups, (GFunc)cell_group_free, NULL);
1501   g_list_free (priv->groups);
1502   priv->groups = construct_cell_groups (box);
1503
1504   /* Reinitialize groups on iters */
1505   init_iter_groups (box);
1506 }
1507
1508 gint
1509 gtk_cell_area_box_get_spacing (GtkCellAreaBox  *box)
1510 {
1511   g_return_val_if_fail (GTK_IS_CELL_AREA_BOX (box), 0);
1512
1513   return box->priv->spacing;
1514 }
1515
1516 void
1517 gtk_cell_area_box_set_spacing (GtkCellAreaBox  *box,
1518                                gint             spacing)
1519 {
1520   GtkCellAreaBoxPrivate *priv;
1521
1522   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
1523
1524   priv = box->priv;
1525
1526   if (priv->spacing != spacing)
1527     {
1528       priv->spacing = spacing;
1529
1530       g_object_notify (G_OBJECT (box), "spacing");
1531
1532       /* Notify that size needs to be requested again */
1533       flush_iters (box);
1534     }
1535 }