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