]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellarea.c
Added event handling to GtkCellAreaBox
[~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 gboolean  gtk_cell_area_real_can_focus                      (GtkCellArea           *area);
72 static gboolean  gtk_cell_area_real_activate                       (GtkCellArea           *area,
73                                                                     GtkCellAreaIter       *iter,
74                                                                     GtkWidget             *widget,
75                                                                     const GdkRectangle    *cell_area,
76                                                                     GtkCellRendererState   flags);
77
78 /* GtkCellLayoutIface */
79 static void      gtk_cell_area_cell_layout_init              (GtkCellLayoutIface    *iface);
80 static void      gtk_cell_area_pack_default                  (GtkCellLayout         *cell_layout,
81                                                               GtkCellRenderer       *renderer,
82                                                               gboolean               expand);
83 static void      gtk_cell_area_clear                         (GtkCellLayout         *cell_layout);
84 static void      gtk_cell_area_add_attribute                 (GtkCellLayout         *cell_layout,
85                                                               GtkCellRenderer       *renderer,
86                                                               const gchar           *attribute,
87                                                               gint                   column);
88 static void      gtk_cell_area_set_cell_data_func            (GtkCellLayout         *cell_layout,
89                                                               GtkCellRenderer       *cell,
90                                                               GtkCellLayoutDataFunc  func,
91                                                               gpointer               func_data,
92                                                               GDestroyNotify         destroy);
93 static void      gtk_cell_area_clear_attributes              (GtkCellLayout         *cell_layout,
94                                                               GtkCellRenderer       *renderer);
95 static void      gtk_cell_area_reorder                       (GtkCellLayout         *cell_layout,
96                                                               GtkCellRenderer       *cell,
97                                                               gint                   position);
98 static GList    *gtk_cell_area_get_cells                     (GtkCellLayout         *cell_layout);
99
100
101 /* Used in forall loop to check if a child renderer is present */
102 typedef struct {
103   GtkCellRenderer *renderer;
104   gboolean         has_renderer;
105 } HasRendererCheck;
106
107 /* Attribute/Cell metadata */
108 typedef struct {
109   const gchar *attribute;
110   gint         column;
111 } CellAttribute;
112
113 typedef struct {
114   GSList                *attributes;
115
116   GtkCellLayoutDataFunc  func;
117   gpointer               data;
118   GDestroyNotify         destroy;
119 } CellInfo;
120
121 static CellInfo       *cell_info_new       (GtkCellLayoutDataFunc  func,
122                                             gpointer               data,
123                                             GDestroyNotify         destroy);
124 static void            cell_info_free      (CellInfo              *info);
125 static CellAttribute  *cell_attribute_new  (GtkCellRenderer       *renderer,
126                                             const gchar           *attribute,
127                                             gint                   column);
128 static void            cell_attribute_free (CellAttribute         *attribute);
129 static gint            cell_attribute_find (CellAttribute         *cell_attribute,
130                                             const gchar           *attribute);
131
132 /* Internal signal emissions */
133 static void            gtk_cell_area_editing_started  (GtkCellArea        *area,
134                                                        GtkCellRenderer    *renderer,
135                                                        GtkCellEditable    *editable);
136 static void            gtk_cell_area_editing_canceled (GtkCellArea        *area,
137                                                        GtkCellRenderer    *renderer);
138 static void            gtk_cell_area_editing_done     (GtkCellArea        *area,
139                                                        GtkCellRenderer    *renderer,
140                                                        GtkCellEditable    *editable);
141 static void            gtk_cell_area_remove_editable  (GtkCellArea        *area,
142                                                        GtkCellRenderer    *renderer,
143                                                        GtkCellEditable    *editable);
144
145
146 /* Struct to pass data along while looping over 
147  * cell renderers to apply attributes
148  */
149 typedef struct {
150   GtkCellArea  *area;
151   GtkTreeModel *model;
152   GtkTreeIter  *iter;
153   gboolean      is_expander;
154   gboolean      is_expanded;
155 } AttributeData;
156
157 struct _GtkCellAreaPrivate
158 {
159   /* The GtkCellArea bookkeeps any connected 
160    * attributes in this hash table.
161    */
162   GHashTable      *cell_info;
163
164   /* Tracking which cells are focus siblings of focusable cells */
165   GHashTable      *focus_siblings;
166
167   /* The cell border decides how much space to reserve
168    * around each cell for the background_area
169    */
170   GtkBorder        cell_border;
171
172   /* Current path is saved as a side-effect
173    * of gtk_cell_area_apply_attributes() */
174   gchar           *current_path;
175
176   /* Current cell being edited and editable widget used */
177   GtkCellEditable *edit_widget;
178   GtkCellRenderer *edited_cell;
179
180   /* Signal connections to the editable widget */
181   gulong           editing_done_id;
182   gulong           remove_widget_id;
183
184   /* Currently focused cell */
185   GtkCellRenderer *focus_cell;
186 };
187
188 enum {
189   PROP_0,
190   PROP_CELL_MARGIN_LEFT,
191   PROP_CELL_MARGIN_RIGHT,
192   PROP_CELL_MARGIN_TOP,
193   PROP_CELL_MARGIN_BOTTOM,
194   PROP_FOCUS_CELL,
195   PROP_EDITED_CELL,
196   PROP_EDIT_WIDGET
197 };
198
199 enum {
200   SIGNAL_EDITING_STARTED,
201   SIGNAL_EDITING_CANCELED,
202   SIGNAL_EDITING_DONE,
203   SIGNAL_REMOVE_EDITABLE,
204   SIGNAL_FOCUS_CHANGED,
205   LAST_SIGNAL
206 };
207
208 /* Keep the paramspec pool internal, no need to deliver notifications
209  * on cells. at least no percieved need for now */
210 static GParamSpecPool *cell_property_pool = NULL;
211 static guint           cell_area_signals[LAST_SIGNAL] = { 0 };
212
213 #define PARAM_SPEC_PARAM_ID(pspec)              ((pspec)->param_id)
214 #define PARAM_SPEC_SET_PARAM_ID(pspec, id)      ((pspec)->param_id = (id))
215
216
217 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
218                                   G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
219                                                          gtk_cell_area_cell_layout_init));
220
221 static void
222 gtk_cell_area_init (GtkCellArea *area)
223 {
224   GtkCellAreaPrivate *priv;
225
226   area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
227                                             GTK_TYPE_CELL_AREA,
228                                             GtkCellAreaPrivate);
229   priv = area->priv;
230
231   priv->cell_info = g_hash_table_new_full (g_direct_hash, 
232                                            g_direct_equal,
233                                            NULL, 
234                                            (GDestroyNotify)cell_info_free);
235
236   priv->focus_siblings = g_hash_table_new_full (g_direct_hash, 
237                                                 g_direct_equal,
238                                                 NULL, 
239                                                 (GDestroyNotify)g_list_free);
240
241   priv->cell_border.left   = 0;
242   priv->cell_border.right  = 0;
243   priv->cell_border.top    = 0;
244   priv->cell_border.bottom = 0;
245
246   priv->focus_cell         = NULL;
247   priv->edited_cell        = NULL;
248   priv->edit_widget        = NULL;
249
250   priv->editing_done_id    = 0;
251   priv->remove_widget_id   = 0;
252 }
253
254 static void 
255 gtk_cell_area_class_init (GtkCellAreaClass *class)
256 {
257   GObjectClass *object_class = G_OBJECT_CLASS (class);
258   
259   /* GObjectClass */
260   object_class->dispose      = gtk_cell_area_dispose;
261   object_class->finalize     = gtk_cell_area_finalize;
262   object_class->get_property = gtk_cell_area_get_property;
263   object_class->set_property = gtk_cell_area_set_property;
264
265   /* general */
266   class->add     = NULL;
267   class->remove  = NULL;
268   class->forall  = NULL;
269   class->event   = gtk_cell_area_real_event;
270   class->render  = NULL;
271
272   /* geometry */
273   class->create_iter                    = NULL;
274   class->get_request_mode               = NULL;
275   class->get_preferred_width            = NULL;
276   class->get_preferred_height           = NULL;
277   class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
278   class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
279
280   /* focus */
281   class->can_focus  = gtk_cell_area_real_can_focus;
282   class->focus      = NULL;
283   class->activate   = gtk_cell_area_real_activate;
284
285   /* Signals */
286   cell_area_signals[SIGNAL_EDITING_STARTED] =
287     g_signal_new (I_("editing-started"),
288                   G_OBJECT_CLASS_TYPE (object_class),
289                   G_SIGNAL_RUN_FIRST,
290                   0, /* No class closure here */
291                   NULL, NULL,
292                   _gtk_marshal_VOID__OBJECT_OBJECT_STRING,
293                   G_TYPE_NONE, 3,
294                   GTK_TYPE_CELL_RENDERER,
295                   GTK_TYPE_CELL_EDITABLE,
296                   G_TYPE_STRING);
297
298   cell_area_signals[SIGNAL_EDITING_CANCELED] =
299     g_signal_new (I_("editing-canceled"),
300                   G_OBJECT_CLASS_TYPE (object_class),
301                   G_SIGNAL_RUN_FIRST,
302                   0, /* No class closure here */
303                   NULL, NULL,
304                   _gtk_marshal_VOID__OBJECT,
305                   G_TYPE_NONE, 1,
306                   GTK_TYPE_CELL_RENDERER);
307
308   cell_area_signals[SIGNAL_EDITING_DONE] =
309     g_signal_new (I_("editing-done"),
310                   G_OBJECT_CLASS_TYPE (object_class),
311                   G_SIGNAL_RUN_FIRST,
312                   0, /* No class closure here */
313                   NULL, NULL,
314                   _gtk_marshal_VOID__OBJECT_OBJECT,
315                   G_TYPE_NONE, 2,
316                   GTK_TYPE_CELL_RENDERER,
317                   GTK_TYPE_CELL_EDITABLE);
318
319   cell_area_signals[SIGNAL_REMOVE_EDITABLE] =
320     g_signal_new (I_("remove-editable"),
321                   G_OBJECT_CLASS_TYPE (object_class),
322                   G_SIGNAL_RUN_FIRST,
323                   0, /* No class closure here */
324                   NULL, NULL,
325                   _gtk_marshal_VOID__OBJECT_OBJECT,
326                   G_TYPE_NONE, 2,
327                   GTK_TYPE_CELL_RENDERER,
328                   GTK_TYPE_CELL_EDITABLE);
329
330   cell_area_signals[SIGNAL_FOCUS_CHANGED] =
331     g_signal_new (I_("focus-changed"),
332                   G_OBJECT_CLASS_TYPE (object_class),
333                   G_SIGNAL_RUN_FIRST,
334                   0, /* No class closure here */
335                   NULL, NULL,
336                   _gtk_marshal_VOID__OBJECT_STRING,
337                   G_TYPE_NONE, 2,
338                   GTK_TYPE_CELL_RENDERER,
339                   G_TYPE_STRING);
340
341   /* Properties */
342   g_object_class_install_property (object_class,
343                                    PROP_CELL_MARGIN_LEFT,
344                                    g_param_spec_int
345                                    ("cell-margin-left",
346                                     P_("Margin on Left"),
347                                     P_("Pixels of extra space on the left side of each cell"),
348                                     0,
349                                     G_MAXINT16,
350                                     0,
351                                     GTK_PARAM_READWRITE));
352
353   g_object_class_install_property (object_class,
354                                    PROP_CELL_MARGIN_RIGHT,
355                                    g_param_spec_int
356                                    ("cell-margin-right",
357                                     P_("Margin on Right"),
358                                     P_("Pixels of extra space on the right side of each cell"),
359                                     0,
360                                     G_MAXINT16,
361                                     0,
362                                     GTK_PARAM_READWRITE));
363   
364   g_object_class_install_property (object_class,
365                                    PROP_CELL_MARGIN_TOP,
366                                    g_param_spec_int 
367                                    ("cell-margin-top",
368                                     P_("Margin on Top"),
369                                     P_("Pixels of extra space on the top side of each cell"),
370                                     0,
371                                     G_MAXINT16,
372                                     0,
373                                     GTK_PARAM_READWRITE));
374
375   g_object_class_install_property (object_class,
376                                    PROP_CELL_MARGIN_BOTTOM,
377                                    g_param_spec_int
378                                    ("cell-margin-bottom",
379                                     P_("Margin on Bottom"),
380                                     P_("Pixels of extra space on the bottom side of each cell"),
381                                     0,
382                                     G_MAXINT16,
383                                     0,
384                                     GTK_PARAM_READWRITE));
385
386   g_object_class_install_property (object_class,
387                                    PROP_FOCUS_CELL,
388                                    g_param_spec_object
389                                    ("focus-cell",
390                                     P_("Focus Cell"),
391                                     P_("The cell which currently has focus"),
392                                     GTK_TYPE_CELL_RENDERER,
393                                     GTK_PARAM_READWRITE));
394
395   g_object_class_install_property (object_class,
396                                    PROP_EDITED_CELL,
397                                    g_param_spec_object
398                                    ("edited-cell",
399                                     P_("Edited Cell"),
400                                     P_("The cell which is currently being edited"),
401                                     GTK_TYPE_CELL_RENDERER,
402                                     GTK_PARAM_READWRITE));
403
404   g_object_class_install_property (object_class,
405                                    PROP_EDIT_WIDGET,
406                                    g_param_spec_object
407                                    ("edit-widget",
408                                     P_("Edit Widget"),
409                                     P_("The widget currently editing the edited cell"),
410                                     GTK_TYPE_CELL_RENDERER,
411                                     GTK_PARAM_READWRITE));
412
413   /* Pool for Cell Properties */
414   if (!cell_property_pool)
415     cell_property_pool = g_param_spec_pool_new (FALSE);
416
417   g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
418 }
419
420 /*************************************************************
421  *                    CellInfo Basics                        *
422  *************************************************************/
423 static CellInfo *
424 cell_info_new (GtkCellLayoutDataFunc  func,
425                gpointer               data,
426                GDestroyNotify         destroy)
427 {
428   CellInfo *info = g_slice_new (CellInfo);
429   
430   info->attributes = NULL;
431   info->func       = func;
432   info->data       = data;
433   info->destroy    = destroy;
434
435   return info;
436 }
437
438 static void
439 cell_info_free (CellInfo *info)
440 {
441   if (info->destroy)
442     info->destroy (info->data);
443
444   g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
445   g_slist_free (info->attributes);
446
447   g_slice_free (CellInfo, info);
448 }
449
450 static CellAttribute  *
451 cell_attribute_new  (GtkCellRenderer       *renderer,
452                      const gchar           *attribute,
453                      gint                   column)
454 {
455   GParamSpec *pspec;
456
457   /* Check if the attribute really exists and point to
458    * the property string installed on the cell renderer
459    * class (dont dup the string) 
460    */
461   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);
462
463   if (pspec)
464     {
465       CellAttribute *cell_attribute = g_slice_new (CellAttribute);
466
467       cell_attribute->attribute = pspec->name;
468       cell_attribute->column    = column;
469
470       return cell_attribute;
471     }
472
473   return NULL;
474 }
475
476 static void
477 cell_attribute_free (CellAttribute *attribute)
478 {
479   g_slice_free (CellAttribute, attribute);
480 }
481
482 /* GCompareFunc for g_slist_find_custom() */
483 static gint
484 cell_attribute_find (CellAttribute *cell_attribute,
485                      const gchar   *attribute)
486 {
487   return g_strcmp0 (cell_attribute->attribute, attribute);
488 }
489
490 /*************************************************************
491  *                      GObjectClass                         *
492  *************************************************************/
493 static void
494 gtk_cell_area_finalize (GObject *object)
495 {
496   GtkCellArea        *area   = GTK_CELL_AREA (object);
497   GtkCellAreaPrivate *priv   = area->priv;
498
499   /* All cell renderers should already be removed at this point,
500    * just kill our (empty) hash tables here. 
501    */
502   g_hash_table_destroy (priv->cell_info);
503   g_hash_table_destroy (priv->focus_siblings);
504
505   g_free (priv->current_path);
506
507   G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
508 }
509
510
511 static void
512 gtk_cell_area_dispose (GObject *object)
513 {
514   /* This removes every cell renderer that may be added to the GtkCellArea,
515    * subclasses should be breaking references to the GtkCellRenderers 
516    * at this point.
517    */
518   gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
519
520   /* Remove any ref to a focused/edited cell */
521   gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL);
522   gtk_cell_area_set_edited_cell (GTK_CELL_AREA (object), NULL);
523   gtk_cell_area_set_edit_widget (GTK_CELL_AREA (object), NULL);
524
525   G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
526 }
527
528 static void
529 gtk_cell_area_set_property (GObject       *object,
530                             guint          prop_id,
531                             const GValue  *value,
532                             GParamSpec    *pspec)
533 {
534   GtkCellArea *area = GTK_CELL_AREA (object);
535
536   switch (prop_id)
537     {
538     case PROP_CELL_MARGIN_LEFT:
539       gtk_cell_area_set_cell_margin_left (area, g_value_get_int (value));
540       break;
541     case PROP_CELL_MARGIN_RIGHT:
542       gtk_cell_area_set_cell_margin_right (area, g_value_get_int (value));
543       break;
544     case PROP_CELL_MARGIN_TOP:
545       gtk_cell_area_set_cell_margin_top (area, g_value_get_int (value));
546       break;
547     case PROP_CELL_MARGIN_BOTTOM:
548       gtk_cell_area_set_cell_margin_bottom (area, g_value_get_int (value));
549       break;
550     case PROP_FOCUS_CELL:
551       gtk_cell_area_set_focus_cell (area, (GtkCellRenderer *)g_value_get_object (value));
552       break;
553     case PROP_EDITED_CELL:
554       gtk_cell_area_set_edited_cell (area, (GtkCellRenderer *)g_value_get_object (value));
555       break;
556     case PROP_EDIT_WIDGET:
557       gtk_cell_area_set_edit_widget (area, (GtkCellEditable *)g_value_get_object (value));
558       break;
559     default:
560       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
561       break;
562     }
563 }
564
565 static void
566 gtk_cell_area_get_property (GObject     *object,
567                             guint        prop_id,
568                             GValue      *value,
569                             GParamSpec  *pspec)
570 {
571   GtkCellArea        *area = GTK_CELL_AREA (object);
572   GtkCellAreaPrivate *priv = area->priv;
573
574   switch (prop_id)
575     {
576     case PROP_CELL_MARGIN_LEFT:
577       g_value_set_int (value, priv->cell_border.left);
578       break;
579     case PROP_CELL_MARGIN_RIGHT:
580       g_value_set_int (value, priv->cell_border.right);
581       break;
582     case PROP_CELL_MARGIN_TOP:
583       g_value_set_int (value, priv->cell_border.top);
584       break;
585     case PROP_CELL_MARGIN_BOTTOM:
586       g_value_set_int (value, priv->cell_border.bottom);
587       break;
588     case PROP_FOCUS_CELL:
589       g_value_set_object (value, priv->focus_cell);
590       break;
591     case PROP_EDITED_CELL:
592       g_value_set_object (value, priv->edited_cell);
593       break;
594     case PROP_EDIT_WIDGET:
595       g_value_set_object (value, priv->edit_widget);
596       break;
597     default:
598       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
599       break;
600     }
601 }
602
603 /*************************************************************
604  *                    GtkCellAreaClass                       *
605  *************************************************************/
606 static gint
607 gtk_cell_area_real_event (GtkCellArea          *area,
608                           GtkCellAreaIter      *iter,
609                           GtkWidget            *widget,
610                           GdkEvent             *event,
611                           const GdkRectangle   *cell_area,
612                           GtkCellRendererState  flags)
613 {
614   GtkCellAreaPrivate *priv = area->priv;
615
616   if (event->type == GDK_KEY_PRESS && (flags & GTK_CELL_RENDERER_FOCUSED) != 0)
617     {
618       GdkEventKey *key_event = (GdkEventKey *)event;
619
620       /* Cancel any edits in progress */
621       if (priv->edited_cell && (key_event->keyval == GDK_KEY_Escape))
622         {
623           gtk_cell_area_stop_editing (area, TRUE);
624           return TRUE;
625         }
626     }
627
628   return FALSE;
629 }
630
631 static void
632 gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea        *area,
633                                                    GtkCellAreaIter    *iter,
634                                                    GtkWidget          *widget,
635                                                    gint                width,
636                                                    gint               *minimum_height,
637                                                    gint               *natural_height)
638 {
639   /* If the area doesnt do height-for-width, fallback on base preferred height */
640   GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_height, natural_height);
641 }
642
643 static void
644 gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea        *area,
645                                                    GtkCellAreaIter    *iter,
646                                                    GtkWidget          *widget,
647                                                    gint                height,
648                                                    gint               *minimum_width,
649                                                    gint               *natural_width)
650 {
651   /* If the area doesnt do width-for-height, fallback on base preferred width */
652   GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_width, natural_width);
653 }
654
655 static void
656 get_can_focus (GtkCellRenderer *renderer,
657                gboolean        *can_focus)
658 {
659
660   if (gtk_cell_renderer_can_focus (renderer))
661     *can_focus = TRUE;
662 }
663
664 static gboolean
665 gtk_cell_area_real_can_focus (GtkCellArea *area)
666 {
667   gboolean can_focus = FALSE;
668
669   /* Checks if any renderer can focus for the currently applied
670    * attributes.
671    *
672    * Subclasses can override this in the case that they are also
673    * rendering widgets as well as renderers.
674    */
675   gtk_cell_area_forall (area, (GtkCellCallback)get_can_focus, &can_focus);
676
677   return can_focus;
678 }
679
680 static gboolean
681 gtk_cell_area_real_activate (GtkCellArea         *area,
682                              GtkCellAreaIter     *iter,
683                              GtkWidget           *widget,
684                              const GdkRectangle  *cell_area,
685                              GtkCellRendererState flags)
686 {
687   GtkCellAreaPrivate *priv = area->priv;
688   GdkRectangle        background_area;
689
690   if (priv->focus_cell)
691     {
692       /* Get the allocation of the focused cell.
693        */
694       gtk_cell_area_get_cell_allocation (area, iter, widget, priv->focus_cell,
695                                          cell_area, &background_area);
696       
697       /* Activate or Edit the currently focused cell 
698        *
699        * Currently just not sending an event, renderers afaics dont use
700        * the event argument anyway, worst case is we can synthesize one.
701        */
702       if (gtk_cell_area_activate_cell (area, widget, priv->focus_cell, NULL,
703                                        &background_area, flags))
704         return TRUE;
705     }
706
707   return FALSE;
708 }
709
710 /*************************************************************
711  *                   GtkCellLayoutIface                      *
712  *************************************************************/
713 static void
714 gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
715 {
716   iface->pack_start         = gtk_cell_area_pack_default;
717   iface->pack_end           = gtk_cell_area_pack_default;
718   iface->clear              = gtk_cell_area_clear;
719   iface->add_attribute      = gtk_cell_area_add_attribute;
720   iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
721   iface->clear_attributes   = gtk_cell_area_clear_attributes;
722   iface->reorder            = gtk_cell_area_reorder;
723   iface->get_cells          = gtk_cell_area_get_cells;
724 }
725
726 static void
727 gtk_cell_area_pack_default (GtkCellLayout         *cell_layout,
728                             GtkCellRenderer       *renderer,
729                             gboolean               expand)
730 {
731   gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer);
732 }
733
734 static void
735 gtk_cell_area_clear (GtkCellLayout *cell_layout)
736 {
737   GtkCellArea *area = GTK_CELL_AREA (cell_layout);
738   GList *l, *cells  =
739     gtk_cell_layout_get_cells (cell_layout);
740
741   for (l = cells; l; l = l->next)
742     {
743       GtkCellRenderer *renderer = l->data;
744       gtk_cell_area_remove (area, renderer);
745     }
746
747   g_list_free (cells);
748 }
749
750 static void
751 gtk_cell_area_add_attribute (GtkCellLayout         *cell_layout,
752                              GtkCellRenderer       *renderer,
753                              const gchar           *attribute,
754                              gint                   column)
755 {
756   gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout),
757                                    renderer, attribute, column);
758 }
759
760 static void
761 gtk_cell_area_set_cell_data_func (GtkCellLayout         *cell_layout,
762                                   GtkCellRenderer       *renderer,
763                                   GtkCellLayoutDataFunc  func,
764                                   gpointer               func_data,
765                                   GDestroyNotify         destroy)
766 {
767   GtkCellArea        *area   = GTK_CELL_AREA (cell_layout);
768   GtkCellAreaPrivate *priv   = area->priv;
769   CellInfo           *info;
770
771   info = g_hash_table_lookup (priv->cell_info, renderer);
772
773   if (info)
774     {
775       if (info->destroy && info->data)
776         info->destroy (info->data);
777
778       if (func)
779         {
780           info->func    = func;
781           info->data    = func_data;
782           info->destroy = destroy;
783         }
784       else
785         {
786           info->func    = NULL;
787           info->data    = NULL;
788           info->destroy = NULL;
789         }
790     }
791   else
792     {
793       info = cell_info_new (func, func_data, destroy);
794
795       g_hash_table_insert (priv->cell_info, renderer, info);
796     }
797 }
798
799 static void
800 gtk_cell_area_clear_attributes (GtkCellLayout         *cell_layout,
801                                 GtkCellRenderer       *renderer)
802 {
803   GtkCellArea        *area = GTK_CELL_AREA (cell_layout);
804   GtkCellAreaPrivate *priv = area->priv;
805   CellInfo           *info;
806
807   info = g_hash_table_lookup (priv->cell_info, renderer);
808
809   if (info)
810     {
811       g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
812       g_slist_free (info->attributes);
813
814       info->attributes = NULL;
815     }
816 }
817
818 static void 
819 gtk_cell_area_reorder (GtkCellLayout   *cell_layout,
820                        GtkCellRenderer *cell,
821                        gint             position)
822 {
823   g_warning ("GtkCellLayout::reorder not implemented for `%s'", 
824              g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
825 }
826
827 static void
828 accum_cells (GtkCellRenderer *renderer,
829              GList          **accum)
830 {
831   *accum = g_list_prepend (*accum, renderer);
832 }
833
834 static GList *
835 gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
836 {
837   GList *cells = NULL;
838
839   gtk_cell_area_forall (GTK_CELL_AREA (cell_layout), 
840                         (GtkCellCallback)accum_cells,
841                         &cells);
842
843   return g_list_reverse (cells);
844 }
845
846
847 /*************************************************************
848  *                            API                            *
849  *************************************************************/
850
851 /**
852  * gtk_cell_area_add:
853  * @area: a #GtkCellArea
854  * @renderer: the #GtkCellRenderer to add to @area
855  *
856  * Adds @renderer to @area with the default child cell properties.
857  */
858 void
859 gtk_cell_area_add (GtkCellArea        *area,
860                    GtkCellRenderer    *renderer)
861 {
862   GtkCellAreaClass *class;
863
864   g_return_if_fail (GTK_IS_CELL_AREA (area));
865   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
866
867   class = GTK_CELL_AREA_GET_CLASS (area);
868
869   if (class->add)
870     class->add (area, renderer);
871   else
872     g_warning ("GtkCellAreaClass::add not implemented for `%s'", 
873                g_type_name (G_TYPE_FROM_INSTANCE (area)));
874 }
875
876 /**
877  * gtk_cell_area_remove:
878  * @area: a #GtkCellArea
879  * @renderer: the #GtkCellRenderer to add to @area
880  *
881  * Removes @renderer from @area.
882  */
883 void
884 gtk_cell_area_remove (GtkCellArea        *area,
885                       GtkCellRenderer    *renderer)
886 {
887   GtkCellAreaClass   *class;
888   GtkCellAreaPrivate *priv;
889   GList              *renderers, *l;
890
891   g_return_if_fail (GTK_IS_CELL_AREA (area));
892   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
893
894   class = GTK_CELL_AREA_GET_CLASS (area);
895   priv  = area->priv;
896
897   /* Remove any custom attributes and custom cell data func here first */
898   g_hash_table_remove (priv->cell_info, renderer);
899
900   /* Remove focus siblings of this renderer */
901   g_hash_table_remove (priv->focus_siblings, renderer);
902
903   /* Remove this renderer from any focus renderer's sibling list */ 
904   renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
905
906   for (l = renderers; l; l = l->next)
907     {
908       GtkCellRenderer *focus_renderer = l->data;
909
910       if (gtk_cell_area_is_focus_sibling (area, focus_renderer, renderer))
911         {
912           gtk_cell_area_remove_focus_sibling (area, focus_renderer, renderer);
913           break;
914         }
915     }
916
917   g_list_free (renderers);
918
919   if (class->remove)
920     class->remove (area, renderer);
921   else
922     g_warning ("GtkCellAreaClass::remove not implemented for `%s'", 
923                g_type_name (G_TYPE_FROM_INSTANCE (area)));
924 }
925
926 static void
927 get_has_renderer (GtkCellRenderer  *renderer,
928                   HasRendererCheck *check)
929 {
930   if (renderer == check->renderer)
931     check->has_renderer = TRUE;
932 }
933
934 gboolean
935 gtk_cell_area_has_renderer (GtkCellArea     *area,
936                             GtkCellRenderer *renderer)
937 {
938   HasRendererCheck check = { renderer, FALSE };
939
940   g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
941   g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
942
943   gtk_cell_area_forall (area, (GtkCellCallback)get_has_renderer, &check);
944
945   return check.has_renderer;
946 }
947
948 /**
949  * gtk_cell_area_forall
950  * @area: a #GtkCellArea
951  * @callback: the #GtkCellCallback to call
952  * @callback_data: user provided data pointer
953  *
954  * Calls @callback for every #GtkCellRenderer in @area.
955  */
956 void
957 gtk_cell_area_forall (GtkCellArea        *area,
958                       GtkCellCallback     callback,
959                       gpointer            callback_data)
960 {
961   GtkCellAreaClass *class;
962
963   g_return_if_fail (GTK_IS_CELL_AREA (area));
964   g_return_if_fail (callback != NULL);
965
966   class = GTK_CELL_AREA_GET_CLASS (area);
967
968   if (class->forall)
969     class->forall (area, callback, callback_data);
970   else
971     g_warning ("GtkCellAreaClass::forall not implemented for `%s'", 
972                g_type_name (G_TYPE_FROM_INSTANCE (area)));
973 }
974
975 /**
976  * gtk_cell_area_get_cell_allocation:
977  * @area: a #GtkCellArea
978  * @iter: the #GtkCellAreaIter used to hold sizes for @area.
979  * @widget: the #GtkWidget that @area is rendering on
980  * @renderer: the #GtkCellRenderer to get the allocation for
981  * @cell_area: the whole allocated area for @area in @widget
982  *             for this row
983  * @allocation: where to store the allocation for @renderer
984  *
985  * Derives the allocation of @renderer inside @area if @area
986  * were to be renderered in @cell_area.
987  */
988 void
989 gtk_cell_area_get_cell_allocation (GtkCellArea          *area,
990                                    GtkCellAreaIter      *iter,  
991                                    GtkWidget            *widget,
992                                    GtkCellRenderer      *renderer,
993                                    const GdkRectangle   *cell_area,
994                                    GdkRectangle         *allocation)
995 {
996   GtkCellAreaClass *class;
997
998   g_return_if_fail (GTK_IS_CELL_AREA (area));
999   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
1000   g_return_if_fail (GTK_IS_WIDGET (widget));
1001   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1002   g_return_if_fail (cell_area != NULL);
1003   g_return_if_fail (allocation != NULL);
1004
1005   class = GTK_CELL_AREA_GET_CLASS (area);
1006
1007   if (class->get_cell_allocation)
1008     class->get_cell_allocation (area, iter, widget, renderer, cell_area, allocation);
1009   else
1010     g_warning ("GtkCellAreaClass::get_cell_allocation not implemented for `%s'", 
1011                g_type_name (G_TYPE_FROM_INSTANCE (area)));
1012 }
1013
1014 gint
1015 gtk_cell_area_event (GtkCellArea          *area,
1016                      GtkCellAreaIter      *iter,
1017                      GtkWidget            *widget,
1018                      GdkEvent             *event,
1019                      const GdkRectangle   *cell_area,
1020                      GtkCellRendererState  flags)
1021 {
1022   GtkCellAreaClass *class;
1023
1024   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1025   g_return_val_if_fail (GTK_IS_CELL_AREA_ITER (iter), 0);
1026   g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
1027   g_return_val_if_fail (event != NULL, 0);
1028   g_return_val_if_fail (cell_area != NULL, 0);
1029
1030   class = GTK_CELL_AREA_GET_CLASS (area);
1031
1032   if (class->event)
1033     return class->event (area, iter, widget, event, cell_area, flags);
1034
1035   g_warning ("GtkCellAreaClass::event not implemented for `%s'", 
1036              g_type_name (G_TYPE_FROM_INSTANCE (area)));
1037   return 0;
1038 }
1039
1040 void
1041 gtk_cell_area_render (GtkCellArea          *area,
1042                       GtkCellAreaIter      *iter,
1043                       GtkWidget            *widget,
1044                       cairo_t              *cr,
1045                       const GdkRectangle   *background_area,
1046                       const GdkRectangle   *cell_area,
1047                       GtkCellRendererState  flags,
1048                       gboolean              paint_focus)
1049 {
1050   GtkCellAreaClass *class;
1051
1052   g_return_if_fail (GTK_IS_CELL_AREA (area));
1053   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
1054   g_return_if_fail (GTK_IS_WIDGET (widget));
1055   g_return_if_fail (cr != NULL);
1056   g_return_if_fail (background_area != NULL);
1057   g_return_if_fail (cell_area != NULL);
1058
1059   class = GTK_CELL_AREA_GET_CLASS (area);
1060
1061   if (class->render)
1062     class->render (area, iter, widget, cr, background_area, cell_area, flags, paint_focus);
1063   else
1064     g_warning ("GtkCellAreaClass::render not implemented for `%s'", 
1065                g_type_name (G_TYPE_FROM_INSTANCE (area)));
1066 }
1067
1068 /*************************************************************
1069  *                      API: Geometry                        *
1070  *************************************************************/
1071 GtkCellAreaIter   *
1072 gtk_cell_area_create_iter (GtkCellArea *area)
1073 {
1074   GtkCellAreaClass *class;
1075
1076   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1077
1078   class = GTK_CELL_AREA_GET_CLASS (area);
1079
1080   if (class->create_iter)
1081     return class->create_iter (area);
1082
1083   g_warning ("GtkCellAreaClass::create_iter not implemented for `%s'", 
1084              g_type_name (G_TYPE_FROM_INSTANCE (area)));
1085   
1086   return NULL;
1087 }
1088
1089
1090 GtkSizeRequestMode 
1091 gtk_cell_area_get_request_mode (GtkCellArea *area)
1092 {
1093   GtkCellAreaClass *class;
1094
1095   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 
1096                         GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH);
1097
1098   class = GTK_CELL_AREA_GET_CLASS (area);
1099
1100   if (class->get_request_mode)
1101     return class->get_request_mode (area);
1102
1103   g_warning ("GtkCellAreaClass::get_request_mode not implemented for `%s'", 
1104              g_type_name (G_TYPE_FROM_INSTANCE (area)));
1105   
1106   return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
1107 }
1108
1109 void
1110 gtk_cell_area_get_preferred_width (GtkCellArea        *area,
1111                                    GtkCellAreaIter    *iter,
1112                                    GtkWidget          *widget,
1113                                    gint               *minimum_size,
1114                                    gint               *natural_size)
1115 {
1116   GtkCellAreaClass *class;
1117
1118   g_return_if_fail (GTK_IS_CELL_AREA (area));
1119   g_return_if_fail (GTK_IS_WIDGET (widget));
1120
1121   class = GTK_CELL_AREA_GET_CLASS (area);
1122
1123   if (class->get_preferred_width)
1124     class->get_preferred_width (area, iter, widget, minimum_size, natural_size);
1125   else
1126     g_warning ("GtkCellAreaClass::get_preferred_width not implemented for `%s'", 
1127                g_type_name (G_TYPE_FROM_INSTANCE (area)));
1128 }
1129
1130 void
1131 gtk_cell_area_get_preferred_height_for_width (GtkCellArea        *area,
1132                                               GtkCellAreaIter    *iter,
1133                                               GtkWidget          *widget,
1134                                               gint                width,
1135                                               gint               *minimum_height,
1136                                               gint               *natural_height)
1137 {
1138   GtkCellAreaClass *class;
1139
1140   g_return_if_fail (GTK_IS_CELL_AREA (area));
1141   g_return_if_fail (GTK_IS_WIDGET (widget));
1142
1143   class = GTK_CELL_AREA_GET_CLASS (area);
1144   class->get_preferred_height_for_width (area, iter, widget, width, minimum_height, natural_height);
1145 }
1146
1147 void
1148 gtk_cell_area_get_preferred_height (GtkCellArea        *area,
1149                                     GtkCellAreaIter    *iter,
1150                                     GtkWidget          *widget,
1151                                     gint               *minimum_size,
1152                                     gint               *natural_size)
1153 {
1154   GtkCellAreaClass *class;
1155
1156   g_return_if_fail (GTK_IS_CELL_AREA (area));
1157   g_return_if_fail (GTK_IS_WIDGET (widget));
1158
1159   class = GTK_CELL_AREA_GET_CLASS (area);
1160
1161   if (class->get_preferred_height)
1162     class->get_preferred_height (area, iter, widget, minimum_size, natural_size);
1163   else
1164     g_warning ("GtkCellAreaClass::get_preferred_height not implemented for `%s'", 
1165                g_type_name (G_TYPE_FROM_INSTANCE (area)));
1166 }
1167
1168 void
1169 gtk_cell_area_get_preferred_width_for_height (GtkCellArea        *area,
1170                                               GtkCellAreaIter    *iter,
1171                                               GtkWidget          *widget,
1172                                               gint                height,
1173                                               gint               *minimum_width,
1174                                               gint               *natural_width)
1175 {
1176   GtkCellAreaClass *class;
1177
1178   g_return_if_fail (GTK_IS_CELL_AREA (area));
1179   g_return_if_fail (GTK_IS_WIDGET (widget));
1180
1181   class = GTK_CELL_AREA_GET_CLASS (area);
1182   class->get_preferred_width_for_height (area, iter, widget, height, minimum_width, natural_width);
1183 }
1184
1185 /*************************************************************
1186  *                      API: Attributes                      *
1187  *************************************************************/
1188
1189 /**
1190  * gtk_cell_area_attribute_connect:
1191  * @area: a #GtkCellArea
1192  * @renderer: the #GtkCellRenderer to connect an attribute for
1193  * @attribute: the attribute name
1194  * @column: the #GtkTreeModel column to fetch attribute values from
1195  *
1196  * Connects an @attribute to apply values from @column for the
1197  * #GtkTreeModel in use.
1198  */
1199 void
1200 gtk_cell_area_attribute_connect (GtkCellArea        *area,
1201                                  GtkCellRenderer    *renderer,
1202                                  const gchar        *attribute,
1203                                  gint                column)
1204
1205   GtkCellAreaPrivate *priv;
1206   CellInfo           *info;
1207   CellAttribute      *cell_attribute;
1208
1209   g_return_if_fail (GTK_IS_CELL_AREA (area));
1210   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1211   g_return_if_fail (attribute != NULL);
1212   g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
1213
1214   priv = area->priv;
1215   info = g_hash_table_lookup (priv->cell_info, renderer);
1216
1217   if (!info)
1218     {
1219       info = cell_info_new (NULL, NULL, NULL);
1220
1221       g_hash_table_insert (priv->cell_info, renderer, info);
1222     }
1223   else
1224     {
1225       GSList *node;
1226
1227       /* Check we are not adding the same attribute twice */
1228       if ((node = g_slist_find_custom (info->attributes, attribute,
1229                                        (GCompareFunc)cell_attribute_find)) != NULL)
1230         {
1231           cell_attribute = node->data;
1232
1233           g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
1234                      "since `%s' is already attributed to column %d", 
1235                      attribute,
1236                      g_type_name (G_TYPE_FROM_INSTANCE (area)),
1237                      attribute, cell_attribute->column);
1238           return;
1239         }
1240     }
1241
1242   cell_attribute = cell_attribute_new (renderer, attribute, column);
1243
1244   if (!cell_attribute)
1245     {
1246       g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
1247                  "since attribute does not exist", 
1248                  attribute,
1249                  g_type_name (G_TYPE_FROM_INSTANCE (area)));
1250       return;
1251     }
1252
1253   info->attributes = g_slist_prepend (info->attributes, cell_attribute);
1254 }
1255
1256 /**
1257  * gtk_cell_area_attribute_disconnect:
1258  * @area: a #GtkCellArea
1259  * @renderer: the #GtkCellRenderer to disconnect an attribute for
1260  * @attribute: the attribute name
1261  *
1262  * Disconnects @attribute for the @renderer in @area so that
1263  * attribute will no longer be updated with values from the
1264  * model.
1265  */
1266 void 
1267 gtk_cell_area_attribute_disconnect (GtkCellArea        *area,
1268                                     GtkCellRenderer    *renderer,
1269                                     const gchar        *attribute)
1270 {
1271   GtkCellAreaPrivate *priv;
1272   CellInfo           *info;
1273   CellAttribute      *cell_attribute;
1274   GSList             *node;
1275
1276   g_return_if_fail (GTK_IS_CELL_AREA (area));
1277   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1278   g_return_if_fail (attribute != NULL);
1279   g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
1280
1281   priv = area->priv;
1282   info = g_hash_table_lookup (priv->cell_info, renderer);
1283
1284   if (info)
1285     {
1286       node = g_slist_find_custom (info->attributes, attribute,
1287                                   (GCompareFunc)cell_attribute_find);
1288       if (node)
1289         {
1290           cell_attribute = node->data;
1291
1292           cell_attribute_free (cell_attribute);
1293
1294           info->attributes = g_slist_delete_link (info->attributes, node);
1295         }
1296     }
1297 }
1298
1299 static void
1300 apply_cell_attributes (GtkCellRenderer *renderer,
1301                        CellInfo        *info,
1302                        AttributeData   *data)
1303 {
1304   CellAttribute *attribute;
1305   GSList        *list;
1306   GValue         value = { 0, };
1307   gboolean       is_expander;
1308   gboolean       is_expanded;
1309
1310   g_object_freeze_notify (G_OBJECT (renderer));
1311
1312   /* Whether a row expands or is presently expanded can only be 
1313    * provided by the view (as these states can vary across views
1314    * accessing the same model).
1315    */
1316   g_object_get (renderer, "is-expander", &is_expander, NULL);
1317   if (is_expander != data->is_expander)
1318     g_object_set (renderer, "is-expander", data->is_expander, NULL);
1319   
1320   g_object_get (renderer, "is-expanded", &is_expanded, NULL);
1321   if (is_expanded != data->is_expanded)
1322     g_object_set (renderer, "is-expanded", data->is_expanded, NULL);
1323
1324   /* Apply the attributes directly to the renderer */
1325   for (list = info->attributes; list; list = list->next)
1326     {
1327       attribute = list->data;
1328
1329       gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value);
1330       g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value);
1331       g_value_unset (&value);
1332     }
1333
1334   /* Call any GtkCellLayoutDataFunc that may have been set by the user
1335    */
1336   if (info->func)
1337     info->func (GTK_CELL_LAYOUT (data->area), renderer,
1338                 data->model, data->iter, info->data);
1339
1340   g_object_thaw_notify (G_OBJECT (renderer));
1341 }
1342
1343 /**
1344  * gtk_cell_area_apply_attributes
1345  * @area: a #GtkCellArea
1346  * @tree_model: a #GtkTreeModel to pull values from
1347  * @iter: the #GtkTreeIter in @tree_model to apply values for
1348  * @is_expander: whether @iter has children
1349  * @is_expanded: whether @iter is expanded in the view and
1350  *               children are visible
1351  *
1352  * Applies any connected attributes to the renderers in 
1353  * @area by pulling the values from @tree_model.
1354  */
1355 void
1356 gtk_cell_area_apply_attributes (GtkCellArea  *area,
1357                                 GtkTreeModel *tree_model,
1358                                 GtkTreeIter  *iter,
1359                                 gboolean      is_expander,
1360                                 gboolean      is_expanded)
1361 {
1362   GtkCellAreaPrivate *priv;
1363   AttributeData       data;
1364   GtkTreePath        *path;
1365
1366   g_return_if_fail (GTK_IS_CELL_AREA (area));
1367   g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
1368   g_return_if_fail (iter != NULL);
1369
1370   priv = area->priv;
1371
1372   /* Feed in data needed to apply to every renderer */
1373   data.area        = area;
1374   data.model       = tree_model;
1375   data.iter        = iter;
1376   data.is_expander = is_expander;
1377   data.is_expanded = is_expanded;
1378
1379   /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and
1380    * apply the data from the treemodel */
1381   g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data);
1382
1383   /* Update the currently applied path */
1384   g_free (priv->current_path);
1385   path               = gtk_tree_model_get_path (tree_model, iter);
1386   priv->current_path = gtk_tree_path_to_string (path);
1387   gtk_tree_path_free (path);
1388 }
1389
1390 /**
1391  * gtk_cell_area_get_current_path_string:
1392  * @area: a #GtkCellArea
1393  *
1394  * Gets the current #GtkTreePath string for the currently
1395  * applied #GtkTreeIter, this is implicitly updated when
1396  * gtk_cell_area_apply_attributes() is called and can be
1397  * used to interact with renderers from #GtkCellArea
1398  * subclasses.
1399  */
1400 const gchar *
1401 gtk_cell_area_get_current_path_string (GtkCellArea *area)
1402 {
1403   GtkCellAreaPrivate *priv;
1404
1405   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1406
1407   priv = area->priv;
1408
1409   return priv->current_path;
1410 }
1411
1412
1413 /*************************************************************
1414  *                    API: Cell Properties                   *
1415  *************************************************************/
1416 void
1417 gtk_cell_area_class_install_cell_property (GtkCellAreaClass   *aclass,
1418                                            guint               property_id,
1419                                            GParamSpec         *pspec)
1420 {
1421   g_return_if_fail (GTK_IS_CELL_AREA_CLASS (aclass));
1422   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
1423   if (pspec->flags & G_PARAM_WRITABLE)
1424     g_return_if_fail (aclass->set_cell_property != NULL);
1425   if (pspec->flags & G_PARAM_READABLE)
1426     g_return_if_fail (aclass->get_cell_property != NULL);
1427   g_return_if_fail (property_id > 0);
1428   g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0);  /* paranoid */
1429   g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
1430
1431   if (g_param_spec_pool_lookup (cell_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (aclass), TRUE))
1432     {
1433       g_warning (G_STRLOC ": class `%s' already contains a cell property named `%s'",
1434                  G_OBJECT_CLASS_NAME (aclass), pspec->name);
1435       return;
1436     }
1437   g_param_spec_ref (pspec);
1438   g_param_spec_sink (pspec);
1439   PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
1440   g_param_spec_pool_insert (cell_property_pool, pspec, G_OBJECT_CLASS_TYPE (aclass));
1441 }
1442
1443 GParamSpec*
1444 gtk_cell_area_class_find_cell_property (GtkCellAreaClass   *aclass,
1445                                         const gchar        *property_name)
1446 {
1447   g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1448   g_return_val_if_fail (property_name != NULL, NULL);
1449
1450   return g_param_spec_pool_lookup (cell_property_pool,
1451                                    property_name,
1452                                    G_OBJECT_CLASS_TYPE (aclass),
1453                                    TRUE);
1454 }
1455
1456 GParamSpec**
1457 gtk_cell_area_class_list_cell_properties (GtkCellAreaClass   *aclass,
1458                                           guint             *n_properties)
1459 {
1460   GParamSpec **pspecs;
1461   guint n;
1462
1463   g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1464
1465   pspecs = g_param_spec_pool_list (cell_property_pool,
1466                                    G_OBJECT_CLASS_TYPE (aclass),
1467                                    &n);
1468   if (n_properties)
1469     *n_properties = n;
1470
1471   return pspecs;
1472 }
1473
1474 void
1475 gtk_cell_area_add_with_properties (GtkCellArea        *area,
1476                                    GtkCellRenderer    *renderer,
1477                                    const gchar        *first_prop_name,
1478                                    ...)
1479 {
1480   GtkCellAreaClass *class;
1481
1482   g_return_if_fail (GTK_IS_CELL_AREA (area));
1483   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1484
1485   class = GTK_CELL_AREA_GET_CLASS (area);
1486
1487   if (class->add)
1488     {
1489       va_list var_args;
1490
1491       class->add (area, renderer);
1492
1493       va_start (var_args, first_prop_name);
1494       gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1495       va_end (var_args);
1496     }
1497   else
1498     g_warning ("GtkCellAreaClass::add not implemented for `%s'", 
1499                g_type_name (G_TYPE_FROM_INSTANCE (area)));
1500 }
1501
1502 void
1503 gtk_cell_area_cell_set (GtkCellArea        *area,
1504                         GtkCellRenderer    *renderer,
1505                         const gchar        *first_prop_name,
1506                         ...)
1507 {
1508   va_list var_args;
1509
1510   g_return_if_fail (GTK_IS_CELL_AREA (area));
1511   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1512
1513   va_start (var_args, first_prop_name);
1514   gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1515   va_end (var_args);
1516 }
1517
1518 void
1519 gtk_cell_area_cell_get (GtkCellArea        *area,
1520                         GtkCellRenderer    *renderer,
1521                         const gchar        *first_prop_name,
1522                         ...)
1523 {
1524   va_list var_args;
1525
1526   g_return_if_fail (GTK_IS_CELL_AREA (area));
1527   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1528
1529   va_start (var_args, first_prop_name);
1530   gtk_cell_area_cell_get_valist (area, renderer, first_prop_name, var_args);
1531   va_end (var_args);
1532 }
1533
1534 static inline void
1535 area_get_cell_property (GtkCellArea     *area,
1536                         GtkCellRenderer *renderer,
1537                         GParamSpec      *pspec,
1538                         GValue          *value)
1539 {
1540   GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1541   
1542   class->get_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
1543 }
1544
1545 static inline void
1546 area_set_cell_property (GtkCellArea     *area,
1547                         GtkCellRenderer *renderer,
1548                         GParamSpec      *pspec,
1549                         const GValue    *value)
1550 {
1551   GValue tmp_value = { 0, };
1552   GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1553
1554   /* provide a copy to work from, convert (if necessary) and validate */
1555   g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1556   if (!g_value_transform (value, &tmp_value))
1557     g_warning ("unable to set cell property `%s' of type `%s' from value of type `%s'",
1558                pspec->name,
1559                g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1560                G_VALUE_TYPE_NAME (value));
1561   else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
1562     {
1563       gchar *contents = g_strdup_value_contents (value);
1564
1565       g_warning ("value \"%s\" of type `%s' is invalid for property `%s' of type `%s'",
1566                  contents,
1567                  G_VALUE_TYPE_NAME (value),
1568                  pspec->name,
1569                  g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
1570       g_free (contents);
1571     }
1572   else
1573     {
1574       class->set_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
1575     }
1576   g_value_unset (&tmp_value);
1577 }
1578
1579 void
1580 gtk_cell_area_cell_set_valist (GtkCellArea        *area,
1581                                GtkCellRenderer    *renderer,
1582                                const gchar        *first_property_name,
1583                                va_list             var_args)
1584 {
1585   const gchar *name;
1586
1587   g_return_if_fail (GTK_IS_CELL_AREA (area));
1588   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1589
1590   name = first_property_name;
1591   while (name)
1592     {
1593       GValue value = { 0, };
1594       gchar *error = NULL;
1595       GParamSpec *pspec = 
1596         g_param_spec_pool_lookup (cell_property_pool, name,
1597                                   G_OBJECT_TYPE (area), TRUE);
1598       if (!pspec)
1599         {
1600           g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1601                      G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1602           break;
1603         }
1604       if (!(pspec->flags & G_PARAM_WRITABLE))
1605         {
1606           g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1607                      G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1608           break;
1609         }
1610
1611       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1612       G_VALUE_COLLECT (&value, var_args, 0, &error);
1613       if (error)
1614         {
1615           g_warning ("%s: %s", G_STRLOC, error);
1616           g_free (error);
1617
1618           /* we purposely leak the value here, it might not be
1619            * in a sane state if an error condition occoured
1620            */
1621           break;
1622         }
1623       area_set_cell_property (area, renderer, pspec, &value);
1624       g_value_unset (&value);
1625       name = va_arg (var_args, gchar*);
1626     }
1627 }
1628
1629 void
1630 gtk_cell_area_cell_get_valist (GtkCellArea        *area,
1631                                GtkCellRenderer    *renderer,
1632                                const gchar        *first_property_name,
1633                                va_list             var_args)
1634 {
1635   const gchar *name;
1636
1637   g_return_if_fail (GTK_IS_CELL_AREA (area));
1638   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1639
1640   name = first_property_name;
1641   while (name)
1642     {
1643       GValue value = { 0, };
1644       GParamSpec *pspec;
1645       gchar *error;
1646
1647       pspec = g_param_spec_pool_lookup (cell_property_pool, name,
1648                                         G_OBJECT_TYPE (area), TRUE);
1649       if (!pspec)
1650         {
1651           g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1652                      G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1653           break;
1654         }
1655       if (!(pspec->flags & G_PARAM_READABLE))
1656         {
1657           g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1658                      G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1659           break;
1660         }
1661
1662       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1663       area_get_cell_property (area, renderer, pspec, &value);
1664       G_VALUE_LCOPY (&value, var_args, 0, &error);
1665       if (error)
1666         {
1667           g_warning ("%s: %s", G_STRLOC, error);
1668           g_free (error);
1669           g_value_unset (&value);
1670           break;
1671         }
1672       g_value_unset (&value);
1673       name = va_arg (var_args, gchar*);
1674     }
1675 }
1676
1677 void
1678 gtk_cell_area_cell_set_property (GtkCellArea        *area,
1679                                  GtkCellRenderer    *renderer,
1680                                  const gchar        *property_name,
1681                                  const GValue       *value)
1682 {
1683   GParamSpec *pspec;
1684
1685   g_return_if_fail (GTK_IS_CELL_AREA (area));
1686   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1687   g_return_if_fail (property_name != NULL);
1688   g_return_if_fail (G_IS_VALUE (value));
1689   
1690   pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1691                                     G_OBJECT_TYPE (area), TRUE);
1692   if (!pspec)
1693     g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1694                G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1695   else if (!(pspec->flags & G_PARAM_WRITABLE))
1696     g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1697                G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1698   else
1699     {
1700       area_set_cell_property (area, renderer, pspec, value);
1701     }
1702 }
1703
1704 void
1705 gtk_cell_area_cell_get_property (GtkCellArea        *area,
1706                                  GtkCellRenderer    *renderer,
1707                                  const gchar        *property_name,
1708                                  GValue             *value)
1709 {
1710   GParamSpec *pspec;
1711
1712   g_return_if_fail (GTK_IS_CELL_AREA (area));
1713   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1714   g_return_if_fail (property_name != NULL);
1715   g_return_if_fail (G_IS_VALUE (value));
1716   
1717   pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1718                                     G_OBJECT_TYPE (area), TRUE);
1719   if (!pspec)
1720     g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1721                G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1722   else if (!(pspec->flags & G_PARAM_READABLE))
1723     g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1724                G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1725   else
1726     {
1727       GValue *prop_value, tmp_value = { 0, };
1728
1729       /* auto-conversion of the callers value type
1730        */
1731       if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
1732         {
1733           g_value_reset (value);
1734           prop_value = value;
1735         }
1736       else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
1737         {
1738           g_warning ("can't retrieve cell property `%s' of type `%s' as value of type `%s'",
1739                      pspec->name,
1740                      g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1741                      G_VALUE_TYPE_NAME (value));
1742           return;
1743         }
1744       else
1745         {
1746           g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1747           prop_value = &tmp_value;
1748         }
1749
1750       area_get_cell_property (area, renderer, pspec, prop_value);
1751
1752       if (prop_value != value)
1753         {
1754           g_value_transform (prop_value, value);
1755           g_value_unset (&tmp_value);
1756         }
1757     }
1758 }
1759
1760 /*************************************************************
1761  *                         API: Focus                        *
1762  *************************************************************/
1763
1764 /**
1765  * gtk_cell_area_can_focus:
1766  * @area: a #GtkCellArea
1767  *
1768  * Returns whether the area can receive keyboard focus,
1769  * after applying new attributes to @area.
1770  *
1771  * Returns: whether @area can receive focus.
1772  */
1773 gboolean
1774 gtk_cell_area_can_focus (GtkCellArea *area)
1775 {
1776   g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1777
1778   return GTK_CELL_AREA_GET_CLASS (area)->can_focus (area);
1779 }
1780
1781 /**
1782  * gtk_cell_area_focus:
1783  * @area: a #GtkCellArea
1784  * @direction: the #GtkDirectionType
1785  *
1786  * This should be called by the @area's owning layout widget
1787  * when focus is to be passed to @area, or moved within @area
1788  * for a given @direction and row data.
1789  *
1790  * Implementing #GtkCellArea classes should implement this
1791  * method to receive and navigate focus in it's own way particular
1792  * to how it lays out cells.
1793  *
1794  * Returns: %TRUE if focus remains inside @area as a result of this call.
1795  */
1796 gboolean
1797 gtk_cell_area_focus (GtkCellArea      *area,
1798                      GtkDirectionType  direction)
1799 {
1800   GtkCellAreaClass *class;
1801
1802   g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1803
1804   class = GTK_CELL_AREA_GET_CLASS (area);
1805
1806   if (class->focus)
1807     return class->focus (area, direction);
1808
1809   g_warning ("GtkCellAreaClass::focus not implemented for `%s'", 
1810              g_type_name (G_TYPE_FROM_INSTANCE (area)));
1811
1812   return FALSE;
1813 }
1814
1815 /**
1816  * gtk_cell_area_activate:
1817  * @area: a #GtkCellArea
1818  * @iter: the #GtkCellAreaIter in context with the current row data
1819  * @widget: the #GtkWidget that @area is rendering on
1820  * @cell_area: the size and location of @area relative to @widget's allocation
1821  * @flags: the #GtkCellRendererState flags for @area for this row of data.
1822  *
1823  * Activates @area, usually by activating the currently focused
1824  * cell, however some subclasses which embed widgets in the area
1825  * can also activate a widget if it currently has the focus.
1826  *
1827  * Returns: Whether @area was successfully activated.
1828  */
1829 gboolean
1830 gtk_cell_area_activate (GtkCellArea         *area,
1831                         GtkCellAreaIter     *iter,
1832                         GtkWidget           *widget,
1833                         const GdkRectangle  *cell_area,
1834                         GtkCellRendererState flags)
1835 {
1836   g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1837
1838   return GTK_CELL_AREA_GET_CLASS (area)->activate (area, iter, widget, cell_area, flags);
1839 }
1840
1841
1842 /**
1843  * gtk_cell_area_set_focus_cell:
1844  * @area: a #GtkCellArea
1845  * @focus_cell: the #GtkCellRenderer to give focus to
1846  *
1847  * This is generally called from #GtkCellArea implementations
1848  * either gtk_cell_area_grab_focus() or gtk_cell_area_update_focus()
1849  * is called. It's also up to the #GtkCellArea implementation
1850  * to update the focused cell when receiving events from
1851  * gtk_cell_area_event() appropriately.
1852  */
1853 void
1854 gtk_cell_area_set_focus_cell (GtkCellArea     *area,
1855                               GtkCellRenderer *renderer)
1856 {
1857   GtkCellAreaPrivate *priv;
1858
1859   g_return_if_fail (GTK_IS_CELL_AREA (area));
1860   g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
1861
1862   priv = area->priv;
1863
1864   if (priv->focus_cell != renderer)
1865     {
1866       if (priv->focus_cell)
1867         g_object_unref (priv->focus_cell);
1868
1869       priv->focus_cell = renderer;
1870
1871       if (priv->focus_cell)
1872         g_object_ref (priv->focus_cell);
1873
1874       g_object_notify (G_OBJECT (area), "focus-cell");
1875     }
1876
1877   /* Signal that the current focus renderer for this path changed
1878    * (it may be that the focus cell did not change, but the row
1879    * may have changed so we need to signal it) */
1880   g_signal_emit (area, cell_area_signals[SIGNAL_FOCUS_CHANGED], 0, 
1881                  priv->focus_cell, priv->current_path);
1882
1883 }
1884
1885 /**
1886  * gtk_cell_area_get_focus_cell:
1887  * @area: a #GtkCellArea
1888  *
1889  * Retrieves the currently focused cell for @area
1890  *
1891  * Returns: the currently focused cell in @area.
1892  */
1893 GtkCellRenderer *
1894 gtk_cell_area_get_focus_cell (GtkCellArea *area)
1895 {
1896   GtkCellAreaPrivate *priv;
1897
1898   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1899
1900   priv = area->priv;
1901
1902   return priv->focus_cell;
1903 }
1904
1905
1906 /*************************************************************
1907  *                    API: Focus Siblings                    *
1908  *************************************************************/
1909 void
1910 gtk_cell_area_add_focus_sibling (GtkCellArea     *area,
1911                                  GtkCellRenderer *renderer,
1912                                  GtkCellRenderer *sibling)
1913 {
1914   GtkCellAreaPrivate *priv;
1915   GList              *siblings;
1916
1917   g_return_if_fail (GTK_IS_CELL_AREA (area));
1918   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1919   g_return_if_fail (GTK_IS_CELL_RENDERER (sibling));
1920   g_return_if_fail (renderer != sibling);
1921   g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
1922   g_return_if_fail (gtk_cell_area_has_renderer (area, sibling));
1923   g_return_if_fail (!gtk_cell_area_is_focus_sibling (area, renderer, sibling));
1924
1925   /* XXX We should also check that sibling is not in any other renderer's sibling
1926    * list already, a renderer can be sibling of only one focusable renderer
1927    * at a time.
1928    */
1929
1930   priv = area->priv;
1931
1932   siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
1933
1934   if (siblings)
1935     siblings = g_list_append (siblings, sibling);
1936   else
1937     {
1938       siblings = g_list_append (siblings, sibling);
1939       g_hash_table_insert (priv->focus_siblings, renderer, siblings);
1940     }
1941 }
1942
1943 void
1944 gtk_cell_area_remove_focus_sibling (GtkCellArea     *area,
1945                                     GtkCellRenderer *renderer,
1946                                     GtkCellRenderer *sibling)
1947 {
1948   GtkCellAreaPrivate *priv;
1949   GList              *siblings;
1950
1951   g_return_if_fail (GTK_IS_CELL_AREA (area));
1952   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1953   g_return_if_fail (GTK_IS_CELL_RENDERER (sibling));
1954   g_return_if_fail (gtk_cell_area_is_focus_sibling (area, renderer, sibling));
1955
1956   priv = area->priv;
1957
1958   siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
1959
1960   siblings = g_list_copy (siblings);
1961   siblings = g_list_remove (siblings, sibling);
1962
1963   if (!siblings)
1964     g_hash_table_remove (priv->focus_siblings, renderer);
1965   else
1966     g_hash_table_insert (priv->focus_siblings, renderer, siblings);
1967 }
1968
1969 gboolean
1970 gtk_cell_area_is_focus_sibling (GtkCellArea     *area,
1971                                 GtkCellRenderer *renderer,
1972                                 GtkCellRenderer *sibling)
1973 {
1974   GtkCellAreaPrivate *priv;
1975   GList              *siblings, *l;
1976
1977   g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1978   g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
1979   g_return_val_if_fail (GTK_IS_CELL_RENDERER (sibling), FALSE);
1980
1981   priv = area->priv;
1982
1983   siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
1984
1985   for (l = siblings; l; l = l->next)
1986     {
1987       GtkCellRenderer *a_sibling = l->data;
1988
1989       if (a_sibling == sibling)
1990         return TRUE;
1991     }
1992
1993   return FALSE;
1994 }
1995
1996 const GList *
1997 gtk_cell_area_get_focus_siblings (GtkCellArea     *area,
1998                                   GtkCellRenderer *renderer)
1999 {
2000   GtkCellAreaPrivate *priv;
2001
2002   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
2003   g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), NULL);
2004
2005   priv = area->priv;
2006
2007   return g_hash_table_lookup (priv->focus_siblings, renderer);  
2008 }
2009
2010 GtkCellRenderer *
2011 gtk_cell_area_get_focus_from_sibling (GtkCellArea          *area,
2012                                       GtkCellRenderer      *renderer)
2013 {
2014   GtkCellRenderer *ret_renderer = NULL;
2015   GList           *renderers, *l;
2016
2017   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
2018   g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), NULL);
2019
2020   renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
2021
2022   for (l = renderers; l; l = l->next)
2023     {
2024       GtkCellRenderer *a_renderer = l->data;
2025       const GList     *list;
2026
2027       for (list = gtk_cell_area_get_focus_siblings (area, a_renderer); 
2028            list; list = list->next)
2029         {
2030           GtkCellRenderer *sibling_renderer = list->data;
2031
2032           if (sibling_renderer == renderer)
2033             {
2034               ret_renderer = a_renderer;
2035               break;
2036             }
2037         }
2038     }
2039   g_list_free (renderers);
2040
2041   return ret_renderer;
2042 }
2043
2044 /*************************************************************
2045  *              API: Cell Activation/Editing                 *
2046  *************************************************************/
2047 static void
2048 gtk_cell_area_editing_started (GtkCellArea        *area,
2049                                GtkCellRenderer    *renderer,
2050                                GtkCellEditable    *editable)
2051 {
2052   g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_STARTED], 0, 
2053                  renderer, editable, area->priv->current_path);
2054 }
2055
2056 static void
2057 gtk_cell_area_editing_canceled (GtkCellArea        *area,
2058                                 GtkCellRenderer    *renderer)
2059 {
2060   g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_CANCELED], 0, renderer);
2061 }
2062
2063 static void
2064 gtk_cell_area_editing_done (GtkCellArea        *area,
2065                             GtkCellRenderer    *renderer,
2066                             GtkCellEditable    *editable)
2067 {
2068   g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_DONE], 0, renderer, editable);
2069 }
2070
2071 static void
2072 gtk_cell_area_remove_editable  (GtkCellArea        *area,
2073                                 GtkCellRenderer    *renderer,
2074                                 GtkCellEditable    *editable)
2075 {
2076   g_signal_emit (area, cell_area_signals[SIGNAL_REMOVE_EDITABLE], 0, renderer, editable);
2077 }
2078
2079 static void
2080 cell_area_editing_done_cb (GtkCellEditable *editable,
2081                            GtkCellArea     *area)
2082 {
2083   GtkCellAreaPrivate *priv = area->priv;
2084
2085   g_assert (priv->edit_widget == editable);
2086   g_assert (priv->edited_cell != NULL);
2087
2088   gtk_cell_area_editing_done (area, priv->edited_cell, priv->edit_widget);
2089 }
2090
2091 static void
2092 cell_area_remove_widget_cb (GtkCellEditable *editable,
2093                             GtkCellArea     *area)
2094 {
2095   GtkCellAreaPrivate *priv = area->priv;
2096
2097   g_assert (priv->edit_widget == editable);
2098   g_assert (priv->edited_cell != NULL);
2099
2100   gtk_cell_area_remove_editable (area, priv->edited_cell, priv->edit_widget);
2101
2102   /* Now that we're done with editing the widget and it can be removed,
2103    * remove our references to the widget and disconnect handlers */
2104   gtk_cell_area_set_edited_cell (area, NULL);
2105   gtk_cell_area_set_edit_widget (area, NULL);
2106 }
2107
2108 void
2109 gtk_cell_area_set_edited_cell (GtkCellArea     *area,
2110                                GtkCellRenderer *renderer)
2111 {
2112   GtkCellAreaPrivate *priv;
2113
2114   g_return_if_fail (GTK_IS_CELL_AREA (area));
2115   g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
2116
2117   priv = area->priv;
2118
2119   if (priv->edited_cell != renderer)
2120     {
2121       if (priv->edited_cell)
2122         g_object_unref (priv->edited_cell);
2123
2124       priv->edited_cell = renderer;
2125
2126       if (priv->edited_cell)
2127         g_object_ref (priv->edited_cell);
2128
2129       g_object_notify (G_OBJECT (area), "edited-cell");
2130     }
2131 }
2132
2133 GtkCellRenderer   *
2134 gtk_cell_area_get_edited_cell (GtkCellArea *area)
2135 {
2136   GtkCellAreaPrivate *priv;
2137
2138   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
2139
2140   priv = area->priv;
2141
2142   return priv->edited_cell;
2143 }
2144
2145 void
2146 gtk_cell_area_set_edit_widget (GtkCellArea     *area,
2147                                GtkCellEditable *editable)
2148 {
2149   GtkCellAreaPrivate *priv;
2150
2151   g_return_if_fail (GTK_IS_CELL_AREA (area));
2152   g_return_if_fail (editable == NULL || GTK_IS_CELL_EDITABLE (editable));
2153
2154   priv = area->priv;
2155
2156   if (priv->edit_widget != editable)
2157     {
2158       if (priv->edit_widget)
2159         {
2160           g_signal_handler_disconnect (priv->edit_widget, priv->editing_done_id);
2161           g_signal_handler_disconnect (priv->edit_widget, priv->remove_widget_id);
2162
2163           g_object_unref (priv->edit_widget);
2164         }
2165
2166       priv->edit_widget = editable;
2167
2168       if (priv->edit_widget)
2169         {
2170           priv->editing_done_id =
2171             g_signal_connect (priv->edit_widget, "editing-done",
2172                               G_CALLBACK (cell_area_editing_done_cb), area);
2173           priv->remove_widget_id =
2174             g_signal_connect (priv->edit_widget, "remove-widget",
2175                               G_CALLBACK (cell_area_remove_widget_cb), area);
2176
2177           g_object_ref (priv->edit_widget);
2178         }
2179
2180       g_object_notify (G_OBJECT (area), "edit-widget");
2181     }
2182 }
2183
2184 GtkCellEditable *
2185 gtk_cell_area_get_edit_widget (GtkCellArea *area)
2186 {
2187   GtkCellAreaPrivate *priv;
2188
2189   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
2190
2191   priv = area->priv;
2192
2193   return priv->edit_widget;
2194 }
2195
2196 gboolean
2197 gtk_cell_area_activate_cell (GtkCellArea          *area,
2198                              GtkWidget            *widget,
2199                              GtkCellRenderer      *renderer,
2200                              GdkEvent             *event,
2201                              const GdkRectangle   *cell_area,
2202                              GtkCellRendererState  flags)
2203 {
2204   GtkCellRendererMode mode;
2205   GdkRectangle        inner_area;
2206   GtkCellAreaPrivate *priv;
2207   
2208   g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
2209   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
2210   g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
2211   g_return_val_if_fail (cell_area != NULL, FALSE);
2212
2213   priv = area->priv;
2214
2215   /* Remove margins from the background area to produce the cell area.
2216    *
2217    * XXX Maybe have to do some rtl mode treatment here...
2218    */
2219   gtk_cell_area_inner_cell_area (area, cell_area, &inner_area);
2220
2221   g_object_get (renderer, "mode", &mode, NULL);
2222
2223   if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
2224     {
2225       if (gtk_cell_renderer_activate (renderer,
2226                                       event, widget,
2227                                       priv->current_path,
2228                                       cell_area,
2229                                       &inner_area,
2230                                       flags))
2231         return TRUE;
2232     }
2233   else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2234     {
2235       GtkCellEditable *editable_widget;
2236       
2237       editable_widget =
2238         gtk_cell_renderer_start_editing (renderer,
2239                                          event, widget,
2240                                          priv->current_path,
2241                                          cell_area,
2242                                          &inner_area,
2243                                          flags);
2244       
2245       if (editable_widget != NULL)
2246         {
2247           g_return_val_if_fail (GTK_IS_CELL_EDITABLE (editable_widget), FALSE);
2248           
2249           gtk_cell_area_set_edited_cell (area, renderer);
2250           gtk_cell_area_set_edit_widget (area, editable_widget);
2251           
2252           /* Signal that editing started so that callers can get 
2253            * a handle on the editable_widget */
2254           gtk_cell_area_editing_started (area, priv->focus_cell, editable_widget);
2255           
2256           return TRUE;
2257         }
2258     }
2259
2260   return FALSE;
2261 }
2262
2263 void
2264 gtk_cell_area_stop_editing (GtkCellArea *area,
2265                             gboolean     canceled)
2266 {
2267   GtkCellAreaPrivate *priv;
2268
2269   g_return_if_fail (GTK_IS_CELL_AREA (area));
2270
2271   priv = area->priv;
2272
2273   if (priv->edited_cell)
2274     {
2275       /* Stop editing of the cell renderer */
2276       gtk_cell_renderer_stop_editing (priv->edited_cell, canceled);
2277       
2278       /* Signal that editing has been canceled */
2279       if (canceled)
2280         gtk_cell_area_editing_canceled (area, priv->edited_cell);       
2281       
2282       /* Remove any references to the editable widget */
2283       gtk_cell_area_set_edited_cell (area, NULL);
2284       gtk_cell_area_set_edit_widget (area, NULL);
2285     }
2286 }
2287
2288 /*************************************************************
2289  *                        API: Margins                       *
2290  *************************************************************/
2291 gint
2292 gtk_cell_area_get_cell_margin_left (GtkCellArea *area)
2293 {
2294   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2295
2296   return area->priv->cell_border.left;
2297 }
2298
2299 void
2300 gtk_cell_area_set_cell_margin_left (GtkCellArea *area,
2301                                     gint         margin)
2302 {
2303   GtkCellAreaPrivate *priv;
2304
2305   g_return_if_fail (GTK_IS_CELL_AREA (area));
2306
2307   priv = area->priv;
2308
2309   if (priv->cell_border.left != margin)
2310     {
2311       priv->cell_border.left = margin;
2312
2313       g_object_notify (G_OBJECT (area), "margin-left");
2314     }
2315 }
2316
2317 gint
2318 gtk_cell_area_get_cell_margin_right (GtkCellArea *area)
2319 {
2320   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2321
2322   return area->priv->cell_border.right;
2323 }
2324
2325 void
2326 gtk_cell_area_set_cell_margin_right (GtkCellArea *area,
2327                                      gint         margin)
2328 {
2329   GtkCellAreaPrivate *priv;
2330
2331   g_return_if_fail (GTK_IS_CELL_AREA (area));
2332
2333   priv = area->priv;
2334
2335   if (priv->cell_border.right != margin)
2336     {
2337       priv->cell_border.right = margin;
2338
2339       g_object_notify (G_OBJECT (area), "margin-right");
2340     }
2341 }
2342
2343 gint
2344 gtk_cell_area_get_cell_margin_top (GtkCellArea *area)
2345 {
2346   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2347
2348   return area->priv->cell_border.top;
2349 }
2350
2351 void
2352 gtk_cell_area_set_cell_margin_top (GtkCellArea *area,
2353                                    gint         margin)
2354 {
2355   GtkCellAreaPrivate *priv;
2356
2357   g_return_if_fail (GTK_IS_CELL_AREA (area));
2358
2359   priv = area->priv;
2360
2361   if (priv->cell_border.top != margin)
2362     {
2363       priv->cell_border.top = margin;
2364
2365       g_object_notify (G_OBJECT (area), "margin-top");
2366     }
2367 }
2368
2369 gint
2370 gtk_cell_area_get_cell_margin_bottom (GtkCellArea *area)
2371 {
2372   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2373
2374   return area->priv->cell_border.bottom;
2375 }
2376
2377 void
2378 gtk_cell_area_set_cell_margin_bottom (GtkCellArea *area,
2379                                       gint         margin)
2380 {
2381   GtkCellAreaPrivate *priv;
2382
2383   g_return_if_fail (GTK_IS_CELL_AREA (area));
2384
2385   priv = area->priv;
2386
2387   if (priv->cell_border.bottom != margin)
2388     {
2389       priv->cell_border.bottom = margin;
2390
2391       g_object_notify (G_OBJECT (area), "margin-bottom");
2392     }
2393 }
2394
2395 void
2396 gtk_cell_area_inner_cell_area (GtkCellArea        *area,
2397                                const GdkRectangle *background_area,
2398                                GdkRectangle       *cell_area)
2399 {
2400   GtkCellAreaPrivate *priv;
2401
2402   g_return_if_fail (GTK_IS_CELL_AREA (area));
2403   g_return_if_fail (background_area != NULL);
2404   g_return_if_fail (cell_area != NULL);
2405
2406   priv = area->priv;
2407
2408   *cell_area = *background_area;
2409
2410   cell_area->x      += priv->cell_border.left;
2411   cell_area->width  -= (priv->cell_border.left + priv->cell_border.right);
2412   cell_area->y      += priv->cell_border.top;
2413   cell_area->height -= (priv->cell_border.top + priv->cell_border.bottom);
2414 }
2415
2416 void
2417 gtk_cell_area_request_renderer (GtkCellArea        *area,
2418                                 GtkCellRenderer    *renderer,
2419                                 GtkOrientation      orientation,
2420                                 GtkWidget          *widget,
2421                                 gint                for_size,
2422                                 gint               *minimum_size,
2423                                 gint               *natural_size)
2424 {
2425   GtkCellAreaPrivate *priv;
2426
2427   g_return_if_fail (GTK_IS_CELL_AREA (area));
2428   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
2429   g_return_if_fail (GTK_IS_WIDGET (widget));
2430   g_return_if_fail (minimum_size != NULL);
2431   g_return_if_fail (natural_size != NULL);
2432
2433   priv = area->priv;
2434
2435   if (orientation == GTK_ORIENTATION_HORIZONTAL)
2436     {
2437       if (for_size < 0)
2438           gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size);
2439       else
2440         {
2441           for_size = MAX (0, for_size - (priv->cell_border.top + priv->cell_border.bottom));
2442
2443           gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size, 
2444                                                             minimum_size, natural_size);
2445         }
2446
2447       *minimum_size += (priv->cell_border.left + priv->cell_border.right);
2448       *natural_size += (priv->cell_border.left + priv->cell_border.right);
2449     }
2450   else /* GTK_ORIENTATION_VERTICAL */
2451     {
2452       if (for_size < 0)
2453         gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size);
2454       else
2455         {
2456           for_size = MAX (0, for_size - (priv->cell_border.left + priv->cell_border.right));
2457
2458           gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size, 
2459                                                             minimum_size, natural_size);
2460         }
2461
2462       *minimum_size += (priv->cell_border.top + priv->cell_border.bottom);
2463       *natural_size += (priv->cell_border.top + priv->cell_border.bottom);
2464     }
2465 }