]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellarea.c
Implemented "cell properties" on the GtkCellArea
[~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 "gtkcelllayout.h"
31 #include "gtkcellarea.h"
32 #include "gtkcellareaiter.h"
33
34 #include <gobject/gvaluecollector.h>
35
36
37 /* GObjectClass */
38 static void      gtk_cell_area_dispose                             (GObject            *object);
39 static void      gtk_cell_area_finalize                            (GObject            *object);
40
41 /* GtkCellAreaClass */
42 static void      gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea        *area,
43                                                                     GtkCellAreaIter    *iter,
44                                                                     GtkWidget          *widget,
45                                                                     gint                width,
46                                                                     gint               *minimum_height,
47                                                                     gint               *natural_height);
48 static void      gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea        *area,
49                                                                     GtkCellAreaIter    *iter,
50                                                                     GtkWidget          *widget,
51                                                                     gint                height,
52                                                                     gint               *minimum_width,
53                                                                     gint               *natural_width);
54
55 /* GtkCellLayoutIface */
56 static void      gtk_cell_area_cell_layout_init              (GtkCellLayoutIface    *iface);
57 static void      gtk_cell_area_pack_default                  (GtkCellLayout         *cell_layout,
58                                                               GtkCellRenderer       *renderer,
59                                                               gboolean               expand);
60 static void      gtk_cell_area_clear                         (GtkCellLayout         *cell_layout);
61 static void      gtk_cell_area_add_attribute                 (GtkCellLayout         *cell_layout,
62                                                               GtkCellRenderer       *renderer,
63                                                               const gchar           *attribute,
64                                                               gint                   column);
65 static void      gtk_cell_area_set_cell_data_func            (GtkCellLayout         *cell_layout,
66                                                               GtkCellRenderer       *cell,
67                                                               GtkCellLayoutDataFunc  func,
68                                                               gpointer               func_data,
69                                                               GDestroyNotify         destroy);
70 static void      gtk_cell_area_clear_attributes              (GtkCellLayout         *cell_layout,
71                                                               GtkCellRenderer       *renderer);
72 static void      gtk_cell_area_reorder                       (GtkCellLayout         *cell_layout,
73                                                               GtkCellRenderer       *cell,
74                                                               gint                   position);
75 static GList    *gtk_cell_area_get_cells                     (GtkCellLayout         *cell_layout);
76
77 /* Attribute/Cell metadata */
78 typedef struct {
79   const gchar *attribute;
80   gint         column;
81 } CellAttribute;
82
83 typedef struct {
84   GSList                *attributes;
85
86   GtkCellLayoutDataFunc  func;
87   gpointer               data;
88   GDestroyNotify         destroy;
89 } CellInfo;
90
91 static CellInfo       *cell_info_new       (GtkCellLayoutDataFunc  func,
92                                             gpointer               data,
93                                             GDestroyNotify         destroy);
94 static void            cell_info_free      (CellInfo              *info);
95 static CellAttribute  *cell_attribute_new  (GtkCellRenderer       *renderer,
96                                             const gchar           *attribute,
97                                             gint                   column);
98 static void            cell_attribute_free (CellAttribute         *attribute);
99 static gint            cell_attribute_find (CellAttribute         *cell_attribute,
100                                             const gchar           *attribute);
101
102 /* Struct to pass data along while looping over 
103  * cell renderers to apply attributes
104  */
105 typedef struct {
106   GtkCellArea  *area;
107   GtkTreeModel *model;
108   GtkTreeIter  *iter;
109 } AttributeData;
110
111 struct _GtkCellAreaPrivate
112 {
113   GHashTable *cell_info;
114 };
115
116 /* Keep the paramspec pool internal, no need to deliver notifications
117  * on cells. at least no percieved need for now */
118 static GParamSpecPool *cell_property_pool = NULL;
119
120 #define PARAM_SPEC_PARAM_ID(pspec)              ((pspec)->param_id)
121 #define PARAM_SPEC_SET_PARAM_ID(pspec, id)      ((pspec)->param_id = (id))
122
123
124 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
125                                   G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
126                                                          gtk_cell_area_cell_layout_init));
127
128 static void
129 gtk_cell_area_init (GtkCellArea *area)
130 {
131   GtkCellAreaPrivate *priv;
132
133   area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
134                                             GTK_TYPE_CELL_AREA,
135                                             GtkCellAreaPrivate);
136   priv = area->priv;
137
138   priv->cell_info = g_hash_table_new_full (g_direct_hash, 
139                                            g_direct_equal,
140                                            NULL, 
141                                            (GDestroyNotify)cell_info_free);
142 }
143
144 static void 
145 gtk_cell_area_class_init (GtkCellAreaClass *class)
146 {
147   GObjectClass *object_class = G_OBJECT_CLASS (class);
148   
149   /* GObjectClass */
150   object_class->dispose  = gtk_cell_area_dispose;
151   object_class->finalize = gtk_cell_area_finalize;
152
153   /* general */
154   class->add     = NULL;
155   class->remove  = NULL;
156   class->forall  = NULL;
157   class->event   = NULL;
158   class->render  = NULL;
159
160   /* geometry */
161   class->create_iter                    = NULL;
162   class->get_request_mode               = NULL;
163   class->get_preferred_width            = NULL;
164   class->get_preferred_height           = NULL;
165   class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
166   class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
167
168   /* Cell properties */
169   if (!cell_property_pool)
170     cell_property_pool = g_param_spec_pool_new (FALSE);
171
172   g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
173 }
174
175 /*************************************************************
176  *                    CellInfo Basics                        *
177  *************************************************************/
178 static CellInfo *
179 cell_info_new (GtkCellLayoutDataFunc  func,
180                gpointer               data,
181                GDestroyNotify         destroy)
182 {
183   CellInfo *info = g_slice_new (CellInfo);
184   
185   info->attributes = NULL;
186   info->func       = func;
187   info->data       = data;
188   info->destroy    = destroy;
189
190   return info;
191 }
192
193 static void
194 cell_info_free (CellInfo *info)
195 {
196   if (info->destroy)
197     info->destroy (info->data);
198
199   g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
200   g_slist_free (info->attributes);
201
202   g_slice_free (CellInfo, info);
203 }
204
205 static CellAttribute  *
206 cell_attribute_new  (GtkCellRenderer       *renderer,
207                      const gchar           *attribute,
208                      gint                   column)
209 {
210   GParamSpec *pspec;
211
212   /* Check if the attribute really exists and point to
213    * the property string installed on the cell renderer
214    * class (dont dup the string) 
215    */
216   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);
217
218   if (pspec)
219     {
220       CellAttribute *cell_attribute = g_slice_new (CellAttribute);
221
222       cell_attribute->attribute = pspec->name;
223       cell_attribute->column    = column;
224
225       return cell_attribute;
226     }
227
228   return NULL;
229 }
230
231 static void
232 cell_attribute_free (CellAttribute *attribute)
233 {
234   g_slice_free (CellAttribute, attribute);
235 }
236
237 /* GCompareFunc for g_slist_find_custom() */
238 static gint
239 cell_attribute_find (CellAttribute *cell_attribute,
240                      const gchar   *attribute)
241 {
242   return g_strcmp0 (cell_attribute->attribute, attribute);
243 }
244
245 /*************************************************************
246  *                      GObjectClass                         *
247  *************************************************************/
248 static void
249 gtk_cell_area_finalize (GObject *object)
250 {
251   GtkCellArea        *area   = GTK_CELL_AREA (object);
252   GtkCellAreaPrivate *priv   = area->priv;
253
254   /* All cell renderers should already be removed at this point,
255    * just kill our hash table here. 
256    */
257   g_hash_table_destroy (priv->cell_info);
258
259   G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
260 }
261
262
263 static void
264 gtk_cell_area_dispose (GObject *object)
265 {
266   /* This removes every cell renderer that may be added to the GtkCellArea,
267    * subclasses should be breaking references to the GtkCellRenderers 
268    * at this point.
269    */
270   gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
271
272   G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
273 }
274
275
276 /*************************************************************
277  *                    GtkCellAreaClass                       *
278  *************************************************************/
279 static void
280 gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea        *area,
281                                                    GtkCellAreaIter    *iter,
282                                                    GtkWidget          *widget,
283                                                    gint                width,
284                                                    gint               *minimum_height,
285                                                    gint               *natural_height)
286 {
287   /* If the area doesnt do height-for-width, fallback on base preferred height */
288   GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_height, natural_height);
289 }
290
291 static void
292 gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea        *area,
293                                                    GtkCellAreaIter    *iter,
294                                                    GtkWidget          *widget,
295                                                    gint                height,
296                                                    gint               *minimum_width,
297                                                    gint               *natural_width)
298 {
299   /* If the area doesnt do width-for-height, fallback on base preferred width */
300   GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_width, natural_width);
301 }
302
303 /*************************************************************
304  *                   GtkCellLayoutIface                      *
305  *************************************************************/
306 static void
307 gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
308 {
309   iface->pack_start         = gtk_cell_area_pack_default;
310   iface->pack_end           = gtk_cell_area_pack_default;
311   iface->clear              = gtk_cell_area_clear;
312   iface->add_attribute      = gtk_cell_area_add_attribute;
313   iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
314   iface->clear_attributes   = gtk_cell_area_clear_attributes;
315   iface->reorder            = gtk_cell_area_reorder;
316   iface->get_cells          = gtk_cell_area_get_cells;
317 }
318
319 static void
320 gtk_cell_area_pack_default (GtkCellLayout         *cell_layout,
321                             GtkCellRenderer       *renderer,
322                             gboolean               expand)
323 {
324   gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer);
325 }
326
327 static void
328 gtk_cell_area_clear (GtkCellLayout *cell_layout)
329 {
330   GtkCellArea *area = GTK_CELL_AREA (cell_layout);
331   GList *l, *cells  =
332     gtk_cell_layout_get_cells (cell_layout);
333
334   for (l = cells; l; l = l->next)
335     {
336       GtkCellRenderer *renderer = l->data;
337       gtk_cell_area_remove (area, renderer);
338     }
339
340   g_list_free (cells);
341 }
342
343 static void
344 gtk_cell_area_add_attribute (GtkCellLayout         *cell_layout,
345                              GtkCellRenderer       *renderer,
346                              const gchar           *attribute,
347                              gint                   column)
348 {
349   gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout),
350                                    renderer, attribute, column);
351 }
352
353 static void
354 gtk_cell_area_set_cell_data_func (GtkCellLayout         *cell_layout,
355                                   GtkCellRenderer       *renderer,
356                                   GtkCellLayoutDataFunc  func,
357                                   gpointer               func_data,
358                                   GDestroyNotify         destroy)
359 {
360   GtkCellArea        *area   = GTK_CELL_AREA (cell_layout);
361   GtkCellAreaPrivate *priv   = area->priv;
362   CellInfo           *info;
363
364   info = g_hash_table_lookup (priv->cell_info, renderer);
365
366   if (info)
367     {
368       if (info->destroy && info->data)
369         info->destroy (info->data);
370
371       if (func)
372         {
373           info->func    = func;
374           info->data    = func_data;
375           info->destroy = destroy;
376         }
377       else
378         {
379           info->func    = NULL;
380           info->data    = NULL;
381           info->destroy = NULL;
382         }
383     }
384   else
385     {
386       info = cell_info_new (func, func_data, destroy);
387
388       g_hash_table_insert (priv->cell_info, renderer, info);
389     }
390 }
391
392 static void
393 gtk_cell_area_clear_attributes (GtkCellLayout         *cell_layout,
394                                 GtkCellRenderer       *renderer)
395 {
396   GtkCellArea        *area = GTK_CELL_AREA (cell_layout);
397   GtkCellAreaPrivate *priv = area->priv;
398   CellInfo           *info;
399
400   info = g_hash_table_lookup (priv->cell_info, renderer);
401
402   if (info)
403     {
404       g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
405       g_slist_free (info->attributes);
406
407       info->attributes = NULL;
408     }
409 }
410
411 static void 
412 gtk_cell_area_reorder (GtkCellLayout   *cell_layout,
413                        GtkCellRenderer *cell,
414                        gint             position)
415 {
416   g_warning ("GtkCellLayout::reorder not implemented for `%s'", 
417              g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
418 }
419
420 static void
421 accum_cells (GtkCellRenderer *renderer,
422              GList          **accum)
423 {
424   *accum = g_list_prepend (*accum, renderer);
425 }
426
427 static GList *
428 gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
429 {
430   GList *cells = NULL;
431
432   gtk_cell_area_forall (GTK_CELL_AREA (cell_layout), 
433                         (GtkCellCallback)accum_cells,
434                         &cells);
435
436   return g_list_reverse (cells);
437 }
438
439
440 /*************************************************************
441  *                            API                            *
442  *************************************************************/
443 void
444 gtk_cell_area_add (GtkCellArea        *area,
445                    GtkCellRenderer    *renderer)
446 {
447   GtkCellAreaClass *class;
448
449   g_return_if_fail (GTK_IS_CELL_AREA (area));
450   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
451
452   class = GTK_CELL_AREA_GET_CLASS (area);
453
454   if (class->add)
455     class->add (area, renderer);
456   else
457     g_warning ("GtkCellAreaClass::add not implemented for `%s'", 
458                g_type_name (G_TYPE_FROM_INSTANCE (area)));
459 }
460
461 void
462 gtk_cell_area_remove (GtkCellArea        *area,
463                       GtkCellRenderer    *renderer)
464 {
465   GtkCellAreaClass   *class;
466   GtkCellAreaPrivate *priv;
467
468   g_return_if_fail (GTK_IS_CELL_AREA (area));
469   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
470
471   class = GTK_CELL_AREA_GET_CLASS (area);
472   priv  = area->priv;
473
474   /* Remove any custom attributes and custom cell data func here first */
475   g_hash_table_remove (priv->cell_info, renderer);
476
477   if (class->remove)
478     class->remove (area, renderer);
479   else
480     g_warning ("GtkCellAreaClass::remove not implemented for `%s'", 
481                g_type_name (G_TYPE_FROM_INSTANCE (area)));
482 }
483
484 void
485 gtk_cell_area_forall (GtkCellArea        *area,
486                       GtkCellCallback     callback,
487                       gpointer            callback_data)
488 {
489   GtkCellAreaClass *class;
490
491   g_return_if_fail (GTK_IS_CELL_AREA (area));
492   g_return_if_fail (callback != NULL);
493
494   class = GTK_CELL_AREA_GET_CLASS (area);
495
496   if (class->forall)
497     class->forall (area, callback, callback_data);
498   else
499     g_warning ("GtkCellAreaClass::forall not implemented for `%s'", 
500                g_type_name (G_TYPE_FROM_INSTANCE (area)));
501 }
502
503 gint
504 gtk_cell_area_event (GtkCellArea        *area,
505                      GtkCellAreaIter    *iter,
506                      GtkWidget          *widget,
507                      GdkEvent           *event,
508                      const GdkRectangle *cell_area)
509 {
510   GtkCellAreaClass *class;
511
512   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
513   g_return_val_if_fail (GTK_IS_CELL_AREA_ITER (iter), 0);
514   g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
515   g_return_val_if_fail (event != NULL, 0);
516   g_return_val_if_fail (cell_area != NULL, 0);
517
518   class = GTK_CELL_AREA_GET_CLASS (area);
519
520   if (class->event)
521     return class->event (area, iter, widget, event, cell_area);
522
523   g_warning ("GtkCellAreaClass::event not implemented for `%s'", 
524              g_type_name (G_TYPE_FROM_INSTANCE (area)));
525   return 0;
526 }
527
528 void
529 gtk_cell_area_render (GtkCellArea        *area,
530                       GtkCellAreaIter    *iter,
531                       GtkWidget          *widget,
532                       cairo_t            *cr,
533                       const GdkRectangle *cell_area)
534 {
535   GtkCellAreaClass *class;
536
537   g_return_if_fail (GTK_IS_CELL_AREA (area));
538   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
539   g_return_if_fail (GTK_IS_WIDGET (widget));
540   g_return_if_fail (cr != NULL);
541   g_return_if_fail (cell_area != NULL);
542
543   class = GTK_CELL_AREA_GET_CLASS (area);
544
545   if (class->render)
546     class->render (area, iter, widget, cr, cell_area);
547   else
548     g_warning ("GtkCellAreaClass::render not implemented for `%s'", 
549                g_type_name (G_TYPE_FROM_INSTANCE (area)));
550 }
551
552 /* Geometry */
553 GtkCellAreaIter   *
554 gtk_cell_area_create_iter (GtkCellArea *area)
555 {
556   GtkCellAreaClass *class;
557
558   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
559
560   class = GTK_CELL_AREA_GET_CLASS (area);
561
562   if (class->create_iter)
563     return class->create_iter (area);
564
565   g_warning ("GtkCellAreaClass::create_iter not implemented for `%s'", 
566              g_type_name (G_TYPE_FROM_INSTANCE (area)));
567   
568   return NULL;
569 }
570
571
572 GtkSizeRequestMode 
573 gtk_cell_area_get_request_mode (GtkCellArea *area)
574 {
575   GtkCellAreaClass *class;
576
577   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 
578                         GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH);
579
580   class = GTK_CELL_AREA_GET_CLASS (area);
581
582   if (class->get_request_mode)
583     return class->get_request_mode (area);
584
585   g_warning ("GtkCellAreaClass::get_request_mode not implemented for `%s'", 
586              g_type_name (G_TYPE_FROM_INSTANCE (area)));
587   
588   return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
589 }
590
591 void
592 gtk_cell_area_get_preferred_width (GtkCellArea        *area,
593                                    GtkCellAreaIter    *iter,
594                                    GtkWidget          *widget,
595                                    gint               *minimum_size,
596                                    gint               *natural_size)
597 {
598   GtkCellAreaClass *class;
599
600   g_return_if_fail (GTK_IS_CELL_AREA (area));
601   g_return_if_fail (GTK_IS_WIDGET (widget));
602
603   class = GTK_CELL_AREA_GET_CLASS (area);
604
605   if (class->get_preferred_width)
606     class->get_preferred_width (area, iter, widget, minimum_size, natural_size);
607   else
608     g_warning ("GtkCellAreaClass::get_preferred_width not implemented for `%s'", 
609                g_type_name (G_TYPE_FROM_INSTANCE (area)));
610 }
611
612 void
613 gtk_cell_area_get_preferred_height_for_width (GtkCellArea        *area,
614                                               GtkCellAreaIter    *iter,
615                                               GtkWidget          *widget,
616                                               gint                width,
617                                               gint               *minimum_height,
618                                               gint               *natural_height)
619 {
620   GtkCellAreaClass *class;
621
622   g_return_if_fail (GTK_IS_CELL_AREA (area));
623   g_return_if_fail (GTK_IS_WIDGET (widget));
624
625   class = GTK_CELL_AREA_GET_CLASS (area);
626   class->get_preferred_height_for_width (area, iter, widget, width, minimum_height, natural_height);
627 }
628
629 void
630 gtk_cell_area_get_preferred_height (GtkCellArea        *area,
631                                     GtkCellAreaIter    *iter,
632                                     GtkWidget          *widget,
633                                     gint               *minimum_size,
634                                     gint               *natural_size)
635 {
636   GtkCellAreaClass *class;
637
638   g_return_if_fail (GTK_IS_CELL_AREA (area));
639   g_return_if_fail (GTK_IS_WIDGET (widget));
640
641   class = GTK_CELL_AREA_GET_CLASS (area);
642
643   if (class->get_preferred_height)
644     class->get_preferred_height (area, iter, widget, minimum_size, natural_size);
645   else
646     g_warning ("GtkCellAreaClass::get_preferred_height not implemented for `%s'", 
647                g_type_name (G_TYPE_FROM_INSTANCE (area)));
648 }
649
650 void
651 gtk_cell_area_get_preferred_width_for_height (GtkCellArea        *area,
652                                               GtkCellAreaIter    *iter,
653                                               GtkWidget          *widget,
654                                               gint                height,
655                                               gint               *minimum_width,
656                                               gint               *natural_width)
657 {
658   GtkCellAreaClass *class;
659
660   g_return_if_fail (GTK_IS_CELL_AREA (area));
661   g_return_if_fail (GTK_IS_WIDGET (widget));
662
663   class = GTK_CELL_AREA_GET_CLASS (area);
664   class->get_preferred_width_for_height (area, iter, widget, height, minimum_width, natural_width);
665 }
666
667 /* Attributes */
668 void
669 gtk_cell_area_attribute_connect (GtkCellArea        *area,
670                                  GtkCellRenderer    *renderer,
671                                  const gchar        *attribute,
672                                  gint                column)
673
674   GtkCellAreaPrivate *priv;
675   CellInfo           *info;
676   CellAttribute      *cell_attribute;
677
678   g_return_if_fail (GTK_IS_CELL_AREA (area));
679   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
680   g_return_if_fail (attribute != NULL);
681
682   priv = area->priv;
683   info = g_hash_table_lookup (priv->cell_info, renderer);
684
685   if (!info)
686     {
687       info = cell_info_new (NULL, NULL, NULL);
688
689       g_hash_table_insert (priv->cell_info, renderer, info);
690     }
691   else
692     {
693       GSList *node;
694
695       /* Check we are not adding the same attribute twice */
696       if ((node = g_slist_find_custom (info->attributes, attribute,
697                                        (GCompareFunc)cell_attribute_find)) != NULL)
698         {
699           cell_attribute = node->data;
700
701           g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
702                      "since `%s' is already attributed to column %d", 
703                      attribute,
704                      g_type_name (G_TYPE_FROM_INSTANCE (area)),
705                      attribute, cell_attribute->column);
706           return;
707         }
708     }
709
710   cell_attribute = cell_attribute_new (renderer, attribute, column);
711
712   if (!cell_attribute)
713     {
714       g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
715                  "since attribute does not exist", 
716                  attribute,
717                  g_type_name (G_TYPE_FROM_INSTANCE (area)));
718       return;
719     }
720
721   info->attributes = g_slist_prepend (info->attributes, cell_attribute);
722 }
723
724 void 
725 gtk_cell_area_attribute_disconnect (GtkCellArea        *area,
726                                     GtkCellRenderer    *renderer,
727                                     const gchar        *attribute)
728 {
729   GtkCellAreaPrivate *priv;
730   CellInfo           *info;
731   CellAttribute      *cell_attribute;
732   GSList             *node;
733
734   g_return_if_fail (GTK_IS_CELL_AREA (area));
735   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
736   g_return_if_fail (attribute != NULL);
737
738   priv = area->priv;
739   info = g_hash_table_lookup (priv->cell_info, renderer);
740
741   if (info)
742     {
743       node = g_slist_find_custom (info->attributes, attribute,
744                                   (GCompareFunc)cell_attribute_find);
745       if (node)
746         {
747           cell_attribute = node->data;
748
749           cell_attribute_free (cell_attribute);
750
751           info->attributes = g_slist_delete_link (info->attributes, node);
752         }
753     }
754 }
755
756 static void
757 apply_cell_attributes (GtkCellRenderer *renderer,
758                        CellInfo        *info,
759                        AttributeData   *data)
760 {
761   CellAttribute *attribute;
762   GSList        *list;
763   GValue         value = { 0, };
764
765   /* Apply the attributes directly to the renderer */
766   for (list = info->attributes; list; list = list->next)
767     {
768       attribute = list->data;
769
770       gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value);
771       g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value);
772       g_value_unset (&value);
773     }
774
775   /* Call any GtkCellLayoutDataFunc that may have been set by the user
776    */
777   if (info->func)
778     info->func (GTK_CELL_LAYOUT (data->area), renderer,
779                 data->model, data->iter, info->data);
780 }
781
782 void
783 gtk_cell_area_apply_attributes (GtkCellArea  *area,
784                                 GtkTreeModel *tree_model,
785                                 GtkTreeIter  *iter)
786 {
787   GtkCellAreaPrivate *priv;
788   AttributeData       data;
789
790   g_return_if_fail (GTK_IS_CELL_AREA (area));
791   g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
792   g_return_if_fail (iter != NULL);
793
794   priv = area->priv;
795
796   /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and
797    * apply the data from the treemodel */
798   g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data);
799 }
800
801 /* Cell Properties */
802 void
803 gtk_cell_area_class_install_cell_property (GtkCellAreaClass   *aclass,
804                                            guint               property_id,
805                                            GParamSpec         *pspec)
806 {
807   g_return_if_fail (GTK_IS_CELL_AREA_CLASS (aclass));
808   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
809   if (pspec->flags & G_PARAM_WRITABLE)
810     g_return_if_fail (aclass->set_cell_property != NULL);
811   if (pspec->flags & G_PARAM_READABLE)
812     g_return_if_fail (aclass->get_cell_property != NULL);
813   g_return_if_fail (property_id > 0);
814   g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0);  /* paranoid */
815   g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
816
817   if (g_param_spec_pool_lookup (cell_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (aclass), TRUE))
818     {
819       g_warning (G_STRLOC ": class `%s' already contains a cell property named `%s'",
820                  G_OBJECT_CLASS_NAME (aclass), pspec->name);
821       return;
822     }
823   g_param_spec_ref (pspec);
824   g_param_spec_sink (pspec);
825   PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
826   g_param_spec_pool_insert (cell_property_pool, pspec, G_OBJECT_CLASS_TYPE (aclass));
827 }
828
829 GParamSpec*
830 gtk_cell_area_class_find_cell_property (GtkCellAreaClass   *aclass,
831                                         const gchar        *property_name)
832 {
833   g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
834   g_return_val_if_fail (property_name != NULL, NULL);
835
836   return g_param_spec_pool_lookup (cell_property_pool,
837                                    property_name,
838                                    G_OBJECT_CLASS_TYPE (aclass),
839                                    TRUE);
840 }
841
842 GParamSpec**
843 gtk_cell_area_class_list_cell_properties (GtkCellAreaClass   *aclass,
844                                           guint             *n_properties)
845 {
846   GParamSpec **pspecs;
847   guint n;
848
849   g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
850
851   pspecs = g_param_spec_pool_list (cell_property_pool,
852                                    G_OBJECT_CLASS_TYPE (aclass),
853                                    &n);
854   if (n_properties)
855     *n_properties = n;
856
857   return pspecs;
858 }
859
860 void
861 gtk_cell_area_add_with_properties (GtkCellArea        *area,
862                                    GtkCellRenderer    *renderer,
863                                    const gchar        *first_prop_name,
864                                    ...)
865 {
866   GtkCellAreaClass *class;
867
868   g_return_if_fail (GTK_IS_CELL_AREA (area));
869   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
870
871   class = GTK_CELL_AREA_GET_CLASS (area);
872
873   if (class->add)
874     {
875       va_list var_args;
876
877       class->add (area, renderer);
878
879       va_start (var_args, first_prop_name);
880       gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
881       va_end (var_args);
882     }
883   else
884     g_warning ("GtkCellAreaClass::add not implemented for `%s'", 
885                g_type_name (G_TYPE_FROM_INSTANCE (area)));
886 }
887
888 void
889 gtk_cell_area_cell_set (GtkCellArea        *area,
890                         GtkCellRenderer    *renderer,
891                         const gchar        *first_prop_name,
892                         ...)
893 {
894   va_list var_args;
895
896   g_return_if_fail (GTK_IS_CELL_AREA (area));
897   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
898
899   va_start (var_args, first_prop_name);
900   gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
901   va_end (var_args);
902 }
903
904 void
905 gtk_cell_area_cell_get (GtkCellArea        *area,
906                         GtkCellRenderer    *renderer,
907                         const gchar        *first_prop_name,
908                         ...)
909 {
910   va_list var_args;
911
912   g_return_if_fail (GTK_IS_CELL_AREA (area));
913   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
914
915   va_start (var_args, first_prop_name);
916   gtk_cell_area_cell_get_valist (area, renderer, first_prop_name, var_args);
917   va_end (var_args);
918 }
919
920 static inline void
921 area_get_cell_property (GtkCellArea     *area,
922                         GtkCellRenderer *renderer,
923                         GParamSpec      *pspec,
924                         GValue          *value)
925 {
926   GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
927   
928   class->get_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
929 }
930
931 static inline void
932 area_set_cell_property (GtkCellArea     *area,
933                         GtkCellRenderer *renderer,
934                         GParamSpec      *pspec,
935                         const GValue    *value)
936 {
937   GValue tmp_value = { 0, };
938   GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
939
940   /* provide a copy to work from, convert (if necessary) and validate */
941   g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
942   if (!g_value_transform (value, &tmp_value))
943     g_warning ("unable to set cell property `%s' of type `%s' from value of type `%s'",
944                pspec->name,
945                g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
946                G_VALUE_TYPE_NAME (value));
947   else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
948     {
949       gchar *contents = g_strdup_value_contents (value);
950
951       g_warning ("value \"%s\" of type `%s' is invalid for property `%s' of type `%s'",
952                  contents,
953                  G_VALUE_TYPE_NAME (value),
954                  pspec->name,
955                  g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
956       g_free (contents);
957     }
958   else
959     {
960       class->set_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
961     }
962   g_value_unset (&tmp_value);
963 }
964
965 void
966 gtk_cell_area_cell_set_valist (GtkCellArea        *area,
967                                GtkCellRenderer    *renderer,
968                                const gchar        *first_property_name,
969                                va_list             var_args)
970 {
971   const gchar *name;
972
973   g_return_if_fail (GTK_IS_CELL_AREA (area));
974   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
975
976   name = first_property_name;
977   while (name)
978     {
979       GValue value = { 0, };
980       gchar *error = NULL;
981       GParamSpec *pspec = 
982         g_param_spec_pool_lookup (cell_property_pool, name,
983                                   G_OBJECT_TYPE (area), TRUE);
984       if (!pspec)
985         {
986           g_warning ("%s: cell area class `%s' has no cell property named `%s'",
987                      G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
988           break;
989         }
990       if (!(pspec->flags & G_PARAM_WRITABLE))
991         {
992           g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
993                      G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
994           break;
995         }
996
997       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
998       G_VALUE_COLLECT (&value, var_args, 0, &error);
999       if (error)
1000         {
1001           g_warning ("%s: %s", G_STRLOC, error);
1002           g_free (error);
1003
1004           /* we purposely leak the value here, it might not be
1005            * in a sane state if an error condition occoured
1006            */
1007           break;
1008         }
1009       area_set_cell_property (area, renderer, pspec, &value);
1010       g_value_unset (&value);
1011       name = va_arg (var_args, gchar*);
1012     }
1013 }
1014
1015 void
1016 gtk_cell_area_cell_get_valist (GtkCellArea        *area,
1017                                GtkCellRenderer    *renderer,
1018                                const gchar        *first_property_name,
1019                                va_list             var_args)
1020 {
1021   const gchar *name;
1022
1023   g_return_if_fail (GTK_IS_CELL_AREA (area));
1024   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1025
1026   name = first_property_name;
1027   while (name)
1028     {
1029       GValue value = { 0, };
1030       GParamSpec *pspec;
1031       gchar *error;
1032
1033       pspec = g_param_spec_pool_lookup (cell_property_pool, name,
1034                                         G_OBJECT_TYPE (area), TRUE);
1035       if (!pspec)
1036         {
1037           g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1038                      G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1039           break;
1040         }
1041       if (!(pspec->flags & G_PARAM_READABLE))
1042         {
1043           g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1044                      G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1045           break;
1046         }
1047
1048       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1049       area_get_cell_property (area, renderer, pspec, &value);
1050       G_VALUE_LCOPY (&value, var_args, 0, &error);
1051       if (error)
1052         {
1053           g_warning ("%s: %s", G_STRLOC, error);
1054           g_free (error);
1055           g_value_unset (&value);
1056           break;
1057         }
1058       g_value_unset (&value);
1059       name = va_arg (var_args, gchar*);
1060     }
1061 }
1062
1063 void
1064 gtk_cell_area_cell_set_property (GtkCellArea        *area,
1065                                  GtkCellRenderer    *renderer,
1066                                  const gchar        *property_name,
1067                                  const GValue       *value)
1068 {
1069   GParamSpec *pspec;
1070
1071   g_return_if_fail (GTK_IS_CELL_AREA (area));
1072   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1073   g_return_if_fail (property_name != NULL);
1074   g_return_if_fail (G_IS_VALUE (value));
1075   
1076   pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1077                                     G_OBJECT_TYPE (area), TRUE);
1078   if (!pspec)
1079     g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1080                G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1081   else if (!(pspec->flags & G_PARAM_WRITABLE))
1082     g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1083                G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1084   else
1085     {
1086       area_set_cell_property (area, renderer, pspec, value);
1087     }
1088 }
1089
1090 void
1091 gtk_cell_area_cell_get_property (GtkCellArea        *area,
1092                                  GtkCellRenderer    *renderer,
1093                                  const gchar        *property_name,
1094                                  GValue             *value)
1095 {
1096   GParamSpec *pspec;
1097
1098   g_return_if_fail (GTK_IS_CELL_AREA (area));
1099   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1100   g_return_if_fail (property_name != NULL);
1101   g_return_if_fail (G_IS_VALUE (value));
1102   
1103   pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1104                                     G_OBJECT_TYPE (area), TRUE);
1105   if (!pspec)
1106     g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1107                G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1108   else if (!(pspec->flags & G_PARAM_READABLE))
1109     g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1110                G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1111   else
1112     {
1113       GValue *prop_value, tmp_value = { 0, };
1114
1115       /* auto-conversion of the callers value type
1116        */
1117       if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
1118         {
1119           g_value_reset (value);
1120           prop_value = value;
1121         }
1122       else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
1123         {
1124           g_warning ("can't retrieve cell property `%s' of type `%s' as value of type `%s'",
1125                      pspec->name,
1126                      g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1127                      G_VALUE_TYPE_NAME (value));
1128           return;
1129         }
1130       else
1131         {
1132           g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1133           prop_value = &tmp_value;
1134         }
1135
1136       area_get_cell_property (area, renderer, pspec, prop_value);
1137
1138       if (prop_value != value)
1139         {
1140           g_value_transform (prop_value, value);
1141           g_value_unset (&tmp_value);
1142         }
1143     }
1144 }
1145