]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellarea.c
Extended gtk_cell_area_apply_attributes() to account for expander/expanded cells
[~andy/gtk] / gtk / gtkcellarea.c
1 /* gtkcellarea.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
26 #include <stdarg.h>
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include "gtkintl.h"
31 #include "gtkcelllayout.h"
32 #include "gtkcellarea.h"
33 #include "gtkcellareaiter.h"
34 #include "gtkmarshalers.h"
35 #include "gtkprivate.h"
36
37 #include <gobject/gvaluecollector.h>
38
39
40 /* GObjectClass */
41 static void      gtk_cell_area_dispose                             (GObject            *object);
42 static void      gtk_cell_area_finalize                            (GObject            *object);
43 static void      gtk_cell_area_set_property                        (GObject            *object,
44                                                                     guint               prop_id,
45                                                                     const GValue       *value,
46                                                                     GParamSpec         *pspec);
47 static void      gtk_cell_area_get_property                        (GObject            *object,
48                                                                     guint               prop_id,
49                                                                     GValue             *value,
50                                                                     GParamSpec         *pspec);
51
52 /* GtkCellAreaClass */
53 static void      gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea        *area,
54                                                                     GtkCellAreaIter    *iter,
55                                                                     GtkWidget          *widget,
56                                                                     gint                width,
57                                                                     gint               *minimum_height,
58                                                                     gint               *natural_height);
59 static void      gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea        *area,
60                                                                     GtkCellAreaIter    *iter,
61                                                                     GtkWidget          *widget,
62                                                                     gint                height,
63                                                                     gint               *minimum_width,
64                                                                     gint               *natural_width);
65
66 /* GtkCellLayoutIface */
67 static void      gtk_cell_area_cell_layout_init              (GtkCellLayoutIface    *iface);
68 static void      gtk_cell_area_pack_default                  (GtkCellLayout         *cell_layout,
69                                                               GtkCellRenderer       *renderer,
70                                                               gboolean               expand);
71 static void      gtk_cell_area_clear                         (GtkCellLayout         *cell_layout);
72 static void      gtk_cell_area_add_attribute                 (GtkCellLayout         *cell_layout,
73                                                               GtkCellRenderer       *renderer,
74                                                               const gchar           *attribute,
75                                                               gint                   column);
76 static void      gtk_cell_area_set_cell_data_func            (GtkCellLayout         *cell_layout,
77                                                               GtkCellRenderer       *cell,
78                                                               GtkCellLayoutDataFunc  func,
79                                                               gpointer               func_data,
80                                                               GDestroyNotify         destroy);
81 static void      gtk_cell_area_clear_attributes              (GtkCellLayout         *cell_layout,
82                                                               GtkCellRenderer       *renderer);
83 static void      gtk_cell_area_reorder                       (GtkCellLayout         *cell_layout,
84                                                               GtkCellRenderer       *cell,
85                                                               gint                   position);
86 static GList    *gtk_cell_area_get_cells                     (GtkCellLayout         *cell_layout);
87
88 /* Attribute/Cell metadata */
89 typedef struct {
90   const gchar *attribute;
91   gint         column;
92 } CellAttribute;
93
94 typedef struct {
95   GSList                *attributes;
96
97   GtkCellLayoutDataFunc  func;
98   gpointer               data;
99   GDestroyNotify         destroy;
100 } CellInfo;
101
102 static CellInfo       *cell_info_new       (GtkCellLayoutDataFunc  func,
103                                             gpointer               data,
104                                             GDestroyNotify         destroy);
105 static void            cell_info_free      (CellInfo              *info);
106 static CellAttribute  *cell_attribute_new  (GtkCellRenderer       *renderer,
107                                             const gchar           *attribute,
108                                             gint                   column);
109 static void            cell_attribute_free (CellAttribute         *attribute);
110 static gint            cell_attribute_find (CellAttribute         *cell_attribute,
111                                             const gchar           *attribute);
112
113 /* Struct to pass data along while looping over 
114  * cell renderers to apply attributes
115  */
116 typedef struct {
117   GtkCellArea  *area;
118   GtkTreeModel *model;
119   GtkTreeIter  *iter;
120   gboolean      is_expander;
121   gboolean      is_expanded;
122 } AttributeData;
123
124 struct _GtkCellAreaPrivate
125 {
126   GHashTable      *cell_info;
127
128   GtkBorder        cell_border;
129
130   GtkCellRenderer *focus_cell;
131   guint            can_focus : 1;
132
133 };
134
135 enum {
136   PROP_0,
137   PROP_CELL_MARGIN_LEFT,
138   PROP_CELL_MARGIN_RIGHT,
139   PROP_CELL_MARGIN_TOP,
140   PROP_CELL_MARGIN_BOTTOM
141 };
142
143 enum {
144   SIGNAL_FOCUS_LEAVE,
145   LAST_SIGNAL
146 };
147
148 /* Keep the paramspec pool internal, no need to deliver notifications
149  * on cells. at least no percieved need for now */
150 static GParamSpecPool *cell_property_pool = NULL;
151 static guint           cell_area_signals[LAST_SIGNAL] = { 0 };
152
153 #define PARAM_SPEC_PARAM_ID(pspec)              ((pspec)->param_id)
154 #define PARAM_SPEC_SET_PARAM_ID(pspec, id)      ((pspec)->param_id = (id))
155
156
157 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
158                                   G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
159                                                          gtk_cell_area_cell_layout_init));
160
161 static void
162 gtk_cell_area_init (GtkCellArea *area)
163 {
164   GtkCellAreaPrivate *priv;
165
166   area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
167                                             GTK_TYPE_CELL_AREA,
168                                             GtkCellAreaPrivate);
169   priv = area->priv;
170
171   priv->cell_info = g_hash_table_new_full (g_direct_hash, 
172                                            g_direct_equal,
173                                            NULL, 
174                                            (GDestroyNotify)cell_info_free);
175
176   priv->cell_border.left   = 0;
177   priv->cell_border.right  = 0;
178   priv->cell_border.top    = 0;
179   priv->cell_border.bottom = 0;
180
181   priv->focus_cell         = NULL;
182 }
183
184 static void 
185 gtk_cell_area_class_init (GtkCellAreaClass *class)
186 {
187   GObjectClass *object_class = G_OBJECT_CLASS (class);
188   
189   /* GObjectClass */
190   object_class->dispose      = gtk_cell_area_dispose;
191   object_class->finalize     = gtk_cell_area_finalize;
192   object_class->get_property = gtk_cell_area_get_property;
193   object_class->set_property = gtk_cell_area_set_property;
194
195   /* general */
196   class->add     = NULL;
197   class->remove  = NULL;
198   class->forall  = NULL;
199   class->event   = NULL;
200   class->render  = NULL;
201
202   /* geometry */
203   class->create_iter                    = NULL;
204   class->get_request_mode               = NULL;
205   class->get_preferred_width            = NULL;
206   class->get_preferred_height           = NULL;
207   class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
208   class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
209
210   /* focus */
211   class->grab_focus = NULL;
212
213   /* Signals */
214   cell_area_signals[SIGNAL_FOCUS_LEAVE] =
215     g_signal_new (I_("focus-leave"),
216                   G_TYPE_FROM_CLASS (object_class),
217                   G_SIGNAL_RUN_LAST,
218                   0, /* Class offset (just a notification, no class handler) */
219                   NULL, NULL,
220                   _gtk_marshal_VOID__ENUM_STRING,
221                   G_TYPE_NONE, 2,
222                   GTK_TYPE_DIRECTION_TYPE, G_TYPE_STRING);
223
224
225   /* Properties */
226   g_object_class_install_property (object_class,
227                                    PROP_CELL_MARGIN_LEFT,
228                                    g_param_spec_int
229                                    ("cell-margin-left",
230                                     P_("Margin on Left"),
231                                     P_("Pixels of extra space on the left side of each cell"),
232                                     0,
233                                     G_MAXINT16,
234                                     0,
235                                     GTK_PARAM_READWRITE));
236
237   g_object_class_install_property (object_class,
238                                    PROP_CELL_MARGIN_RIGHT,
239                                    g_param_spec_int
240                                    ("cell-margin-right",
241                                     P_("Margin on Right"),
242                                     P_("Pixels of extra space on the right side of each cell"),
243                                     0,
244                                     G_MAXINT16,
245                                     0,
246                                     GTK_PARAM_READWRITE));
247   
248   g_object_class_install_property (object_class,
249                                    PROP_CELL_MARGIN_TOP,
250                                    g_param_spec_int 
251                                    ("cell-margin-top",
252                                     P_("Margin on Top"),
253                                     P_("Pixels of extra space on the top side of each cell"),
254                                     0,
255                                     G_MAXINT16,
256                                     0,
257                                     GTK_PARAM_READWRITE));
258
259   g_object_class_install_property (object_class,
260                                    PROP_CELL_MARGIN_BOTTOM,
261                                    g_param_spec_int
262                                    ("cell-margin-bottom",
263                                     P_("Margin on Bottom"),
264                                     P_("Pixels of extra space on the bottom side of each cell"),
265                                     0,
266                                     G_MAXINT16,
267                                     0,
268                                     GTK_PARAM_READWRITE));
269
270   /* Pool for Cell Properties */
271   if (!cell_property_pool)
272     cell_property_pool = g_param_spec_pool_new (FALSE);
273
274   g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
275 }
276
277 /*************************************************************
278  *                    CellInfo Basics                        *
279  *************************************************************/
280 static CellInfo *
281 cell_info_new (GtkCellLayoutDataFunc  func,
282                gpointer               data,
283                GDestroyNotify         destroy)
284 {
285   CellInfo *info = g_slice_new (CellInfo);
286   
287   info->attributes = NULL;
288   info->func       = func;
289   info->data       = data;
290   info->destroy    = destroy;
291
292   return info;
293 }
294
295 static void
296 cell_info_free (CellInfo *info)
297 {
298   if (info->destroy)
299     info->destroy (info->data);
300
301   g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
302   g_slist_free (info->attributes);
303
304   g_slice_free (CellInfo, info);
305 }
306
307 static CellAttribute  *
308 cell_attribute_new  (GtkCellRenderer       *renderer,
309                      const gchar           *attribute,
310                      gint                   column)
311 {
312   GParamSpec *pspec;
313
314   /* Check if the attribute really exists and point to
315    * the property string installed on the cell renderer
316    * class (dont dup the string) 
317    */
318   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);
319
320   if (pspec)
321     {
322       CellAttribute *cell_attribute = g_slice_new (CellAttribute);
323
324       cell_attribute->attribute = pspec->name;
325       cell_attribute->column    = column;
326
327       return cell_attribute;
328     }
329
330   return NULL;
331 }
332
333 static void
334 cell_attribute_free (CellAttribute *attribute)
335 {
336   g_slice_free (CellAttribute, attribute);
337 }
338
339 /* GCompareFunc for g_slist_find_custom() */
340 static gint
341 cell_attribute_find (CellAttribute *cell_attribute,
342                      const gchar   *attribute)
343 {
344   return g_strcmp0 (cell_attribute->attribute, attribute);
345 }
346
347 /*************************************************************
348  *                      GObjectClass                         *
349  *************************************************************/
350 static void
351 gtk_cell_area_finalize (GObject *object)
352 {
353   GtkCellArea        *area   = GTK_CELL_AREA (object);
354   GtkCellAreaPrivate *priv   = area->priv;
355
356   /* All cell renderers should already be removed at this point,
357    * just kill our hash table here. 
358    */
359   g_hash_table_destroy (priv->cell_info);
360
361   G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
362 }
363
364
365 static void
366 gtk_cell_area_dispose (GObject *object)
367 {
368   /* This removes every cell renderer that may be added to the GtkCellArea,
369    * subclasses should be breaking references to the GtkCellRenderers 
370    * at this point.
371    */
372   gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
373
374   /* Remove any ref to a focused cell */
375   gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL);
376
377   G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
378 }
379
380 static void
381 gtk_cell_area_set_property (GObject       *object,
382                             guint          prop_id,
383                             const GValue  *value,
384                             GParamSpec    *pspec)
385 {
386   GtkCellArea *area = GTK_CELL_AREA (object);
387
388   switch (prop_id)
389     {
390     case PROP_CELL_MARGIN_LEFT:
391       gtk_cell_area_set_cell_margin_left (area, g_value_get_int (value));
392       break;
393     case PROP_CELL_MARGIN_RIGHT:
394       gtk_cell_area_set_cell_margin_right (area, g_value_get_int (value));
395       break;
396     case PROP_CELL_MARGIN_TOP:
397       gtk_cell_area_set_cell_margin_top (area, g_value_get_int (value));
398       break;
399     case PROP_CELL_MARGIN_BOTTOM:
400       gtk_cell_area_set_cell_margin_bottom (area, g_value_get_int (value));
401       break;
402     default:
403       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
404       break;
405     }
406 }
407
408 static void
409 gtk_cell_area_get_property (GObject     *object,
410                             guint        prop_id,
411                             GValue      *value,
412                             GParamSpec  *pspec)
413 {
414   GtkCellArea        *area = GTK_CELL_AREA (object);
415   GtkCellAreaPrivate *priv = area->priv;
416
417   switch (prop_id)
418     {
419     case PROP_CELL_MARGIN_LEFT:
420       g_value_set_int (value, priv->cell_border.left);
421       break;
422     case PROP_CELL_MARGIN_RIGHT:
423       g_value_set_int (value, priv->cell_border.right);
424       break;
425     case PROP_CELL_MARGIN_TOP:
426       g_value_set_int (value, priv->cell_border.top);
427       break;
428     case PROP_CELL_MARGIN_BOTTOM:
429       g_value_set_int (value, priv->cell_border.bottom);
430       break;
431     default:
432       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
433       break;
434     }
435 }
436
437 /*************************************************************
438  *                    GtkCellAreaClass                       *
439  *************************************************************/
440 static void
441 gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea        *area,
442                                                    GtkCellAreaIter    *iter,
443                                                    GtkWidget          *widget,
444                                                    gint                width,
445                                                    gint               *minimum_height,
446                                                    gint               *natural_height)
447 {
448   /* If the area doesnt do height-for-width, fallback on base preferred height */
449   GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_height, natural_height);
450 }
451
452 static void
453 gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea        *area,
454                                                    GtkCellAreaIter    *iter,
455                                                    GtkWidget          *widget,
456                                                    gint                height,
457                                                    gint               *minimum_width,
458                                                    gint               *natural_width)
459 {
460   /* If the area doesnt do width-for-height, fallback on base preferred width */
461   GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_width, natural_width);
462 }
463
464 /*************************************************************
465  *                   GtkCellLayoutIface                      *
466  *************************************************************/
467 static void
468 gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
469 {
470   iface->pack_start         = gtk_cell_area_pack_default;
471   iface->pack_end           = gtk_cell_area_pack_default;
472   iface->clear              = gtk_cell_area_clear;
473   iface->add_attribute      = gtk_cell_area_add_attribute;
474   iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
475   iface->clear_attributes   = gtk_cell_area_clear_attributes;
476   iface->reorder            = gtk_cell_area_reorder;
477   iface->get_cells          = gtk_cell_area_get_cells;
478 }
479
480 static void
481 gtk_cell_area_pack_default (GtkCellLayout         *cell_layout,
482                             GtkCellRenderer       *renderer,
483                             gboolean               expand)
484 {
485   gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer);
486 }
487
488 static void
489 gtk_cell_area_clear (GtkCellLayout *cell_layout)
490 {
491   GtkCellArea *area = GTK_CELL_AREA (cell_layout);
492   GList *l, *cells  =
493     gtk_cell_layout_get_cells (cell_layout);
494
495   for (l = cells; l; l = l->next)
496     {
497       GtkCellRenderer *renderer = l->data;
498       gtk_cell_area_remove (area, renderer);
499     }
500
501   g_list_free (cells);
502 }
503
504 static void
505 gtk_cell_area_add_attribute (GtkCellLayout         *cell_layout,
506                              GtkCellRenderer       *renderer,
507                              const gchar           *attribute,
508                              gint                   column)
509 {
510   gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout),
511                                    renderer, attribute, column);
512 }
513
514 static void
515 gtk_cell_area_set_cell_data_func (GtkCellLayout         *cell_layout,
516                                   GtkCellRenderer       *renderer,
517                                   GtkCellLayoutDataFunc  func,
518                                   gpointer               func_data,
519                                   GDestroyNotify         destroy)
520 {
521   GtkCellArea        *area   = GTK_CELL_AREA (cell_layout);
522   GtkCellAreaPrivate *priv   = area->priv;
523   CellInfo           *info;
524
525   info = g_hash_table_lookup (priv->cell_info, renderer);
526
527   if (info)
528     {
529       if (info->destroy && info->data)
530         info->destroy (info->data);
531
532       if (func)
533         {
534           info->func    = func;
535           info->data    = func_data;
536           info->destroy = destroy;
537         }
538       else
539         {
540           info->func    = NULL;
541           info->data    = NULL;
542           info->destroy = NULL;
543         }
544     }
545   else
546     {
547       info = cell_info_new (func, func_data, destroy);
548
549       g_hash_table_insert (priv->cell_info, renderer, info);
550     }
551 }
552
553 static void
554 gtk_cell_area_clear_attributes (GtkCellLayout         *cell_layout,
555                                 GtkCellRenderer       *renderer)
556 {
557   GtkCellArea        *area = GTK_CELL_AREA (cell_layout);
558   GtkCellAreaPrivate *priv = area->priv;
559   CellInfo           *info;
560
561   info = g_hash_table_lookup (priv->cell_info, renderer);
562
563   if (info)
564     {
565       g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
566       g_slist_free (info->attributes);
567
568       info->attributes = NULL;
569     }
570 }
571
572 static void 
573 gtk_cell_area_reorder (GtkCellLayout   *cell_layout,
574                        GtkCellRenderer *cell,
575                        gint             position)
576 {
577   g_warning ("GtkCellLayout::reorder not implemented for `%s'", 
578              g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
579 }
580
581 static void
582 accum_cells (GtkCellRenderer *renderer,
583              GList          **accum)
584 {
585   *accum = g_list_prepend (*accum, renderer);
586 }
587
588 static GList *
589 gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
590 {
591   GList *cells = NULL;
592
593   gtk_cell_area_forall (GTK_CELL_AREA (cell_layout), 
594                         (GtkCellCallback)accum_cells,
595                         &cells);
596
597   return g_list_reverse (cells);
598 }
599
600
601 /*************************************************************
602  *                            API                            *
603  *************************************************************/
604 void
605 gtk_cell_area_add (GtkCellArea        *area,
606                    GtkCellRenderer    *renderer)
607 {
608   GtkCellAreaClass *class;
609
610   g_return_if_fail (GTK_IS_CELL_AREA (area));
611   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
612
613   class = GTK_CELL_AREA_GET_CLASS (area);
614
615   if (class->add)
616     class->add (area, renderer);
617   else
618     g_warning ("GtkCellAreaClass::add not implemented for `%s'", 
619                g_type_name (G_TYPE_FROM_INSTANCE (area)));
620 }
621
622 void
623 gtk_cell_area_remove (GtkCellArea        *area,
624                       GtkCellRenderer    *renderer)
625 {
626   GtkCellAreaClass   *class;
627   GtkCellAreaPrivate *priv;
628
629   g_return_if_fail (GTK_IS_CELL_AREA (area));
630   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
631
632   class = GTK_CELL_AREA_GET_CLASS (area);
633   priv  = area->priv;
634
635   /* Remove any custom attributes and custom cell data func here first */
636   g_hash_table_remove (priv->cell_info, renderer);
637
638   if (class->remove)
639     class->remove (area, renderer);
640   else
641     g_warning ("GtkCellAreaClass::remove not implemented for `%s'", 
642                g_type_name (G_TYPE_FROM_INSTANCE (area)));
643 }
644
645 void
646 gtk_cell_area_forall (GtkCellArea        *area,
647                       GtkCellCallback     callback,
648                       gpointer            callback_data)
649 {
650   GtkCellAreaClass *class;
651
652   g_return_if_fail (GTK_IS_CELL_AREA (area));
653   g_return_if_fail (callback != NULL);
654
655   class = GTK_CELL_AREA_GET_CLASS (area);
656
657   if (class->forall)
658     class->forall (area, callback, callback_data);
659   else
660     g_warning ("GtkCellAreaClass::forall not implemented for `%s'", 
661                g_type_name (G_TYPE_FROM_INSTANCE (area)));
662 }
663
664 gint
665 gtk_cell_area_event (GtkCellArea          *area,
666                      GtkCellAreaIter      *iter,
667                      GtkWidget            *widget,
668                      GdkEvent             *event,
669                      const GdkRectangle   *cell_area,
670                      GtkCellRendererState  flags)
671 {
672   GtkCellAreaClass *class;
673
674   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
675   g_return_val_if_fail (GTK_IS_CELL_AREA_ITER (iter), 0);
676   g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
677   g_return_val_if_fail (event != NULL, 0);
678   g_return_val_if_fail (cell_area != NULL, 0);
679
680   class = GTK_CELL_AREA_GET_CLASS (area);
681
682   if (class->event)
683     return class->event (area, iter, widget, event, cell_area, flags);
684
685   g_warning ("GtkCellAreaClass::event not implemented for `%s'", 
686              g_type_name (G_TYPE_FROM_INSTANCE (area)));
687   return 0;
688 }
689
690 void
691 gtk_cell_area_render (GtkCellArea          *area,
692                       GtkCellAreaIter      *iter,
693                       GtkWidget            *widget,
694                       cairo_t              *cr,
695                       const GdkRectangle   *cell_area,
696                       GtkCellRendererState  flags)
697 {
698   GtkCellAreaClass *class;
699
700   g_return_if_fail (GTK_IS_CELL_AREA (area));
701   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
702   g_return_if_fail (GTK_IS_WIDGET (widget));
703   g_return_if_fail (cr != NULL);
704   g_return_if_fail (cell_area != NULL);
705
706   class = GTK_CELL_AREA_GET_CLASS (area);
707
708   if (class->render)
709     class->render (area, iter, widget, cr, cell_area, flags);
710   else
711     g_warning ("GtkCellAreaClass::render not implemented for `%s'", 
712                g_type_name (G_TYPE_FROM_INSTANCE (area)));
713 }
714
715 /*************************************************************
716  *                      API: Geometry                        *
717  *************************************************************/
718 GtkCellAreaIter   *
719 gtk_cell_area_create_iter (GtkCellArea *area)
720 {
721   GtkCellAreaClass *class;
722
723   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
724
725   class = GTK_CELL_AREA_GET_CLASS (area);
726
727   if (class->create_iter)
728     return class->create_iter (area);
729
730   g_warning ("GtkCellAreaClass::create_iter not implemented for `%s'", 
731              g_type_name (G_TYPE_FROM_INSTANCE (area)));
732   
733   return NULL;
734 }
735
736
737 GtkSizeRequestMode 
738 gtk_cell_area_get_request_mode (GtkCellArea *area)
739 {
740   GtkCellAreaClass *class;
741
742   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 
743                         GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH);
744
745   class = GTK_CELL_AREA_GET_CLASS (area);
746
747   if (class->get_request_mode)
748     return class->get_request_mode (area);
749
750   g_warning ("GtkCellAreaClass::get_request_mode not implemented for `%s'", 
751              g_type_name (G_TYPE_FROM_INSTANCE (area)));
752   
753   return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
754 }
755
756 void
757 gtk_cell_area_get_preferred_width (GtkCellArea        *area,
758                                    GtkCellAreaIter    *iter,
759                                    GtkWidget          *widget,
760                                    gint               *minimum_size,
761                                    gint               *natural_size)
762 {
763   GtkCellAreaClass *class;
764
765   g_return_if_fail (GTK_IS_CELL_AREA (area));
766   g_return_if_fail (GTK_IS_WIDGET (widget));
767
768   class = GTK_CELL_AREA_GET_CLASS (area);
769
770   if (class->get_preferred_width)
771     class->get_preferred_width (area, iter, widget, minimum_size, natural_size);
772   else
773     g_warning ("GtkCellAreaClass::get_preferred_width not implemented for `%s'", 
774                g_type_name (G_TYPE_FROM_INSTANCE (area)));
775 }
776
777 void
778 gtk_cell_area_get_preferred_height_for_width (GtkCellArea        *area,
779                                               GtkCellAreaIter    *iter,
780                                               GtkWidget          *widget,
781                                               gint                width,
782                                               gint               *minimum_height,
783                                               gint               *natural_height)
784 {
785   GtkCellAreaClass *class;
786
787   g_return_if_fail (GTK_IS_CELL_AREA (area));
788   g_return_if_fail (GTK_IS_WIDGET (widget));
789
790   class = GTK_CELL_AREA_GET_CLASS (area);
791   class->get_preferred_height_for_width (area, iter, widget, width, minimum_height, natural_height);
792 }
793
794 void
795 gtk_cell_area_get_preferred_height (GtkCellArea        *area,
796                                     GtkCellAreaIter    *iter,
797                                     GtkWidget          *widget,
798                                     gint               *minimum_size,
799                                     gint               *natural_size)
800 {
801   GtkCellAreaClass *class;
802
803   g_return_if_fail (GTK_IS_CELL_AREA (area));
804   g_return_if_fail (GTK_IS_WIDGET (widget));
805
806   class = GTK_CELL_AREA_GET_CLASS (area);
807
808   if (class->get_preferred_height)
809     class->get_preferred_height (area, iter, widget, minimum_size, natural_size);
810   else
811     g_warning ("GtkCellAreaClass::get_preferred_height not implemented for `%s'", 
812                g_type_name (G_TYPE_FROM_INSTANCE (area)));
813 }
814
815 void
816 gtk_cell_area_get_preferred_width_for_height (GtkCellArea        *area,
817                                               GtkCellAreaIter    *iter,
818                                               GtkWidget          *widget,
819                                               gint                height,
820                                               gint               *minimum_width,
821                                               gint               *natural_width)
822 {
823   GtkCellAreaClass *class;
824
825   g_return_if_fail (GTK_IS_CELL_AREA (area));
826   g_return_if_fail (GTK_IS_WIDGET (widget));
827
828   class = GTK_CELL_AREA_GET_CLASS (area);
829   class->get_preferred_width_for_height (area, iter, widget, height, minimum_width, natural_width);
830 }
831
832 /*************************************************************
833  *                      API: Attributes                      *
834  *************************************************************/
835 void
836 gtk_cell_area_attribute_connect (GtkCellArea        *area,
837                                  GtkCellRenderer    *renderer,
838                                  const gchar        *attribute,
839                                  gint                column)
840
841   GtkCellAreaPrivate *priv;
842   CellInfo           *info;
843   CellAttribute      *cell_attribute;
844
845   g_return_if_fail (GTK_IS_CELL_AREA (area));
846   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
847   g_return_if_fail (attribute != NULL);
848
849   priv = area->priv;
850   info = g_hash_table_lookup (priv->cell_info, renderer);
851
852   if (!info)
853     {
854       info = cell_info_new (NULL, NULL, NULL);
855
856       g_hash_table_insert (priv->cell_info, renderer, info);
857     }
858   else
859     {
860       GSList *node;
861
862       /* Check we are not adding the same attribute twice */
863       if ((node = g_slist_find_custom (info->attributes, attribute,
864                                        (GCompareFunc)cell_attribute_find)) != NULL)
865         {
866           cell_attribute = node->data;
867
868           g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
869                      "since `%s' is already attributed to column %d", 
870                      attribute,
871                      g_type_name (G_TYPE_FROM_INSTANCE (area)),
872                      attribute, cell_attribute->column);
873           return;
874         }
875     }
876
877   cell_attribute = cell_attribute_new (renderer, attribute, column);
878
879   if (!cell_attribute)
880     {
881       g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
882                  "since attribute does not exist", 
883                  attribute,
884                  g_type_name (G_TYPE_FROM_INSTANCE (area)));
885       return;
886     }
887
888   info->attributes = g_slist_prepend (info->attributes, cell_attribute);
889 }
890
891 void 
892 gtk_cell_area_attribute_disconnect (GtkCellArea        *area,
893                                     GtkCellRenderer    *renderer,
894                                     const gchar        *attribute)
895 {
896   GtkCellAreaPrivate *priv;
897   CellInfo           *info;
898   CellAttribute      *cell_attribute;
899   GSList             *node;
900
901   g_return_if_fail (GTK_IS_CELL_AREA (area));
902   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
903   g_return_if_fail (attribute != NULL);
904
905   priv = area->priv;
906   info = g_hash_table_lookup (priv->cell_info, renderer);
907
908   if (info)
909     {
910       node = g_slist_find_custom (info->attributes, attribute,
911                                   (GCompareFunc)cell_attribute_find);
912       if (node)
913         {
914           cell_attribute = node->data;
915
916           cell_attribute_free (cell_attribute);
917
918           info->attributes = g_slist_delete_link (info->attributes, node);
919         }
920     }
921 }
922
923 static void
924 apply_cell_attributes (GtkCellRenderer *renderer,
925                        CellInfo        *info,
926                        AttributeData   *data)
927 {
928   CellAttribute *attribute;
929   GSList        *list;
930   GValue         value = { 0, };
931   gboolean       is_expander;
932   gboolean       is_expanded;
933
934   g_object_freeze_notify (G_OBJECT (renderer));
935
936   /* Whether a row expands or is presently expanded can only be 
937    * provided by the view (as these states can vary across views
938    * accessing the same model).
939    */
940   g_object_get (renderer, "is-expander", &is_expander, NULL);
941   if (is_expander != data->is_expander)
942     g_object_set (renderer, "is-expander", data->is_expander, NULL);
943   
944   g_object_get (renderer, "is-expanded", &is_expanded, NULL);
945   if (is_expanded != data->is_expanded)
946     g_object_set (renderer, "is-expanded", data->is_expanded, NULL);
947
948   /* Apply the attributes directly to the renderer */
949   for (list = info->attributes; list; list = list->next)
950     {
951       attribute = list->data;
952
953       gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value);
954       g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value);
955       g_value_unset (&value);
956     }
957
958   /* Call any GtkCellLayoutDataFunc that may have been set by the user
959    */
960   if (info->func)
961     info->func (GTK_CELL_LAYOUT (data->area), renderer,
962                 data->model, data->iter, info->data);
963
964   g_object_thaw_notify (G_OBJECT (renderer));
965 }
966
967 void
968 gtk_cell_area_apply_attributes (GtkCellArea  *area,
969                                 GtkTreeModel *tree_model,
970                                 GtkTreeIter  *iter,
971                                 gboolean      is_expander,
972                                 gboolean      is_expanded)
973 {
974   GtkCellAreaPrivate *priv;
975   AttributeData       data;
976
977   g_return_if_fail (GTK_IS_CELL_AREA (area));
978   g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
979   g_return_if_fail (iter != NULL);
980
981   priv = area->priv;
982
983   /* Feed in data needed to apply to every renderer */
984   data.area        = area;
985   data.model       = tree_model;
986   data.iter        = iter;
987   data.is_expander = is_expander;
988   data.is_expanded = is_expanded;
989
990   /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and
991    * apply the data from the treemodel */
992   g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data);
993 }
994
995 /*************************************************************
996  *                    API: Cell Properties                   *
997  *************************************************************/
998 void
999 gtk_cell_area_class_install_cell_property (GtkCellAreaClass   *aclass,
1000                                            guint               property_id,
1001                                            GParamSpec         *pspec)
1002 {
1003   g_return_if_fail (GTK_IS_CELL_AREA_CLASS (aclass));
1004   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
1005   if (pspec->flags & G_PARAM_WRITABLE)
1006     g_return_if_fail (aclass->set_cell_property != NULL);
1007   if (pspec->flags & G_PARAM_READABLE)
1008     g_return_if_fail (aclass->get_cell_property != NULL);
1009   g_return_if_fail (property_id > 0);
1010   g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0);  /* paranoid */
1011   g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
1012
1013   if (g_param_spec_pool_lookup (cell_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (aclass), TRUE))
1014     {
1015       g_warning (G_STRLOC ": class `%s' already contains a cell property named `%s'",
1016                  G_OBJECT_CLASS_NAME (aclass), pspec->name);
1017       return;
1018     }
1019   g_param_spec_ref (pspec);
1020   g_param_spec_sink (pspec);
1021   PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
1022   g_param_spec_pool_insert (cell_property_pool, pspec, G_OBJECT_CLASS_TYPE (aclass));
1023 }
1024
1025 GParamSpec*
1026 gtk_cell_area_class_find_cell_property (GtkCellAreaClass   *aclass,
1027                                         const gchar        *property_name)
1028 {
1029   g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1030   g_return_val_if_fail (property_name != NULL, NULL);
1031
1032   return g_param_spec_pool_lookup (cell_property_pool,
1033                                    property_name,
1034                                    G_OBJECT_CLASS_TYPE (aclass),
1035                                    TRUE);
1036 }
1037
1038 GParamSpec**
1039 gtk_cell_area_class_list_cell_properties (GtkCellAreaClass   *aclass,
1040                                           guint             *n_properties)
1041 {
1042   GParamSpec **pspecs;
1043   guint n;
1044
1045   g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1046
1047   pspecs = g_param_spec_pool_list (cell_property_pool,
1048                                    G_OBJECT_CLASS_TYPE (aclass),
1049                                    &n);
1050   if (n_properties)
1051     *n_properties = n;
1052
1053   return pspecs;
1054 }
1055
1056 void
1057 gtk_cell_area_add_with_properties (GtkCellArea        *area,
1058                                    GtkCellRenderer    *renderer,
1059                                    const gchar        *first_prop_name,
1060                                    ...)
1061 {
1062   GtkCellAreaClass *class;
1063
1064   g_return_if_fail (GTK_IS_CELL_AREA (area));
1065   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1066
1067   class = GTK_CELL_AREA_GET_CLASS (area);
1068
1069   if (class->add)
1070     {
1071       va_list var_args;
1072
1073       class->add (area, renderer);
1074
1075       va_start (var_args, first_prop_name);
1076       gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1077       va_end (var_args);
1078     }
1079   else
1080     g_warning ("GtkCellAreaClass::add not implemented for `%s'", 
1081                g_type_name (G_TYPE_FROM_INSTANCE (area)));
1082 }
1083
1084 void
1085 gtk_cell_area_cell_set (GtkCellArea        *area,
1086                         GtkCellRenderer    *renderer,
1087                         const gchar        *first_prop_name,
1088                         ...)
1089 {
1090   va_list var_args;
1091
1092   g_return_if_fail (GTK_IS_CELL_AREA (area));
1093   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1094
1095   va_start (var_args, first_prop_name);
1096   gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1097   va_end (var_args);
1098 }
1099
1100 void
1101 gtk_cell_area_cell_get (GtkCellArea        *area,
1102                         GtkCellRenderer    *renderer,
1103                         const gchar        *first_prop_name,
1104                         ...)
1105 {
1106   va_list var_args;
1107
1108   g_return_if_fail (GTK_IS_CELL_AREA (area));
1109   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1110
1111   va_start (var_args, first_prop_name);
1112   gtk_cell_area_cell_get_valist (area, renderer, first_prop_name, var_args);
1113   va_end (var_args);
1114 }
1115
1116 static inline void
1117 area_get_cell_property (GtkCellArea     *area,
1118                         GtkCellRenderer *renderer,
1119                         GParamSpec      *pspec,
1120                         GValue          *value)
1121 {
1122   GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1123   
1124   class->get_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
1125 }
1126
1127 static inline void
1128 area_set_cell_property (GtkCellArea     *area,
1129                         GtkCellRenderer *renderer,
1130                         GParamSpec      *pspec,
1131                         const GValue    *value)
1132 {
1133   GValue tmp_value = { 0, };
1134   GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1135
1136   /* provide a copy to work from, convert (if necessary) and validate */
1137   g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1138   if (!g_value_transform (value, &tmp_value))
1139     g_warning ("unable to set cell property `%s' of type `%s' from value of type `%s'",
1140                pspec->name,
1141                g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1142                G_VALUE_TYPE_NAME (value));
1143   else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
1144     {
1145       gchar *contents = g_strdup_value_contents (value);
1146
1147       g_warning ("value \"%s\" of type `%s' is invalid for property `%s' of type `%s'",
1148                  contents,
1149                  G_VALUE_TYPE_NAME (value),
1150                  pspec->name,
1151                  g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
1152       g_free (contents);
1153     }
1154   else
1155     {
1156       class->set_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
1157     }
1158   g_value_unset (&tmp_value);
1159 }
1160
1161 void
1162 gtk_cell_area_cell_set_valist (GtkCellArea        *area,
1163                                GtkCellRenderer    *renderer,
1164                                const gchar        *first_property_name,
1165                                va_list             var_args)
1166 {
1167   const gchar *name;
1168
1169   g_return_if_fail (GTK_IS_CELL_AREA (area));
1170   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1171
1172   name = first_property_name;
1173   while (name)
1174     {
1175       GValue value = { 0, };
1176       gchar *error = NULL;
1177       GParamSpec *pspec = 
1178         g_param_spec_pool_lookup (cell_property_pool, name,
1179                                   G_OBJECT_TYPE (area), TRUE);
1180       if (!pspec)
1181         {
1182           g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1183                      G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1184           break;
1185         }
1186       if (!(pspec->flags & G_PARAM_WRITABLE))
1187         {
1188           g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1189                      G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1190           break;
1191         }
1192
1193       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1194       G_VALUE_COLLECT (&value, var_args, 0, &error);
1195       if (error)
1196         {
1197           g_warning ("%s: %s", G_STRLOC, error);
1198           g_free (error);
1199
1200           /* we purposely leak the value here, it might not be
1201            * in a sane state if an error condition occoured
1202            */
1203           break;
1204         }
1205       area_set_cell_property (area, renderer, pspec, &value);
1206       g_value_unset (&value);
1207       name = va_arg (var_args, gchar*);
1208     }
1209 }
1210
1211 void
1212 gtk_cell_area_cell_get_valist (GtkCellArea        *area,
1213                                GtkCellRenderer    *renderer,
1214                                const gchar        *first_property_name,
1215                                va_list             var_args)
1216 {
1217   const gchar *name;
1218
1219   g_return_if_fail (GTK_IS_CELL_AREA (area));
1220   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1221
1222   name = first_property_name;
1223   while (name)
1224     {
1225       GValue value = { 0, };
1226       GParamSpec *pspec;
1227       gchar *error;
1228
1229       pspec = g_param_spec_pool_lookup (cell_property_pool, name,
1230                                         G_OBJECT_TYPE (area), TRUE);
1231       if (!pspec)
1232         {
1233           g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1234                      G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1235           break;
1236         }
1237       if (!(pspec->flags & G_PARAM_READABLE))
1238         {
1239           g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1240                      G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1241           break;
1242         }
1243
1244       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1245       area_get_cell_property (area, renderer, pspec, &value);
1246       G_VALUE_LCOPY (&value, var_args, 0, &error);
1247       if (error)
1248         {
1249           g_warning ("%s: %s", G_STRLOC, error);
1250           g_free (error);
1251           g_value_unset (&value);
1252           break;
1253         }
1254       g_value_unset (&value);
1255       name = va_arg (var_args, gchar*);
1256     }
1257 }
1258
1259 void
1260 gtk_cell_area_cell_set_property (GtkCellArea        *area,
1261                                  GtkCellRenderer    *renderer,
1262                                  const gchar        *property_name,
1263                                  const GValue       *value)
1264 {
1265   GParamSpec *pspec;
1266
1267   g_return_if_fail (GTK_IS_CELL_AREA (area));
1268   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1269   g_return_if_fail (property_name != NULL);
1270   g_return_if_fail (G_IS_VALUE (value));
1271   
1272   pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1273                                     G_OBJECT_TYPE (area), TRUE);
1274   if (!pspec)
1275     g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1276                G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1277   else if (!(pspec->flags & G_PARAM_WRITABLE))
1278     g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1279                G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1280   else
1281     {
1282       area_set_cell_property (area, renderer, pspec, value);
1283     }
1284 }
1285
1286 void
1287 gtk_cell_area_cell_get_property (GtkCellArea        *area,
1288                                  GtkCellRenderer    *renderer,
1289                                  const gchar        *property_name,
1290                                  GValue             *value)
1291 {
1292   GParamSpec *pspec;
1293
1294   g_return_if_fail (GTK_IS_CELL_AREA (area));
1295   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1296   g_return_if_fail (property_name != NULL);
1297   g_return_if_fail (G_IS_VALUE (value));
1298   
1299   pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1300                                     G_OBJECT_TYPE (area), TRUE);
1301   if (!pspec)
1302     g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1303                G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1304   else if (!(pspec->flags & G_PARAM_READABLE))
1305     g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1306                G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1307   else
1308     {
1309       GValue *prop_value, tmp_value = { 0, };
1310
1311       /* auto-conversion of the callers value type
1312        */
1313       if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
1314         {
1315           g_value_reset (value);
1316           prop_value = value;
1317         }
1318       else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
1319         {
1320           g_warning ("can't retrieve cell property `%s' of type `%s' as value of type `%s'",
1321                      pspec->name,
1322                      g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1323                      G_VALUE_TYPE_NAME (value));
1324           return;
1325         }
1326       else
1327         {
1328           g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1329           prop_value = &tmp_value;
1330         }
1331
1332       area_get_cell_property (area, renderer, pspec, prop_value);
1333
1334       if (prop_value != value)
1335         {
1336           g_value_transform (prop_value, value);
1337           g_value_unset (&tmp_value);
1338         }
1339     }
1340 }
1341
1342 /*************************************************************
1343  *                         API: Focus                        *
1344  *************************************************************/
1345 void
1346 gtk_cell_area_grab_focus (GtkCellArea      *area,
1347                           GtkDirectionType  direction)
1348 {
1349   GtkCellAreaClass *class;
1350
1351   g_return_if_fail (GTK_IS_CELL_AREA (area));
1352
1353   class = GTK_CELL_AREA_GET_CLASS (area);
1354
1355   if (class->grab_focus)
1356     class->grab_focus (area, direction);
1357   else
1358     g_warning ("GtkCellAreaClass::grab_focus not implemented for `%s'", 
1359                g_type_name (G_TYPE_FROM_INSTANCE (area)));
1360 }
1361
1362 void
1363 gtk_cell_area_focus_leave (GtkCellArea        *area,
1364                            GtkDirectionType    direction,
1365                            const gchar        *path)
1366 {
1367   g_return_if_fail (GTK_IS_CELL_AREA (area));
1368
1369   g_signal_emit (area, cell_area_signals[SIGNAL_FOCUS_LEAVE], 0, direction, path);
1370 }
1371
1372 void
1373 gtk_cell_area_set_can_focus (GtkCellArea *area,
1374                              gboolean     can_focus)
1375 {
1376   GtkCellAreaPrivate *priv;
1377
1378   g_return_if_fail (GTK_IS_CELL_AREA (area));
1379
1380   priv = area->priv;
1381
1382   if (priv->can_focus != can_focus)
1383     {
1384       priv->can_focus = can_focus;
1385     }
1386 }
1387
1388 gboolean
1389 gtk_cell_area_get_can_focus (GtkCellArea *area)
1390 {
1391   GtkCellAreaPrivate *priv;
1392
1393   g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1394
1395   priv = area->priv;
1396
1397   return priv->can_focus;
1398 }
1399
1400 void
1401 gtk_cell_area_set_focus_cell (GtkCellArea     *area,
1402                               GtkCellRenderer *renderer)
1403 {
1404   GtkCellAreaPrivate *priv;
1405
1406   g_return_if_fail (GTK_IS_CELL_AREA (area));
1407   g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
1408
1409   priv = area->priv;
1410
1411   if (priv->focus_cell != renderer)
1412     {
1413       if (priv->focus_cell)
1414         g_object_unref (priv->focus_cell);
1415
1416       priv->focus_cell = renderer;
1417
1418       if (priv->focus_cell)
1419         g_object_ref (priv->focus_cell);
1420     }
1421 }
1422
1423 GtkCellRenderer *
1424 gtk_cell_area_get_focus_cell (GtkCellArea *area)
1425 {
1426   GtkCellAreaPrivate *priv;
1427
1428   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1429
1430   priv = area->priv;
1431
1432   return priv->focus_cell;
1433 }
1434
1435 /*************************************************************
1436  *                        API: Margins                       *
1437  *************************************************************/
1438 gint
1439 gtk_cell_area_get_cell_margin_left (GtkCellArea *area)
1440 {
1441   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1442
1443   return area->priv->cell_border.left;
1444 }
1445
1446 void
1447 gtk_cell_area_set_cell_margin_left (GtkCellArea *area,
1448                                     gint         margin)
1449 {
1450   GtkCellAreaPrivate *priv;
1451
1452   g_return_if_fail (GTK_IS_CELL_AREA (area));
1453
1454   priv = area->priv;
1455
1456   if (priv->cell_border.left != margin)
1457     {
1458       priv->cell_border.left = margin;
1459
1460       g_object_notify (G_OBJECT (area), "margin-left");
1461     }
1462 }
1463
1464 gint
1465 gtk_cell_area_get_cell_margin_right (GtkCellArea *area)
1466 {
1467   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1468
1469   return area->priv->cell_border.right;
1470 }
1471
1472 void
1473 gtk_cell_area_set_cell_margin_right (GtkCellArea *area,
1474                                      gint         margin)
1475 {
1476   GtkCellAreaPrivate *priv;
1477
1478   g_return_if_fail (GTK_IS_CELL_AREA (area));
1479
1480   priv = area->priv;
1481
1482   if (priv->cell_border.right != margin)
1483     {
1484       priv->cell_border.right = margin;
1485
1486       g_object_notify (G_OBJECT (area), "margin-right");
1487     }
1488 }
1489
1490 gint
1491 gtk_cell_area_get_cell_margin_top (GtkCellArea *area)
1492 {
1493   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1494
1495   return area->priv->cell_border.top;
1496 }
1497
1498 void
1499 gtk_cell_area_set_cell_margin_top (GtkCellArea *area,
1500                                    gint         margin)
1501 {
1502   GtkCellAreaPrivate *priv;
1503
1504   g_return_if_fail (GTK_IS_CELL_AREA (area));
1505
1506   priv = area->priv;
1507
1508   if (priv->cell_border.top != margin)
1509     {
1510       priv->cell_border.top = margin;
1511
1512       g_object_notify (G_OBJECT (area), "margin-top");
1513     }
1514 }
1515
1516 gint
1517 gtk_cell_area_get_cell_margin_bottom (GtkCellArea *area)
1518 {
1519   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1520
1521   return area->priv->cell_border.bottom;
1522 }
1523
1524 void
1525 gtk_cell_area_set_cell_margin_bottom (GtkCellArea *area,
1526                                       gint         margin)
1527 {
1528   GtkCellAreaPrivate *priv;
1529
1530   g_return_if_fail (GTK_IS_CELL_AREA (area));
1531
1532   priv = area->priv;
1533
1534   if (priv->cell_border.bottom != margin)
1535     {
1536       priv->cell_border.bottom = margin;
1537
1538       g_object_notify (G_OBJECT (area), "margin-bottom");
1539     }
1540 }
1541
1542 /* For convenience in area implementations */
1543 void
1544 gtk_cell_area_inner_cell_area (GtkCellArea        *area,
1545                                GdkRectangle       *background_area,
1546                                GdkRectangle       *cell_area)
1547 {
1548   GtkCellAreaPrivate *priv;
1549
1550   g_return_if_fail (GTK_IS_CELL_AREA (area));
1551   g_return_if_fail (background_area != NULL);
1552   g_return_if_fail (cell_area != NULL);
1553
1554   priv = area->priv;
1555
1556   *cell_area = *background_area;
1557
1558   cell_area->x      += priv->cell_border.left;
1559   cell_area->width  -= (priv->cell_border.left + priv->cell_border.right);
1560   cell_area->y      += priv->cell_border.top;
1561   cell_area->height -= (priv->cell_border.top + priv->cell_border.bottom);
1562 }
1563
1564 void
1565 gtk_cell_area_request_renderer (GtkCellArea        *area,
1566                                 GtkCellRenderer    *renderer,
1567                                 GtkOrientation      orientation,
1568                                 GtkWidget          *widget,
1569                                 gint                for_size,
1570                                 gint               *minimum_size,
1571                                 gint               *natural_size)
1572 {
1573   GtkCellAreaPrivate *priv;
1574
1575   g_return_if_fail (GTK_IS_CELL_AREA (area));
1576   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1577   g_return_if_fail (GTK_IS_WIDGET (widget));
1578   g_return_if_fail (minimum_size != NULL);
1579   g_return_if_fail (natural_size != NULL);
1580
1581   priv = area->priv;
1582
1583   if (orientation == GTK_ORIENTATION_HORIZONTAL)
1584     {
1585       if (for_size < 0)
1586           gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size);
1587       else
1588         {
1589           for_size = MAX (0, for_size - (priv->cell_border.top + priv->cell_border.bottom));
1590
1591           gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size, 
1592                                                             minimum_size, natural_size);
1593         }
1594
1595       *minimum_size += (priv->cell_border.left + priv->cell_border.right);
1596       *natural_size += (priv->cell_border.left + priv->cell_border.right);
1597     }
1598   else /* GTK_ORIENTATION_VERTICAL */
1599     {
1600       if (for_size < 0)
1601         gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size);
1602       else
1603         {
1604           for_size = MAX (0, for_size - (priv->cell_border.left + priv->cell_border.right));
1605
1606           gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size, 
1607                                                             minimum_size, natural_size);
1608         }
1609
1610       *minimum_size += (priv->cell_border.top + priv->cell_border.bottom);
1611       *natural_size += (priv->cell_border.top + priv->cell_border.bottom);
1612     }
1613 }