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