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