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