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