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