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