]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellarea.c
Added gtk_cell_area_stop_editing to allow explicit stopping of cell editing.
[~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 void      gtk_cell_area_real_update_focus                   (GtkCellArea           *area);
72
73 /* GtkCellLayoutIface */
74 static void      gtk_cell_area_cell_layout_init              (GtkCellLayoutIface    *iface);
75 static void      gtk_cell_area_pack_default                  (GtkCellLayout         *cell_layout,
76                                                               GtkCellRenderer       *renderer,
77                                                               gboolean               expand);
78 static void      gtk_cell_area_clear                         (GtkCellLayout         *cell_layout);
79 static void      gtk_cell_area_add_attribute                 (GtkCellLayout         *cell_layout,
80                                                               GtkCellRenderer       *renderer,
81                                                               const gchar           *attribute,
82                                                               gint                   column);
83 static void      gtk_cell_area_set_cell_data_func            (GtkCellLayout         *cell_layout,
84                                                               GtkCellRenderer       *cell,
85                                                               GtkCellLayoutDataFunc  func,
86                                                               gpointer               func_data,
87                                                               GDestroyNotify         destroy);
88 static void      gtk_cell_area_clear_attributes              (GtkCellLayout         *cell_layout,
89                                                               GtkCellRenderer       *renderer);
90 static void      gtk_cell_area_reorder                       (GtkCellLayout         *cell_layout,
91                                                               GtkCellRenderer       *cell,
92                                                               gint                   position);
93 static GList    *gtk_cell_area_get_cells                     (GtkCellLayout         *cell_layout);
94
95 /* Attribute/Cell metadata */
96 typedef struct {
97   const gchar *attribute;
98   gint         column;
99 } CellAttribute;
100
101 typedef struct {
102   GSList                *attributes;
103
104   GtkCellLayoutDataFunc  func;
105   gpointer               data;
106   GDestroyNotify         destroy;
107 } CellInfo;
108
109 static CellInfo       *cell_info_new       (GtkCellLayoutDataFunc  func,
110                                             gpointer               data,
111                                             GDestroyNotify         destroy);
112 static void            cell_info_free      (CellInfo              *info);
113 static CellAttribute  *cell_attribute_new  (GtkCellRenderer       *renderer,
114                                             const gchar           *attribute,
115                                             gint                   column);
116 static void            cell_attribute_free (CellAttribute         *attribute);
117 static gint            cell_attribute_find (CellAttribute         *cell_attribute,
118                                             const gchar           *attribute);
119
120 /* Internal signal emissions */
121 static void            gtk_cell_area_editing_started  (GtkCellArea        *area,
122                                                        GtkCellRenderer    *renderer,
123                                                        GtkCellEditable    *editable);
124 static void            gtk_cell_area_editing_canceled (GtkCellArea        *area,
125                                                        GtkCellRenderer    *renderer);
126 static void            gtk_cell_area_editing_done     (GtkCellArea        *area,
127                                                        GtkCellRenderer    *renderer,
128                                                        GtkCellEditable    *editable);
129 static void            gtk_cell_area_remove_editable  (GtkCellArea        *area,
130                                                        GtkCellRenderer    *renderer,
131                                                        GtkCellEditable    *editable);
132
133
134 /* Struct to pass data along while looping over 
135  * cell renderers to apply attributes
136  */
137 typedef struct {
138   GtkCellArea  *area;
139   GtkTreeModel *model;
140   GtkTreeIter  *iter;
141   gboolean      is_expander;
142   gboolean      is_expanded;
143 } AttributeData;
144
145 struct _GtkCellAreaPrivate
146 {
147   /* The GtkCellArea bookkeeps any connected 
148    * attributes in this hash table.
149    */
150   GHashTable      *cell_info;
151
152   /* The cell border decides how much space to reserve
153    * around each cell for the background_area
154    */
155   GtkBorder        cell_border;
156
157   /* Current path is saved as a side-effect
158    * of gtk_cell_area_apply_attributes() */
159   gchar           *current_path;
160
161   /* Current cell being edited and editable widget used */
162   GtkCellEditable *edit_widget;
163   GtkCellRenderer *edited_cell;
164
165   /* Signal connections to the editable widget */
166   gulong           editing_done_id;
167   gulong           remove_widget_id;
168
169   /* Currently focused cell */
170   GtkCellRenderer *focus_cell;
171   guint            can_focus : 1;
172
173 };
174
175 enum {
176   PROP_0,
177   PROP_CELL_MARGIN_LEFT,
178   PROP_CELL_MARGIN_RIGHT,
179   PROP_CELL_MARGIN_TOP,
180   PROP_CELL_MARGIN_BOTTOM,
181   PROP_FOCUS_CELL,
182   PROP_EDITED_CELL,
183   PROP_EDIT_WIDGET
184 };
185
186 enum {
187   SIGNAL_FOCUS_LEAVE,
188   SIGNAL_EDITING_STARTED,
189   SIGNAL_EDITING_CANCELED,
190   SIGNAL_EDITING_DONE,
191   SIGNAL_REMOVE_EDITABLE,
192   LAST_SIGNAL
193 };
194
195 /* Keep the paramspec pool internal, no need to deliver notifications
196  * on cells. at least no percieved need for now */
197 static GParamSpecPool *cell_property_pool = NULL;
198 static guint           cell_area_signals[LAST_SIGNAL] = { 0 };
199
200 #define PARAM_SPEC_PARAM_ID(pspec)              ((pspec)->param_id)
201 #define PARAM_SPEC_SET_PARAM_ID(pspec, id)      ((pspec)->param_id = (id))
202
203
204 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
205                                   G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
206                                                          gtk_cell_area_cell_layout_init));
207
208 static void
209 gtk_cell_area_init (GtkCellArea *area)
210 {
211   GtkCellAreaPrivate *priv;
212
213   area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
214                                             GTK_TYPE_CELL_AREA,
215                                             GtkCellAreaPrivate);
216   priv = area->priv;
217
218   priv->cell_info = g_hash_table_new_full (g_direct_hash, 
219                                            g_direct_equal,
220                                            NULL, 
221                                            (GDestroyNotify)cell_info_free);
222
223   priv->cell_border.left   = 0;
224   priv->cell_border.right  = 0;
225   priv->cell_border.top    = 0;
226   priv->cell_border.bottom = 0;
227
228   priv->focus_cell         = NULL;
229   priv->edited_cell        = NULL;
230   priv->edit_widget        = NULL;
231   priv->can_focus          = FALSE;
232
233   priv->editing_done_id    = 0;
234   priv->remove_widget_id   = 0;
235 }
236
237 static void 
238 gtk_cell_area_class_init (GtkCellAreaClass *class)
239 {
240   GObjectClass *object_class = G_OBJECT_CLASS (class);
241   
242   /* GObjectClass */
243   object_class->dispose      = gtk_cell_area_dispose;
244   object_class->finalize     = gtk_cell_area_finalize;
245   object_class->get_property = gtk_cell_area_get_property;
246   object_class->set_property = gtk_cell_area_set_property;
247
248   /* general */
249   class->add     = NULL;
250   class->remove  = NULL;
251   class->forall  = NULL;
252   class->event   = gtk_cell_area_real_event;
253   class->render  = NULL;
254
255   /* geometry */
256   class->create_iter                    = NULL;
257   class->get_request_mode               = NULL;
258   class->get_preferred_width            = NULL;
259   class->get_preferred_height           = NULL;
260   class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
261   class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
262
263   /* focus */
264   class->grab_focus = NULL;
265   class->update_focus = gtk_cell_area_real_update_focus;
266
267   /* Signals */
268   cell_area_signals[SIGNAL_FOCUS_LEAVE] =
269     g_signal_new (I_("focus-leave"),
270                   G_TYPE_FROM_CLASS (object_class),
271                   G_SIGNAL_RUN_LAST,
272                   0, /* Class offset (just a notification, no class handler) */
273                   NULL, NULL,
274                   _gtk_marshal_VOID__ENUM_STRING,
275                   G_TYPE_NONE, 2,
276                   GTK_TYPE_DIRECTION_TYPE, G_TYPE_STRING);
277
278   cell_area_signals[SIGNAL_EDITING_STARTED] =
279     g_signal_new (I_("editing-started"),
280                   G_OBJECT_CLASS_TYPE (object_class),
281                   G_SIGNAL_RUN_FIRST,
282                   0, /* No class closure here */
283                   NULL, NULL,
284                   _gtk_marshal_VOID__OBJECT_OBJECT_STRING,
285                   G_TYPE_NONE, 3,
286                   GTK_TYPE_CELL_RENDERER,
287                   GTK_TYPE_CELL_EDITABLE,
288                   G_TYPE_STRING);
289
290   cell_area_signals[SIGNAL_EDITING_CANCELED] =
291     g_signal_new (I_("editing-canceled"),
292                   G_OBJECT_CLASS_TYPE (object_class),
293                   G_SIGNAL_RUN_FIRST,
294                   0, /* No class closure here */
295                   NULL, NULL,
296                   _gtk_marshal_VOID__OBJECT,
297                   G_TYPE_NONE, 1,
298                   GTK_TYPE_CELL_RENDERER);
299
300   cell_area_signals[SIGNAL_EDITING_DONE] =
301     g_signal_new (I_("editing-done"),
302                   G_OBJECT_CLASS_TYPE (object_class),
303                   G_SIGNAL_RUN_FIRST,
304                   0, /* No class closure here */
305                   NULL, NULL,
306                   _gtk_marshal_VOID__OBJECT_OBJECT,
307                   G_TYPE_NONE, 2,
308                   GTK_TYPE_CELL_RENDERER,
309                   GTK_TYPE_CELL_EDITABLE);
310
311   cell_area_signals[SIGNAL_REMOVE_EDITABLE] =
312     g_signal_new (I_("remove-editable"),
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   /* Properties */
323   g_object_class_install_property (object_class,
324                                    PROP_CELL_MARGIN_LEFT,
325                                    g_param_spec_int
326                                    ("cell-margin-left",
327                                     P_("Margin on Left"),
328                                     P_("Pixels of extra space on the left side of each cell"),
329                                     0,
330                                     G_MAXINT16,
331                                     0,
332                                     GTK_PARAM_READWRITE));
333
334   g_object_class_install_property (object_class,
335                                    PROP_CELL_MARGIN_RIGHT,
336                                    g_param_spec_int
337                                    ("cell-margin-right",
338                                     P_("Margin on Right"),
339                                     P_("Pixels of extra space on the right side of each cell"),
340                                     0,
341                                     G_MAXINT16,
342                                     0,
343                                     GTK_PARAM_READWRITE));
344   
345   g_object_class_install_property (object_class,
346                                    PROP_CELL_MARGIN_TOP,
347                                    g_param_spec_int 
348                                    ("cell-margin-top",
349                                     P_("Margin on Top"),
350                                     P_("Pixels of extra space on the top 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_BOTTOM,
358                                    g_param_spec_int
359                                    ("cell-margin-bottom",
360                                     P_("Margin on Bottom"),
361                                     P_("Pixels of extra space on the bottom 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_FOCUS_CELL,
369                                    g_param_spec_object
370                                    ("focus-cell",
371                                     P_("Focus Cell"),
372                                     P_("The cell which currently has focus"),
373                                     GTK_TYPE_CELL_RENDERER,
374                                     GTK_PARAM_READWRITE));
375
376   g_object_class_install_property (object_class,
377                                    PROP_EDITED_CELL,
378                                    g_param_spec_object
379                                    ("edited-cell",
380                                     P_("Edited Cell"),
381                                     P_("The cell which is currently being edited"),
382                                     GTK_TYPE_CELL_RENDERER,
383                                     GTK_PARAM_READWRITE));
384
385   g_object_class_install_property (object_class,
386                                    PROP_EDIT_WIDGET,
387                                    g_param_spec_object
388                                    ("edit-widget",
389                                     P_("Edit Widget"),
390                                     P_("The widget currently editing the edited cell"),
391                                     GTK_TYPE_CELL_RENDERER,
392                                     GTK_PARAM_READWRITE));
393
394   /* Pool for Cell Properties */
395   if (!cell_property_pool)
396     cell_property_pool = g_param_spec_pool_new (FALSE);
397
398   g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
399 }
400
401 /*************************************************************
402  *                    CellInfo Basics                        *
403  *************************************************************/
404 static CellInfo *
405 cell_info_new (GtkCellLayoutDataFunc  func,
406                gpointer               data,
407                GDestroyNotify         destroy)
408 {
409   CellInfo *info = g_slice_new (CellInfo);
410   
411   info->attributes = NULL;
412   info->func       = func;
413   info->data       = data;
414   info->destroy    = destroy;
415
416   return info;
417 }
418
419 static void
420 cell_info_free (CellInfo *info)
421 {
422   if (info->destroy)
423     info->destroy (info->data);
424
425   g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
426   g_slist_free (info->attributes);
427
428   g_slice_free (CellInfo, info);
429 }
430
431 static CellAttribute  *
432 cell_attribute_new  (GtkCellRenderer       *renderer,
433                      const gchar           *attribute,
434                      gint                   column)
435 {
436   GParamSpec *pspec;
437
438   /* Check if the attribute really exists and point to
439    * the property string installed on the cell renderer
440    * class (dont dup the string) 
441    */
442   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);
443
444   if (pspec)
445     {
446       CellAttribute *cell_attribute = g_slice_new (CellAttribute);
447
448       cell_attribute->attribute = pspec->name;
449       cell_attribute->column    = column;
450
451       return cell_attribute;
452     }
453
454   return NULL;
455 }
456
457 static void
458 cell_attribute_free (CellAttribute *attribute)
459 {
460   g_slice_free (CellAttribute, attribute);
461 }
462
463 /* GCompareFunc for g_slist_find_custom() */
464 static gint
465 cell_attribute_find (CellAttribute *cell_attribute,
466                      const gchar   *attribute)
467 {
468   return g_strcmp0 (cell_attribute->attribute, attribute);
469 }
470
471 /*************************************************************
472  *                      GObjectClass                         *
473  *************************************************************/
474 static void
475 gtk_cell_area_finalize (GObject *object)
476 {
477   GtkCellArea        *area   = GTK_CELL_AREA (object);
478   GtkCellAreaPrivate *priv   = area->priv;
479
480   /* All cell renderers should already be removed at this point,
481    * just kill our hash table here. 
482    */
483   g_hash_table_destroy (priv->cell_info);
484
485   g_free (priv->current_path);
486
487   G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
488 }
489
490
491 static void
492 gtk_cell_area_dispose (GObject *object)
493 {
494   /* This removes every cell renderer that may be added to the GtkCellArea,
495    * subclasses should be breaking references to the GtkCellRenderers 
496    * at this point.
497    */
498   gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
499
500   /* Remove any ref to a focused/edited cell */
501   gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL);
502   gtk_cell_area_set_edited_cell (GTK_CELL_AREA (object), NULL);
503   gtk_cell_area_set_edit_widget (GTK_CELL_AREA (object), NULL);
504
505   G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
506 }
507
508 static void
509 gtk_cell_area_set_property (GObject       *object,
510                             guint          prop_id,
511                             const GValue  *value,
512                             GParamSpec    *pspec)
513 {
514   GtkCellArea *area = GTK_CELL_AREA (object);
515
516   switch (prop_id)
517     {
518     case PROP_CELL_MARGIN_LEFT:
519       gtk_cell_area_set_cell_margin_left (area, g_value_get_int (value));
520       break;
521     case PROP_CELL_MARGIN_RIGHT:
522       gtk_cell_area_set_cell_margin_right (area, g_value_get_int (value));
523       break;
524     case PROP_CELL_MARGIN_TOP:
525       gtk_cell_area_set_cell_margin_top (area, g_value_get_int (value));
526       break;
527     case PROP_CELL_MARGIN_BOTTOM:
528       gtk_cell_area_set_cell_margin_bottom (area, g_value_get_int (value));
529       break;
530     case PROP_FOCUS_CELL:
531       gtk_cell_area_set_focus_cell (area, (GtkCellRenderer *)g_value_get_object (value));
532       break;
533     case PROP_EDITED_CELL:
534       gtk_cell_area_set_edited_cell (area, (GtkCellRenderer *)g_value_get_object (value));
535       break;
536     case PROP_EDIT_WIDGET:
537       gtk_cell_area_set_edit_widget (area, (GtkCellEditable *)g_value_get_object (value));
538       break;
539     default:
540       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
541       break;
542     }
543 }
544
545 static void
546 gtk_cell_area_get_property (GObject     *object,
547                             guint        prop_id,
548                             GValue      *value,
549                             GParamSpec  *pspec)
550 {
551   GtkCellArea        *area = GTK_CELL_AREA (object);
552   GtkCellAreaPrivate *priv = area->priv;
553
554   switch (prop_id)
555     {
556     case PROP_CELL_MARGIN_LEFT:
557       g_value_set_int (value, priv->cell_border.left);
558       break;
559     case PROP_CELL_MARGIN_RIGHT:
560       g_value_set_int (value, priv->cell_border.right);
561       break;
562     case PROP_CELL_MARGIN_TOP:
563       g_value_set_int (value, priv->cell_border.top);
564       break;
565     case PROP_CELL_MARGIN_BOTTOM:
566       g_value_set_int (value, priv->cell_border.bottom);
567       break;
568     case PROP_FOCUS_CELL:
569       g_value_set_object (value, priv->focus_cell);
570       break;
571     case PROP_EDITED_CELL:
572       g_value_set_object (value, priv->edited_cell);
573       break;
574     case PROP_EDIT_WIDGET:
575       g_value_set_object (value, priv->edit_widget);
576       break;
577     default:
578       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
579       break;
580     }
581 }
582
583 /*************************************************************
584  *                    GtkCellAreaClass                       *
585  *************************************************************/
586 static gint
587 gtk_cell_area_real_event (GtkCellArea          *area,
588                           GtkCellAreaIter      *iter,
589                           GtkWidget            *widget,
590                           GdkEvent             *event,
591                           const GdkRectangle   *cell_area,
592                           GtkCellRendererState  flags)
593 {
594   if (event->type == GDK_KEY_PRESS && (flags & GTK_CELL_RENDERER_FOCUSED) != 0)
595     {
596       GdkEventKey        *key_event = (GdkEventKey *)event;
597       GtkCellAreaPrivate *priv = area->priv;
598
599       if (priv->focus_cell && 
600           (key_event->keyval == GDK_KEY_space ||
601            key_event->keyval == GDK_KEY_KP_Space ||
602            key_event->keyval == GDK_KEY_Return ||
603            key_event->keyval == GDK_KEY_ISO_Enter ||
604            key_event->keyval == GDK_KEY_KP_Enter))
605         {
606           GdkRectangle background_area;
607
608           /* Get the allocation of the focused cell.
609            */
610           gtk_cell_area_get_cell_allocation (area, iter, widget, priv->focus_cell,
611                                              cell_area, &background_area);
612
613           /* Activate or Edit the currently focused cell */
614           if (gtk_cell_area_activate_cell (area, widget, priv->focus_cell, event,
615                                            &background_area, flags))
616             return TRUE;
617         }
618       else if (priv->edited_cell &&
619                (key_event->keyval == GDK_KEY_Escape))
620         {
621           gtk_cell_area_stop_editing (area, TRUE);
622           return TRUE;
623         }
624     }
625
626   return FALSE;
627 }
628
629 static void
630 gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea        *area,
631                                                    GtkCellAreaIter    *iter,
632                                                    GtkWidget          *widget,
633                                                    gint                width,
634                                                    gint               *minimum_height,
635                                                    gint               *natural_height)
636 {
637   /* If the area doesnt do height-for-width, fallback on base preferred height */
638   GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_height, natural_height);
639 }
640
641 static void
642 gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea        *area,
643                                                    GtkCellAreaIter    *iter,
644                                                    GtkWidget          *widget,
645                                                    gint                height,
646                                                    gint               *minimum_width,
647                                                    gint               *natural_width)
648 {
649   /* If the area doesnt do width-for-height, fallback on base preferred width */
650   GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_width, natural_width);
651 }
652
653 static void
654 update_can_focus (GtkCellRenderer *renderer,
655                   gboolean        *can_focus)
656 {
657
658   if (gtk_cell_renderer_can_focus (renderer))
659     *can_focus = TRUE;
660 }
661
662 static void
663 gtk_cell_area_real_update_focus (GtkCellArea *area)
664 {
665   gboolean can_focus = FALSE;
666
667   /* Update the area's can focus flag, if any of the renderers can
668    * focus then the area can focus.
669    *
670    * Subclasses can override this in the case that they are also
671    * rendering widgets as well as renderers.
672    */
673   gtk_cell_area_forall (area, (GtkCellCallback)update_can_focus, &can_focus);
674   gtk_cell_area_set_can_focus (area, can_focus);
675
676   /* Unset the currently focused cell if the area can not receive
677    * focus for the given row data */
678   if (!can_focus)
679     gtk_cell_area_set_focus_cell (area, NULL);
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_grab_focus:
1691  * @area: a #GtkCellArea
1692  * @direction: the #GtkDirectionType from which focus came
1693  *
1694  * This should be called by the @area's owning layout widget
1695  * when focus should be passed to @area for a given row data.
1696  *
1697  * Note that after applying new attributes for @area that
1698  * gtk_cell_area_update_focus() should be called and
1699  * gtk_cell_area_can_focus() should be checked before trying
1700  * to pass focus to @area.
1701  *
1702  * Implementing #GtkCellArea classes should implement this
1703  * method to receive focus in it's own way particular to
1704  * how it lays out cells.
1705  */
1706 void
1707 gtk_cell_area_grab_focus (GtkCellArea      *area,
1708                           GtkDirectionType  direction)
1709 {
1710   GtkCellAreaClass *class;
1711
1712   g_return_if_fail (GTK_IS_CELL_AREA (area));
1713
1714   class = GTK_CELL_AREA_GET_CLASS (area);
1715
1716   if (class->grab_focus)
1717     class->grab_focus (area, direction);
1718   else
1719     g_warning ("GtkCellAreaClass::grab_focus not implemented for `%s'", 
1720                g_type_name (G_TYPE_FROM_INSTANCE (area)));
1721 }
1722
1723 /**
1724  * gtk_cell_area_focus_leave:
1725  * @area: a #GtkCellArea
1726  * @direction: the #GtkDirectionType in which focus
1727  *             is to leave @area
1728  * @path: the current #GtkTreePath string for the 
1729  *        event which was handled by @area
1730  *
1731  * Notifies that focus is to leave @area in the
1732  * given @direction.
1733  *
1734  * This is called by #GtkCellArea implementations upon
1735  * handling a key event that caused focus to leave the
1736  * cell. The resulting signal can be handled by the
1737  * owning layouting widget to decide which new @area
1738  * to pass focus to and from what @direction. Or to
1739  * pass focus along to an entirely new data row.
1740  */
1741 void
1742 gtk_cell_area_focus_leave (GtkCellArea        *area,
1743                            GtkDirectionType    direction,
1744                            const gchar        *path)
1745 {
1746   g_return_if_fail (GTK_IS_CELL_AREA (area));
1747
1748   g_signal_emit (area, cell_area_signals[SIGNAL_FOCUS_LEAVE], 0, direction, path);
1749 }
1750
1751 /**
1752  * gtk_cell_area_update_focus:
1753  * @area: a #GtkCellArea
1754  *
1755  * Updates focus information on @area for a given 
1756  * row of data.
1757  *
1758  * After calling gtk_cell_area_apply_attributes() to
1759  * the @area this method should be called to update
1760  * information about whether the @area can focus and
1761  * which is the cell currently in focus.
1762  */
1763 void
1764 gtk_cell_area_update_focus (GtkCellArea *area)
1765 {
1766   g_return_if_fail (GTK_IS_CELL_AREA (area));
1767
1768   GTK_CELL_AREA_GET_CLASS (area)->update_focus (area);
1769 }
1770
1771 /**
1772  * gtk_cell_area_set_can_focus:
1773  * @area: a #GtkCellArea
1774  * @can_focus: whether @area can receive focus
1775  *
1776  * This is generally called from GtkCellArea::update_focus() 
1777  * implementations to update if the @area can focus after
1778  * applying new row data attributes.
1779  */
1780 void
1781 gtk_cell_area_set_can_focus (GtkCellArea *area,
1782                              gboolean     can_focus)
1783 {
1784   GtkCellAreaPrivate *priv;
1785
1786   g_return_if_fail (GTK_IS_CELL_AREA (area));
1787
1788   priv = area->priv;
1789
1790   if (priv->can_focus != can_focus)
1791     {
1792       priv->can_focus = can_focus;
1793     }
1794 }
1795
1796 /**
1797  * gtk_cell_area_get_can_focus:
1798  * @area: a #GtkCellArea
1799  *
1800  * Returns whether the area can receive keyboard focus,
1801  * after applying new attributes to @area, 
1802  * gtk_cell_area_update_focus() needs to be called before
1803  * calling this method.
1804  *
1805  * Returns: whether @area can receive focus.
1806  */
1807 gboolean
1808 gtk_cell_area_get_can_focus (GtkCellArea *area)
1809 {
1810   GtkCellAreaPrivate *priv;
1811
1812   g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1813
1814   priv = area->priv;
1815
1816   return priv->can_focus;
1817 }
1818
1819
1820 /**
1821  * gtk_cell_area_set_focus_cell:
1822  * @area: a #GtkCellArea
1823  * @focus_cell: the #GtkCellRenderer to give focus to
1824  *
1825  * This is generally called from #GtkCellArea implementations
1826  * either gtk_cell_area_grab_focus() or gtk_cell_area_update_focus()
1827  * is called. It's also up to the #GtkCellArea implementation
1828  * to update the focused cell when receiving events from
1829  * gtk_cell_area_event() appropriately.
1830  */
1831 void
1832 gtk_cell_area_set_focus_cell (GtkCellArea     *area,
1833                               GtkCellRenderer *renderer)
1834 {
1835   GtkCellAreaPrivate *priv;
1836
1837   g_return_if_fail (GTK_IS_CELL_AREA (area));
1838   g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
1839
1840   priv = area->priv;
1841
1842   if (priv->focus_cell != renderer)
1843     {
1844       if (priv->focus_cell)
1845         g_object_unref (priv->focus_cell);
1846
1847       priv->focus_cell = renderer;
1848
1849       if (priv->focus_cell)
1850         g_object_ref (priv->focus_cell);
1851
1852       g_object_notify (G_OBJECT (area), "focus-cell");
1853     }
1854 }
1855
1856 /**
1857  * gtk_cell_area_get_focus_cell:
1858  * @area: a #GtkCellArea
1859  *
1860  * Retrieves the currently focused cell for @area
1861  *
1862  * Returns: the currently focused cell in @area.
1863  */
1864 GtkCellRenderer *
1865 gtk_cell_area_get_focus_cell (GtkCellArea *area)
1866 {
1867   GtkCellAreaPrivate *priv;
1868
1869   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1870
1871   priv = area->priv;
1872
1873   return priv->focus_cell;
1874 }
1875
1876
1877 /*************************************************************
1878  *              API: Cell Activation/Editing                 *
1879  *************************************************************/
1880 static void
1881 gtk_cell_area_editing_started (GtkCellArea        *area,
1882                                GtkCellRenderer    *renderer,
1883                                GtkCellEditable    *editable)
1884 {
1885   g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_STARTED], 0, 
1886                  renderer, editable, area->priv->current_path);
1887 }
1888
1889 static void
1890 gtk_cell_area_editing_canceled (GtkCellArea        *area,
1891                                 GtkCellRenderer    *renderer)
1892 {
1893   g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_CANCELED], 0, renderer);
1894 }
1895
1896 static void
1897 gtk_cell_area_editing_done (GtkCellArea        *area,
1898                             GtkCellRenderer    *renderer,
1899                             GtkCellEditable    *editable)
1900 {
1901   g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_DONE], 0, renderer, editable);
1902 }
1903
1904 static void
1905 gtk_cell_area_remove_editable  (GtkCellArea        *area,
1906                                 GtkCellRenderer    *renderer,
1907                                 GtkCellEditable    *editable)
1908 {
1909   g_signal_emit (area, cell_area_signals[SIGNAL_REMOVE_EDITABLE], 0, renderer, editable);
1910 }
1911
1912 static void
1913 cell_area_editing_done_cb (GtkCellEditable *editable,
1914                            GtkCellArea     *area)
1915 {
1916   GtkCellAreaPrivate *priv = area->priv;
1917
1918   g_assert (priv->edit_widget == editable);
1919   g_assert (priv->edited_cell != NULL);
1920
1921   gtk_cell_area_editing_done (area, priv->edited_cell, priv->edit_widget);
1922 }
1923
1924 static void
1925 cell_area_remove_widget_cb (GtkCellEditable *editable,
1926                             GtkCellArea     *area)
1927 {
1928   GtkCellAreaPrivate *priv = area->priv;
1929
1930   g_assert (priv->edit_widget == editable);
1931   g_assert (priv->edited_cell != NULL);
1932
1933   gtk_cell_area_remove_editable (area, priv->edited_cell, priv->edit_widget);
1934
1935   /* Now that we're done with editing the widget and it can be removed,
1936    * remove our references to the widget and disconnect handlers */
1937   gtk_cell_area_set_edited_cell (area, NULL);
1938   gtk_cell_area_set_edit_widget (area, NULL);
1939 }
1940
1941 void
1942 gtk_cell_area_set_edited_cell (GtkCellArea     *area,
1943                                GtkCellRenderer *renderer)
1944 {
1945   GtkCellAreaPrivate *priv;
1946
1947   g_return_if_fail (GTK_IS_CELL_AREA (area));
1948   g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
1949
1950   priv = area->priv;
1951
1952   if (priv->edited_cell != renderer)
1953     {
1954       if (priv->edited_cell)
1955         g_object_unref (priv->edited_cell);
1956
1957       priv->edited_cell = renderer;
1958
1959       if (priv->edited_cell)
1960         g_object_ref (priv->edited_cell);
1961
1962       g_object_notify (G_OBJECT (area), "edited-cell");
1963     }
1964 }
1965
1966 GtkCellRenderer   *
1967 gtk_cell_area_get_edited_cell (GtkCellArea *area)
1968 {
1969   GtkCellAreaPrivate *priv;
1970
1971   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1972
1973   priv = area->priv;
1974
1975   return priv->edited_cell;
1976 }
1977
1978 void
1979 gtk_cell_area_set_edit_widget (GtkCellArea     *area,
1980                                GtkCellEditable *editable)
1981 {
1982   GtkCellAreaPrivate *priv;
1983
1984   g_return_if_fail (GTK_IS_CELL_AREA (area));
1985   g_return_if_fail (editable == NULL || GTK_IS_CELL_EDITABLE (editable));
1986
1987   priv = area->priv;
1988
1989   if (priv->edit_widget != editable)
1990     {
1991       if (priv->edit_widget)
1992         {
1993           g_signal_handler_disconnect (priv->edit_widget, priv->editing_done_id);
1994           g_signal_handler_disconnect (priv->edit_widget, priv->remove_widget_id);
1995
1996           g_object_unref (priv->edit_widget);
1997         }
1998
1999       priv->edit_widget = editable;
2000
2001       if (priv->edit_widget)
2002         {
2003           priv->editing_done_id =
2004             g_signal_connect (priv->edit_widget, "editing-done",
2005                               G_CALLBACK (cell_area_editing_done_cb), area);
2006           priv->remove_widget_id =
2007             g_signal_connect (priv->edit_widget, "remove-widget",
2008                               G_CALLBACK (cell_area_remove_widget_cb), area);
2009
2010           g_object_ref (priv->edit_widget);
2011         }
2012
2013       g_object_notify (G_OBJECT (area), "edit-widget");
2014     }
2015 }
2016
2017 GtkCellEditable *
2018 gtk_cell_area_get_edit_widget (GtkCellArea *area)
2019 {
2020   GtkCellAreaPrivate *priv;
2021
2022   g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
2023
2024   priv = area->priv;
2025
2026   return priv->edit_widget;
2027 }
2028
2029 gboolean
2030 gtk_cell_area_activate_cell (GtkCellArea          *area,
2031                              GtkWidget            *widget,
2032                              GtkCellRenderer      *renderer,
2033                              GdkEvent             *event,
2034                              const GdkRectangle   *cell_area,
2035                              GtkCellRendererState  flags)
2036 {
2037   GtkCellRendererMode mode;
2038   GdkRectangle        inner_area;
2039   GtkCellAreaPrivate *priv;
2040   
2041   g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
2042   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
2043   g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
2044   g_return_val_if_fail (event != NULL, FALSE);
2045   g_return_val_if_fail (cell_area != NULL, FALSE);
2046
2047   priv = area->priv;
2048
2049   /* Remove margins from the background area to produce the cell area.
2050    *
2051    * XXX Maybe have to do some rtl mode treatment here...
2052    */
2053   gtk_cell_area_inner_cell_area (area, cell_area, &inner_area);
2054
2055   g_object_get (renderer, "mode", &mode, NULL);
2056
2057   if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
2058     {
2059       if (gtk_cell_renderer_activate (renderer,
2060                                       event, widget,
2061                                       priv->current_path,
2062                                       cell_area,
2063                                       &inner_area,
2064                                       flags))
2065         return TRUE;
2066     }
2067   else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2068     {
2069       GtkCellEditable *editable_widget;
2070       
2071       editable_widget =
2072         gtk_cell_renderer_start_editing (renderer,
2073                                          event, widget,
2074                                          priv->current_path,
2075                                          cell_area,
2076                                          &inner_area,
2077                                          flags);
2078       
2079       if (editable_widget != NULL)
2080         {
2081           g_return_val_if_fail (GTK_IS_CELL_EDITABLE (editable_widget), FALSE);
2082           
2083           gtk_cell_area_set_edited_cell (area, renderer);
2084           gtk_cell_area_set_edit_widget (area, editable_widget);
2085           
2086           /* Signal that editing started so that callers can get 
2087            * a handle on the editable_widget */
2088           gtk_cell_area_editing_started (area, priv->focus_cell, editable_widget);
2089           
2090           return TRUE;
2091         }
2092     }
2093
2094   return FALSE;
2095 }
2096
2097 void
2098 gtk_cell_area_stop_editing (GtkCellArea *area,
2099                             gboolean     canceled)
2100 {
2101   GtkCellAreaPrivate *priv;
2102
2103   g_return_if_fail (GTK_IS_CELL_AREA (area));
2104
2105   priv = area->priv;
2106
2107   if (priv->edited_cell)
2108     {
2109       /* Stop editing of the cell renderer */
2110       gtk_cell_renderer_stop_editing (priv->edited_cell, canceled);
2111       
2112       /* Signal that editing has been canceled */
2113       if (canceled)
2114         gtk_cell_area_editing_canceled (area, priv->edited_cell);       
2115       
2116       /* Remove any references to the editable widget */
2117       gtk_cell_area_set_edited_cell (area, NULL);
2118       gtk_cell_area_set_edit_widget (area, NULL);
2119     }
2120 }
2121
2122 /*************************************************************
2123  *                        API: Margins                       *
2124  *************************************************************/
2125 gint
2126 gtk_cell_area_get_cell_margin_left (GtkCellArea *area)
2127 {
2128   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2129
2130   return area->priv->cell_border.left;
2131 }
2132
2133 void
2134 gtk_cell_area_set_cell_margin_left (GtkCellArea *area,
2135                                     gint         margin)
2136 {
2137   GtkCellAreaPrivate *priv;
2138
2139   g_return_if_fail (GTK_IS_CELL_AREA (area));
2140
2141   priv = area->priv;
2142
2143   if (priv->cell_border.left != margin)
2144     {
2145       priv->cell_border.left = margin;
2146
2147       g_object_notify (G_OBJECT (area), "margin-left");
2148     }
2149 }
2150
2151 gint
2152 gtk_cell_area_get_cell_margin_right (GtkCellArea *area)
2153 {
2154   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2155
2156   return area->priv->cell_border.right;
2157 }
2158
2159 void
2160 gtk_cell_area_set_cell_margin_right (GtkCellArea *area,
2161                                      gint         margin)
2162 {
2163   GtkCellAreaPrivate *priv;
2164
2165   g_return_if_fail (GTK_IS_CELL_AREA (area));
2166
2167   priv = area->priv;
2168
2169   if (priv->cell_border.right != margin)
2170     {
2171       priv->cell_border.right = margin;
2172
2173       g_object_notify (G_OBJECT (area), "margin-right");
2174     }
2175 }
2176
2177 gint
2178 gtk_cell_area_get_cell_margin_top (GtkCellArea *area)
2179 {
2180   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2181
2182   return area->priv->cell_border.top;
2183 }
2184
2185 void
2186 gtk_cell_area_set_cell_margin_top (GtkCellArea *area,
2187                                    gint         margin)
2188 {
2189   GtkCellAreaPrivate *priv;
2190
2191   g_return_if_fail (GTK_IS_CELL_AREA (area));
2192
2193   priv = area->priv;
2194
2195   if (priv->cell_border.top != margin)
2196     {
2197       priv->cell_border.top = margin;
2198
2199       g_object_notify (G_OBJECT (area), "margin-top");
2200     }
2201 }
2202
2203 gint
2204 gtk_cell_area_get_cell_margin_bottom (GtkCellArea *area)
2205 {
2206   g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2207
2208   return area->priv->cell_border.bottom;
2209 }
2210
2211 void
2212 gtk_cell_area_set_cell_margin_bottom (GtkCellArea *area,
2213                                       gint         margin)
2214 {
2215   GtkCellAreaPrivate *priv;
2216
2217   g_return_if_fail (GTK_IS_CELL_AREA (area));
2218
2219   priv = area->priv;
2220
2221   if (priv->cell_border.bottom != margin)
2222     {
2223       priv->cell_border.bottom = margin;
2224
2225       g_object_notify (G_OBJECT (area), "margin-bottom");
2226     }
2227 }
2228
2229 void
2230 gtk_cell_area_inner_cell_area (GtkCellArea        *area,
2231                                const GdkRectangle *background_area,
2232                                GdkRectangle       *cell_area)
2233 {
2234   GtkCellAreaPrivate *priv;
2235
2236   g_return_if_fail (GTK_IS_CELL_AREA (area));
2237   g_return_if_fail (background_area != NULL);
2238   g_return_if_fail (cell_area != NULL);
2239
2240   priv = area->priv;
2241
2242   *cell_area = *background_area;
2243
2244   cell_area->x      += priv->cell_border.left;
2245   cell_area->width  -= (priv->cell_border.left + priv->cell_border.right);
2246   cell_area->y      += priv->cell_border.top;
2247   cell_area->height -= (priv->cell_border.top + priv->cell_border.bottom);
2248 }
2249
2250 void
2251 gtk_cell_area_request_renderer (GtkCellArea        *area,
2252                                 GtkCellRenderer    *renderer,
2253                                 GtkOrientation      orientation,
2254                                 GtkWidget          *widget,
2255                                 gint                for_size,
2256                                 gint               *minimum_size,
2257                                 gint               *natural_size)
2258 {
2259   GtkCellAreaPrivate *priv;
2260
2261   g_return_if_fail (GTK_IS_CELL_AREA (area));
2262   g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
2263   g_return_if_fail (GTK_IS_WIDGET (widget));
2264   g_return_if_fail (minimum_size != NULL);
2265   g_return_if_fail (natural_size != NULL);
2266
2267   priv = area->priv;
2268
2269   if (orientation == GTK_ORIENTATION_HORIZONTAL)
2270     {
2271       if (for_size < 0)
2272           gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size);
2273       else
2274         {
2275           for_size = MAX (0, for_size - (priv->cell_border.top + priv->cell_border.bottom));
2276
2277           gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size, 
2278                                                             minimum_size, natural_size);
2279         }
2280
2281       *minimum_size += (priv->cell_border.left + priv->cell_border.right);
2282       *natural_size += (priv->cell_border.left + priv->cell_border.right);
2283     }
2284   else /* GTK_ORIENTATION_VERTICAL */
2285     {
2286       if (for_size < 0)
2287         gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size);
2288       else
2289         {
2290           for_size = MAX (0, for_size - (priv->cell_border.left + priv->cell_border.right));
2291
2292           gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size, 
2293                                                             minimum_size, natural_size);
2294         }
2295
2296       *minimum_size += (priv->cell_border.top + priv->cell_border.bottom);
2297       *natural_size += (priv->cell_border.top + priv->cell_border.bottom);
2298     }
2299 }