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