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