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

source code of gtk/gtk/gtkcellarea.c