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