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