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