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