]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellareabox.c
Added most of the request code for GtkCellAreaBox
[~andy/gtk] / gtk / gtkcellareabox.c
1 /* gtkcellareabox.c
2  *
3  * Copyright (C) 2010 Openismus GmbH
4  *
5  * Authors:
6  *      Tristan Van Berkom <tristanvb@openismus.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include "config.h"
25 #include "gtkintl.h"
26 #include "gtkorientable.h"
27 #include "gtkcelllayout.h"
28 #include "gtkcellareabox.h"
29 #include "gtkcellareaboxiter.h"
30 #include "gtkprivate.h"
31
32
33 /* GObjectClass */
34 static void      gtk_cell_area_box_finalize                       (GObject            *object);
35 static void      gtk_cell_area_box_dispose                        (GObject            *object);
36 static void      gtk_cell_area_box_set_property                   (GObject            *object,
37                                                                    guint               prop_id,
38                                                                    const GValue       *value,
39                                                                    GParamSpec         *pspec);
40 static void      gtk_cell_area_box_get_property                   (GObject            *object,
41                                                                    guint               prop_id,
42                                                                    GValue             *value,
43                                                                    GParamSpec         *pspec);
44
45 /* GtkCellAreaClass */
46 static void      gtk_cell_area_box_add                            (GtkCellArea        *area,
47                                                                    GtkCellRenderer    *renderer);
48 static void      gtk_cell_area_box_remove                         (GtkCellArea        *area,
49                                                                    GtkCellRenderer    *renderer);
50 static void      gtk_cell_area_box_forall                         (GtkCellArea        *area,
51                                                                    GtkCellCallback     callback,
52                                                                    gpointer            callback_data);
53 static gint      gtk_cell_area_box_event                          (GtkCellArea        *area,
54                                                                    GtkWidget          *widget,
55                                                                    GdkEvent           *event,
56                                                                    const GdkRectangle *cell_area);
57 static void      gtk_cell_area_box_render                         (GtkCellArea        *area,
58                                                                    cairo_t            *cr,
59                                                                    GtkWidget          *widget,
60                                                                    const GdkRectangle *cell_area);
61
62 static GtkCellAreaIter    *gtk_cell_area_box_create_iter          (GtkCellArea        *area);
63 static GtkSizeRequestMode  gtk_cell_area_box_get_request_mode     (GtkCellArea        *area);
64 static void      gtk_cell_area_box_get_preferred_width            (GtkCellArea        *area,
65                                                                    GtkCellAreaIter    *iter,
66                                                                    GtkWidget          *widget,
67                                                                    gint               *minimum_width,
68                                                                    gint               *natural_width);
69 static void      gtk_cell_area_box_get_preferred_height           (GtkCellArea        *area,
70                                                                    GtkCellAreaIter    *iter,
71                                                                    GtkWidget          *widget,
72                                                                    gint               *minimum_height,
73                                                                    gint               *natural_height);
74 static void      gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea        *area,
75                                                                    GtkCellAreaIter    *iter,
76                                                                    GtkWidget          *widget,
77                                                                    gint                width,
78                                                                    gint               *minimum_height,
79                                                                    gint               *natural_height);
80 static void      gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea        *area,
81                                                                    GtkCellAreaIter    *iter,
82                                                                    GtkWidget          *widget,
83                                                                    gint                height,
84                                                                    gint               *minimum_width,
85                                                                    gint               *natural_width);
86
87 /* GtkCellLayoutIface */
88 static void      gtk_cell_area_box_cell_layout_init               (GtkCellLayoutIface *iface);
89 static void      gtk_cell_area_box_layout_pack_start              (GtkCellLayout      *cell_layout,
90                                                                    GtkCellRenderer    *renderer,
91                                                                    gboolean            expand);
92 static void      gtk_cell_area_box_layout_pack_end                (GtkCellLayout      *cell_layout,
93                                                                    GtkCellRenderer    *renderer,
94                                                                    gboolean            expand);
95 static void      gtk_cell_area_box_layout_reorder                 (GtkCellLayout      *cell_layout,
96                                                                    GtkCellRenderer    *renderer,
97                                                                    gint                position);
98
99
100 /* CellInfo metadata handling */
101 typedef struct {
102   GtkCellRenderer *renderer;
103
104   guint            expand : 1;
105   guint            pack   : 1;
106 } CellInfo;
107
108 static CellInfo  *cell_info_new  (GtkCellRenderer *renderer, 
109                                   gboolean         expand,
110                                   GtkPackType      pack);
111 static void       cell_info_free (CellInfo        *info);
112 static gint       cell_info_find (CellInfo        *info,
113                                   GtkCellRenderer *renderer);
114
115
116 struct _GtkCellAreaBoxPrivate
117 {
118   GtkOrientation orientation;
119
120   GList         *cells;
121
122   gint           spacing;
123
124   guint          align_cells : 1;
125 };
126
127 enum {
128   PROP_0,
129   PROP_ORIENTATION,
130   PROP_SPACING,
131   PROP_ALIGN_CELLS
132 };
133
134 G_DEFINE_TYPE_WITH_CODE (GtkCellAreaBox, gtk_cell_area_box, GTK_TYPE_CELL_AREA,
135                          G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
136                                                 gtk_cell_area_box_cell_layout_init)
137                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL));
138
139 static void
140 gtk_cell_area_box_init (GtkCellAreaBox *box)
141 {
142   GtkCellAreaBoxPrivate *priv;
143
144   box->priv = G_TYPE_INSTANCE_GET_PRIVATE (box,
145                                            GTK_TYPE_CELL_AREA_BOX,
146                                            GtkCellAreaBoxPrivate);
147   priv = box->priv;
148
149   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
150   priv->cells       = NULL;
151   priv->spacing     = 0;
152   priv->align_cells = TRUE;
153 }
154
155 static void 
156 gtk_cell_area_box_class_init (GtkCellAreaBoxClass *class)
157 {
158   GObjectClass     *object_class = G_OBJECT_CLASS (class);
159   GtkCellAreaClass *area_class   = GTK_CELL_AREA_CLASS (class);
160
161   /* GObjectClass */
162   object_class->finalize     = gtk_cell_area_box_finalize;
163   object_class->dispose      = gtk_cell_area_box_dispose;
164   object_class->set_property = gtk_cell_area_box_set_property;
165   object_class->get_property = gtk_cell_area_box_get_property;
166
167   /* GtkCellAreaClass */
168   area_class->add                            = gtk_cell_area_box_add;
169   area_class->remove                         = gtk_cell_area_box_remove;
170   area_class->forall                         = gtk_cell_area_box_forall;
171   area_class->event                          = gtk_cell_area_box_event;
172   area_class->render                         = gtk_cell_area_box_render;
173   
174   area_class->create_iter                    = gtk_cell_area_box_create_iter;
175   area_class->get_request_mode               = gtk_cell_area_box_get_request_mode;
176   area_class->get_preferred_width            = gtk_cell_area_box_get_preferred_width;
177   area_class->get_preferred_height           = gtk_cell_area_box_get_preferred_height;
178   area_class->get_preferred_height_for_width = gtk_cell_area_box_get_preferred_height_for_width;
179   area_class->get_preferred_width_for_height = gtk_cell_area_box_get_preferred_width_for_height;
180
181   g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
182
183   g_object_class_install_property (object_class,
184                                    PROP_SPACING,
185                                    g_param_spec_int ("spacing",
186                                                      P_("Spacing"),
187                                                      P_("Space which is inserted between cells"),
188                                                      0,
189                                                      G_MAXINT,
190                                                      0,
191                                                      GTK_PARAM_READWRITE));
192
193   g_object_class_install_property (object_class,
194                                    PROP_ALIGN_CELLS,
195                                    g_param_spec_boolean ("align-cells",
196                                                          P_("Align Cells"),
197                                                          P_("Whether cells should be aligned with those "
198                                                             "rendered in adjacent rows"),
199                                                          TRUE,
200                                                          GTK_PARAM_READWRITE));
201
202   g_type_class_add_private (object_class, sizeof (GtkCellAreaBoxPrivate));
203 }
204
205
206 /*************************************************************
207  *                    CellInfo Basics                        *
208  *************************************************************/
209 static CellInfo *
210 cell_info_new  (GtkCellRenderer *renderer, 
211                 gboolean         expand,
212                 GtkPackType      pack)
213 {
214   CellInfo *info = g_slice_new (CellInfo);
215   
216   info->renderer = g_object_ref_sink (renderer);
217   info->expand   = expand;
218   info->pack     = pack;
219
220   return info;
221 }
222
223 static void
224 cell_info_free (CellInfo *info)
225 {
226   g_object_unref (info->renderer);
227
228   g_slice_free (CellInfo, info);
229 }
230
231 static gint
232 cell_info_find (CellInfo        *info,
233                 GtkCellRenderer *renderer)
234 {
235   return (info->renderer == renderer) ? 0 : -1;
236 }
237
238 /*************************************************************
239  *                      GObjectClass                         *
240  *************************************************************/
241 static void
242 gtk_cell_area_box_finalize (GObject *object)
243 {
244   G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->finalize (object);
245 }
246
247 static void
248 gtk_cell_area_box_dispose (GObject *object)
249 {
250   G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->dispose (object);
251 }
252
253 static void
254 gtk_cell_area_box_set_property (GObject       *object,
255                                 guint          prop_id,
256                                 const GValue  *value,
257                                 GParamSpec    *pspec)
258 {
259
260 }
261
262 static void
263 gtk_cell_area_box_get_property (GObject     *object,
264                                 guint        prop_id,
265                                 GValue      *value,
266                                 GParamSpec  *pspec)
267 {
268
269 }
270
271 /*************************************************************
272  *                    GtkCellAreaClass                       *
273  *************************************************************/
274 static void      
275 gtk_cell_area_box_add (GtkCellArea        *area,
276                        GtkCellRenderer    *renderer)
277 {
278   gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area),
279                                 renderer, FALSE);
280 }
281
282 static void
283 gtk_cell_area_box_remove (GtkCellArea        *area,
284                           GtkCellRenderer    *renderer)
285 {
286   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
287   GtkCellAreaBoxPrivate *priv = box->priv;
288   GList                 *node;
289
290   node = g_list_find_custom (priv->cells, renderer, 
291                              (GCompareFunc)cell_info_find);
292
293   if (node)
294     {
295       CellInfo *info = node->data;
296
297       cell_info_free (info);
298
299       priv->cells = g_list_delete_link (priv->cells, node);
300     }
301   else
302     g_warning ("Trying to remove a cell renderer that is not present GtkCellAreaBox");
303 }
304
305 static void
306 gtk_cell_area_box_forall (GtkCellArea        *area,
307                           GtkCellCallback     callback,
308                           gpointer            callback_data)
309 {
310   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
311   GtkCellAreaBoxPrivate *priv = box->priv;
312   GList                 *list;
313
314   for (list = priv->cells; list; list = list->next)
315     {
316       CellInfo *info = list->data;
317
318       callback (info->renderer, callback_data);
319     }
320 }
321
322 static gint
323 gtk_cell_area_box_event (GtkCellArea        *area,
324                          GtkWidget          *widget,
325                          GdkEvent           *event,
326                          const GdkRectangle *cell_area)
327 {
328
329
330   return 0;
331 }
332
333 static void
334 gtk_cell_area_box_render (GtkCellArea        *area,
335                           cairo_t            *cr,
336                           GtkWidget          *widget,
337                           const GdkRectangle *cell_area)
338 {
339
340 }
341
342 static GtkCellAreaIter *
343 gtk_cell_area_box_create_iter (GtkCellArea *area)
344 {
345   return (GtkCellAreaIter *)g_object_new (GTK_TYPE_CELL_AREA_BOX_ITER, NULL);
346 }
347
348 static GtkSizeRequestMode 
349 gtk_cell_area_box_get_request_mode (GtkCellArea *area)
350 {
351   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
352   GtkCellAreaBoxPrivate *priv = box->priv;
353
354   return (priv->orientation) == GTK_ORIENTATION_HORIZONTAL ?
355     GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH :
356     GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
357 }
358
359 static void
360 get_renderer_size (GtkCellRenderer    *renderer,
361                    GtkOrientation      orientation,
362                    GtkWidget          *widget,
363                    gint                for_size,
364                    gint               *minimum_size,
365                    gint               *natural_size)
366 {
367   if (orientation == GTK_ORIENTATION_HORIZONTAL)
368     {
369       if (for_size < 0)
370         gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size);
371       else
372         gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size, 
373                                                           minimum_size, natural_size);
374     }
375   else /* GTK_ORIENTATION_VERTICAL */
376     {
377       if (for_size < 0)
378         gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size);
379       else
380         gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size, 
381                                                           minimum_size, natural_size);
382     }
383 }
384
385 static void
386 compute_size (GtkCellAreaBox     *box,
387               GtkOrientation      orientation,
388               GtkCellAreaBoxIter *iter,
389               GtkWidget          *widget,
390               gint               *minimum_size,
391               gint               *natural_size)
392 {
393   GtkCellAreaBoxPrivate *priv = box->priv;
394   CellInfo              *info;
395   GList                 *l;
396   gint                   min_size = 0;
397   gint                   nat_size = 0;
398   gboolean               first_cell = TRUE;
399   
400   for (l = priv->cells; l; l = l->next)
401     {
402       gint renderer_min_size, renderer_nat_size;
403
404       info = l->data;
405
406       get_renderer_size (info->renderer, orientation, widget, -1, 
407                          &renderer_min_size, &renderer_nat_size);
408
409       /* If we're aligning the cells we need to cache the max results
410        * for all requests performed with the same iter.
411        */
412       if (priv->align_cells)
413         {
414           if (orientation == GTK_ORIENTATION_HORIZONTAL)
415             gtk_cell_area_box_iter_push_cell_width (iter, info->renderer, 
416                                                     renderer_min_size, renderer_nat_size);
417           else
418             gtk_cell_area_box_iter_push_cell_height (iter, info->renderer, 
419                                                      renderer_min_size, renderer_nat_size);
420         }
421
422       if (orientation == priv->orientation)
423         {
424           min_size += renderer_min_size;
425           nat_size += renderer_nat_size;
426
427           if (!first_cell)
428             {
429               min_size += priv->spacing;
430               nat_size += priv->spacing;
431             }
432         }
433       else
434         {
435           min_size = MAX (min_size, renderer_min_size);
436           nat_size = MAX (nat_size, renderer_nat_size);
437         }
438
439       if (first_cell)
440         first_cell = FALSE;
441     }
442
443   *minimum_size = min_size;
444   *natural_size = nat_size;
445 }
446
447 static void
448 update_iter_aligned (GtkCellAreaBox     *box,
449                      GtkCellAreaBoxIter *iter,
450                      gint                for_size)
451 {
452   GtkCellAreaBoxPrivate *priv = box->priv;
453   CellInfo              *info;
454   GList                 *l;
455   gint                   min_size = 0;
456   gint                   nat_size = 0;
457   gboolean               first_cell = TRUE;
458
459   for (l = priv->cells; l; l = l->next)
460     {
461       gint aligned_min_size, aligned_nat_size;
462       
463       info = l->data;
464
465       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
466         {
467           if (for_size < 0)
468             gtk_cell_area_box_iter_get_cell_width (iter, info->renderer, 
469                                                    &aligned_min_size,
470                                                    &aligned_nat_size);
471           else
472             gtk_cell_area_box_iter_get_cell_width_for_height (iter, info->renderer, 
473                                                               for_size,
474                                                               &aligned_min_size,
475                                                               &aligned_nat_size);
476         }
477       else
478         {
479           if (for_size < 0)
480             gtk_cell_area_box_iter_get_cell_height (iter, info->renderer, 
481                                                     &aligned_min_size,
482                                                     &aligned_nat_size);
483           else
484             gtk_cell_area_box_iter_get_cell_height_for_width (iter, info->renderer, 
485                                                               for_size,
486                                                               &aligned_min_size,
487                                                               &aligned_nat_size);
488         }
489
490       min_size += aligned_min_size;
491       nat_size += aligned_nat_size;
492       
493       if (!first_cell)
494         {
495           min_size += priv->spacing;
496           nat_size += priv->spacing;
497         }
498       
499       if (first_cell)
500         first_cell = FALSE;
501     }
502
503   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
504     {
505       if (for_size < 0)
506         gtk_cell_area_iter_push_preferred_width (GTK_CELL_AREA_ITER (iter), min_size, nat_size);
507       else
508         gtk_cell_area_iter_push_preferred_width_for_height (GTK_CELL_AREA_ITER (iter), 
509                                                             for_size, min_size, nat_size);
510     }
511   else
512     {
513       if (for_size < 0)
514         gtk_cell_area_iter_push_preferred_height (GTK_CELL_AREA_ITER (iter), min_size, nat_size);
515       else
516         gtk_cell_area_iter_push_preferred_height_for_width (GTK_CELL_AREA_ITER (iter), 
517                                                             for_size, min_size, nat_size);
518     }
519 }
520
521 static void
522 gtk_cell_area_box_get_preferred_width (GtkCellArea        *area,
523                                        GtkCellAreaIter    *iter,
524                                        GtkWidget          *widget,
525                                        gint               *minimum_width,
526                                        gint               *natural_width)
527 {
528   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
529   GtkCellAreaBoxIter    *box_iter;
530   GtkCellAreaBoxPrivate *priv;
531   gint                   min_width, nat_width;
532
533   g_return_if_fail (GTK_IS_CELL_AREA_BOX_ITER (iter));
534
535   box_iter = GTK_CELL_AREA_BOX_ITER (iter);
536   priv     = box->priv;
537
538   /* Compute the size of all renderers for current row data, possibly
539    * bumping cell alignments in the iter along the way */
540   compute_size (box, GTK_ORIENTATION_HORIZONTAL,
541                 box_iter, widget, &min_width, &nat_width);
542
543   /* Update width of the iter based on aligned cell sizes if 
544    * appropriate */
545   if (priv->align_cells && priv->orientation == GTK_ORIENTATION_HORIZONTAL)
546     update_iter_aligned (box, box_iter, -1);
547   else
548     gtk_cell_area_iter_push_preferred_width (iter, min_width, nat_width);
549
550   if (minimum_width)
551     *minimum_width = min_width;
552
553   if (natural_width)
554     *natural_width = nat_width;
555 }
556
557 static void
558 gtk_cell_area_box_get_preferred_height (GtkCellArea        *area,
559                                         GtkCellAreaIter    *iter,
560                                         GtkWidget          *widget,
561                                         gint               *minimum_height,
562                                         gint               *natural_height)
563 {
564   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
565   GtkCellAreaBoxIter    *box_iter;
566   GtkCellAreaBoxPrivate *priv;
567   gint                   min_height, nat_height;
568
569   g_return_if_fail (GTK_IS_CELL_AREA_BOX_ITER (iter));
570
571   box_iter = GTK_CELL_AREA_BOX_ITER (iter);
572   priv     = box->priv;
573
574   /* Compute the size of all renderers for current row data, possibly
575    * bumping cell alignments in the iter along the way */
576   compute_size (box, GTK_ORIENTATION_VERTICAL,
577                 box_iter, widget, &min_height, &nat_height);
578
579   /* Update width of the iter based on aligned cell sizes if 
580    * appropriate */
581   if (priv->align_cells && priv->orientation == GTK_ORIENTATION_VERTICAL)
582     update_iter_aligned (box, box_iter, -1);
583   else
584     gtk_cell_area_iter_push_preferred_height (iter, min_height, nat_height);
585
586   if (minimum_height)
587     *minimum_height = min_height;
588
589   if (natural_height)
590     *natural_height = nat_height;
591 }
592
593 static void
594 compute_size_for_orientation (GtkCellAreaBox     *box, 
595                               GtkCellAreaBoxIter *iter, 
596                               GtkWidget          *widget, 
597                               gint                for_size,
598                               gint               *minimum_size, 
599                               gint               *natural_size)
600 {
601   GtkCellAreaBoxPrivate *priv = box->priv;
602   CellInfo              *info;
603   GList                 *l;
604   gint                   min_size = 0;
605   gint                   nat_size = 0;
606   gboolean               first_cell = TRUE;
607   
608   for (l = priv->cells; l; l = l->next)
609     {
610       gint renderer_min_size, renderer_nat_size;
611
612       info = l->data;
613
614       get_renderer_size (info->renderer, priv->orientation, widget, for_size, 
615                          &renderer_min_size, &renderer_nat_size);
616
617       /* If we're aligning the cells we need to cache the max results
618        * for all requests performed with the same iter.
619        */
620       if (priv->align_cells)
621         {
622           if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
623             gtk_cell_area_box_iter_push_cell_width_for_height (iter, info->renderer, for_size,
624                                                                renderer_min_size, renderer_nat_size);
625           else
626             gtk_cell_area_box_iter_push_cell_height_for_width (iter, info->renderer, for_size,
627                                                                renderer_min_size, renderer_nat_size);
628         }
629
630       min_size += renderer_min_size;
631       nat_size += renderer_nat_size;
632       
633       if (!first_cell)
634         {
635           min_size += priv->spacing;
636           nat_size += priv->spacing;
637         }
638       
639       if (first_cell)
640         first_cell = FALSE;
641     }
642
643   *minimum_size = min_size;
644   *natural_size = nat_size;
645 }
646
647
648 static void
649 gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea        *area,
650                                                   GtkCellAreaIter    *iter,
651                                                   GtkWidget          *widget,
652                                                   gint                width,
653                                                   gint               *minimum_height,
654                                                   gint               *natural_height)
655 {
656   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
657   GtkCellAreaBoxIter    *box_iter;
658   GtkCellAreaBoxPrivate *priv;
659   gint                   min_height, nat_height;
660
661   g_return_if_fail (GTK_IS_CELL_AREA_BOX_ITER (iter));
662
663   box_iter = GTK_CELL_AREA_BOX_ITER (iter);
664   priv     = box->priv;
665
666   if (priv->orientation == GTK_ORIENTATION_VERTICAL)
667     {
668       /* Add up vertical requests of height for width and possibly push the overall 
669        * cached sizes for alignments */
670       compute_size_for_orientation (box, box_iter, widget, width, &min_height, &nat_height);
671
672       /* Update the overall cached height for width based on aligned cells if appropriate */
673       if (priv->align_cells)
674         update_iter_aligned (box, box_iter, width);
675       else
676         gtk_cell_area_iter_push_preferred_height_for_width (GTK_CELL_AREA_ITER (iter),
677                                                             width, min_height, nat_height);
678     }
679   else
680     {
681       /* XXX Juice: virtually allocate cells into the for_width possibly using the 
682        * alignments and then return the overall height for that width, and cache it */
683     }
684
685   if (minimum_height)
686     *minimum_height = min_height;
687
688   if (natural_height)
689     *natural_height = nat_height;
690 }
691
692 static void
693 gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea        *area,
694                                                   GtkCellAreaIter    *iter,
695                                                   GtkWidget          *widget,
696                                                   gint                height,
697                                                   gint               *minimum_width,
698                                                   gint               *natural_width)
699 {
700   GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (area);
701   GtkCellAreaBoxIter    *box_iter;
702   GtkCellAreaBoxPrivate *priv;
703   gint                   min_width, nat_width;
704
705   g_return_if_fail (GTK_IS_CELL_AREA_BOX_ITER (iter));
706
707   box_iter = GTK_CELL_AREA_BOX_ITER (iter);
708   priv     = box->priv;
709
710   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
711     {
712       /* Add up vertical requests of height for width and possibly push the overall 
713        * cached sizes for alignments */
714       compute_size_for_orientation (box, box_iter, widget, height, &min_width, &nat_width);
715
716       /* Update the overall cached height for width based on aligned cells if appropriate */
717       if (priv->align_cells)
718         update_iter_aligned (box, box_iter, height);
719       else
720         gtk_cell_area_iter_push_preferred_height_for_width (GTK_CELL_AREA_ITER (iter),
721                                                             height, min_width, nat_width);
722     }
723   else
724     {
725       /* XXX Juice: virtually allocate cells into the for_width possibly using the 
726        * alignments and then return the overall height for that width, and cache it */
727     }
728
729   if (minimum_width)
730     *minimum_width = min_width;
731
732   if (natural_width)
733     *natural_width = nat_width;
734 }
735
736
737 /*************************************************************
738  *                    GtkCellLayoutIface                     *
739  *************************************************************/
740 static void
741 gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface)
742 {
743   iface->pack_start = gtk_cell_area_box_layout_pack_start;
744   iface->pack_end   = gtk_cell_area_box_layout_pack_end;
745   iface->reorder    = gtk_cell_area_box_layout_reorder;
746 }
747
748 static void
749 gtk_cell_area_box_layout_pack_start (GtkCellLayout      *cell_layout,
750                                      GtkCellRenderer    *renderer,
751                                      gboolean            expand)
752 {
753   gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (cell_layout), renderer, expand);
754 }
755
756 static void
757 gtk_cell_area_box_layout_pack_end (GtkCellLayout      *cell_layout,
758                                    GtkCellRenderer    *renderer,
759                                    gboolean            expand)
760 {
761   gtk_cell_area_box_pack_end (GTK_CELL_AREA_BOX (cell_layout), renderer, expand);
762 }
763
764 static void
765 gtk_cell_area_box_layout_reorder (GtkCellLayout      *cell_layout,
766                                   GtkCellRenderer    *renderer,
767                                   gint                position)
768 {
769   GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (cell_layout);
770   GtkCellAreaBoxPrivate *priv = box->priv;
771   GList                 *node;
772   CellInfo              *info;
773   
774   node = g_list_find_custom (priv->cells, renderer, 
775                              (GCompareFunc)cell_info_find);
776
777   if (node)
778     {
779       info = node->data;
780
781       priv->cells = g_list_delete_link (priv->cells, node);
782       priv->cells = g_list_insert (priv->cells, info, position);
783     }
784 }
785
786 /*************************************************************
787  *                            API                            *
788  *************************************************************/
789 GtkCellArea *
790 gtk_cell_area_box_new (void)
791 {
792   return (GtkCellArea *)g_object_new (GTK_TYPE_CELL_AREA_BOX, NULL);
793 }
794
795 void
796 gtk_cell_area_box_pack_start  (GtkCellAreaBox  *box,
797                                GtkCellRenderer *renderer,
798                                gboolean         expand)
799 {
800   GtkCellAreaBoxPrivate *priv;
801   CellInfo              *info;
802
803   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
804   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
805
806   priv = box->priv;
807
808   if (g_list_find_custom (priv->cells, renderer, 
809                           (GCompareFunc)cell_info_find))
810     {
811       g_warning ("Refusing to add the same cell renderer to a GtkCellAreaBox twice");
812       return;
813     }
814
815   info = cell_info_new (renderer, expand, GTK_PACK_START);
816
817   priv->cells = g_list_append (priv->cells, info);
818 }
819
820 void
821 gtk_cell_area_box_pack_end (GtkCellAreaBox  *box,
822                             GtkCellRenderer *renderer,
823                             gboolean         expand)
824 {
825   GtkCellAreaBoxPrivate *priv;
826   CellInfo              *info;
827
828   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
829   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
830
831   priv = box->priv;
832
833   if (g_list_find_custom (priv->cells, renderer, 
834                           (GCompareFunc)cell_info_find))
835     {
836       g_warning ("Refusing to add the same cell renderer to a GtkCellArea twice");
837       return;
838     }
839
840   info = cell_info_new (renderer, expand, GTK_PACK_END);
841
842   priv->cells = g_list_append (priv->cells, info);
843 }
844
845 gint
846 gtk_cell_area_box_get_spacing (GtkCellAreaBox  *box)
847 {
848   g_return_val_if_fail (GTK_IS_CELL_AREA_BOX (box), 0);
849
850   return box->priv->spacing;
851 }
852
853 void
854 gtk_cell_area_box_set_spacing (GtkCellAreaBox  *box,
855                                gint             spacing)
856 {
857   GtkCellAreaBoxPrivate *priv;
858
859   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
860
861   priv = box->priv;
862
863   if (priv->spacing != spacing)
864     {
865       priv->spacing = spacing;
866
867       g_object_notify (G_OBJECT (box), "spacing");
868     }
869 }
870
871 gboolean
872 gtk_cell_area_box_get_align_cells (GtkCellAreaBox  *box)
873 {
874   g_return_val_if_fail (GTK_IS_CELL_AREA_BOX (box), FALSE);
875
876   return box->priv->align_cells;
877 }
878
879 void
880 gtk_cell_area_box_set_align_cells (GtkCellAreaBox  *box,
881                                    gboolean         align)
882 {
883   GtkCellAreaBoxPrivate *priv;
884
885   g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
886
887   priv = box->priv;
888
889   if (priv->align_cells != align)
890     {
891       priv->align_cells = align;
892
893       g_object_notify (G_OBJECT (box), "align-cells");
894     }
895 }