1/* gtkcellareacontext.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 * GtkCellAreaContext:
24 *
25 * Stores geometrical information for a series of rows in a GtkCellArea
26 *
27 * The `GtkCellAreaContext` object is created by a given `GtkCellArea`
28 * implementation via its `GtkCellAreaClass.create_context()` virtual
29 * method and is used to store cell sizes and alignments for a series of
30 * `GtkTreeModel` rows that are requested and rendered in the same context.
31 *
32 * `GtkCellLayout` widgets can create any number of contexts in which to
33 * request and render groups of data rows. However, it’s important that the
34 * same context which was used to request sizes for a given `GtkTreeModel`
35 * row also be used for the same row when calling other `GtkCellArea` APIs
36 * such as gtk_cell_area_render() and gtk_cell_area_event().
37 */
38
39#include "config.h"
40#include "gtkintl.h"
41#include "gtkmarshalers.h"
42#include "gtkcellareacontext.h"
43#include "gtkprivate.h"
44
45/* GObjectClass */
46static void gtk_cell_area_context_dispose (GObject *object);
47static void gtk_cell_area_context_get_property (GObject *object,
48 guint prop_id,
49 GValue *value,
50 GParamSpec *pspec);
51static void gtk_cell_area_context_set_property (GObject *object,
52 guint prop_id,
53 const GValue *value,
54 GParamSpec *pspec);
55
56/* GtkCellAreaContextClass */
57static void gtk_cell_area_context_real_reset (GtkCellAreaContext *context);
58static void gtk_cell_area_context_real_allocate (GtkCellAreaContext *context,
59 int width,
60 int height);
61
62typedef struct _GtkCellAreaContextPrivate GtkCellAreaContextPrivate;
63struct _GtkCellAreaContextPrivate
64{
65 GtkCellArea *cell_area;
66
67 int min_width;
68 int nat_width;
69 int min_height;
70 int nat_height;
71 int alloc_width;
72 int alloc_height;
73};
74
75enum {
76 PROP_0,
77 PROP_CELL_AREA,
78 PROP_MIN_WIDTH,
79 PROP_NAT_WIDTH,
80 PROP_MIN_HEIGHT,
81 PROP_NAT_HEIGHT
82};
83
84G_DEFINE_TYPE_WITH_PRIVATE (GtkCellAreaContext, gtk_cell_area_context, G_TYPE_OBJECT)
85
86static void
87gtk_cell_area_context_init (GtkCellAreaContext *context)
88{
89}
90
91static void
92gtk_cell_area_context_class_init (GtkCellAreaContextClass *class)
93{
94 GObjectClass *object_class = G_OBJECT_CLASS (class);
95
96 /* GObjectClass */
97 object_class->dispose = gtk_cell_area_context_dispose;
98 object_class->get_property = gtk_cell_area_context_get_property;
99 object_class->set_property = gtk_cell_area_context_set_property;
100
101 /* GtkCellAreaContextClass */
102 class->reset = gtk_cell_area_context_real_reset;
103 class->allocate = gtk_cell_area_context_real_allocate;
104
105 /**
106 * GtkCellAreaContext:area:
107 *
108 * The `GtkCellArea` this context was created by
109 */
110 g_object_class_install_property (oclass: object_class,
111 property_id: PROP_CELL_AREA,
112 pspec: g_param_spec_object (name: "area",
113 P_("Area"),
114 P_("The Cell Area this context was created for"),
115 GTK_TYPE_CELL_AREA,
116 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
117
118 /**
119 * GtkCellAreaContext:minimum-width:
120 *
121 * The minimum width for the `GtkCellArea` in this context
122 * for all `GtkTreeModel` rows that this context was requested
123 * for using gtk_cell_area_get_preferred_width().
124 */
125 g_object_class_install_property (oclass: object_class,
126 property_id: PROP_MIN_WIDTH,
127 pspec: g_param_spec_int (name: "minimum-width",
128 P_("Minimum Width"),
129 P_("Minimum cached width"),
130 minimum: -1, G_MAXINT, default_value: -1,
131 GTK_PARAM_READABLE));
132
133 /**
134 * GtkCellAreaContext:natural-width:
135 *
136 * The natural width for the `GtkCellArea` in this context
137 * for all `GtkTreeModel` rows that this context was requested
138 * for using gtk_cell_area_get_preferred_width().
139 */
140 g_object_class_install_property (oclass: object_class,
141 property_id: PROP_NAT_WIDTH,
142 pspec: g_param_spec_int (name: "natural-width",
143 P_("Minimum Width"),
144 P_("Minimum cached width"),
145 minimum: -1, G_MAXINT, default_value: -1,
146 GTK_PARAM_READABLE));
147
148 /**
149 * GtkCellAreaContext:minimum-height:
150 *
151 * The minimum height for the `GtkCellArea` in this context
152 * for all `GtkTreeModel` rows that this context was requested
153 * for using gtk_cell_area_get_preferred_height().
154 */
155 g_object_class_install_property (oclass: object_class,
156 property_id: PROP_MIN_HEIGHT,
157 pspec: g_param_spec_int (name: "minimum-height",
158 P_("Minimum Height"),
159 P_("Minimum cached height"),
160 minimum: -1, G_MAXINT, default_value: -1,
161 GTK_PARAM_READABLE));
162
163 /**
164 * GtkCellAreaContext:natural-height:
165 *
166 * The natural height for the `GtkCellArea` in this context
167 * for all `GtkTreeModel` rows that this context was requested
168 * for using gtk_cell_area_get_preferred_height().
169 */
170 g_object_class_install_property (oclass: object_class,
171 property_id: PROP_NAT_HEIGHT,
172 pspec: g_param_spec_int (name: "natural-height",
173 P_("Minimum Height"),
174 P_("Minimum cached height"),
175 minimum: -1, G_MAXINT, default_value: -1,
176 GTK_PARAM_READABLE));
177}
178
179/*************************************************************
180 * GObjectClass *
181 *************************************************************/
182static void
183gtk_cell_area_context_dispose (GObject *object)
184{
185 GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object);
186 GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (self: context);
187
188 if (priv->cell_area)
189 {
190 g_object_unref (object: priv->cell_area);
191
192 priv->cell_area = NULL;
193 }
194
195 G_OBJECT_CLASS (gtk_cell_area_context_parent_class)->dispose (object);
196}
197
198static void
199gtk_cell_area_context_set_property (GObject *object,
200 guint prop_id,
201 const GValue *value,
202 GParamSpec *pspec)
203{
204 GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object);
205 GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (self: context);
206
207 switch (prop_id)
208 {
209 case PROP_CELL_AREA:
210 priv->cell_area = g_value_dup_object (value);
211 break;
212 default:
213 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
214 break;
215 }
216}
217
218static void
219gtk_cell_area_context_get_property (GObject *object,
220 guint prop_id,
221 GValue *value,
222 GParamSpec *pspec)
223{
224 GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object);
225 GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (self: context);
226
227 switch (prop_id)
228 {
229 case PROP_CELL_AREA:
230 g_value_set_object (value, v_object: priv->cell_area);
231 break;
232 case PROP_MIN_WIDTH:
233 g_value_set_int (value, v_int: priv->min_width);
234 break;
235 case PROP_NAT_WIDTH:
236 g_value_set_int (value, v_int: priv->nat_width);
237 break;
238 case PROP_MIN_HEIGHT:
239 g_value_set_int (value, v_int: priv->min_height);
240 break;
241 case PROP_NAT_HEIGHT:
242 g_value_set_int (value, v_int: priv->nat_height);
243 break;
244 default:
245 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
246 break;
247 }
248}
249
250/*************************************************************
251 * GtkCellAreaContextClass *
252 *************************************************************/
253static void
254gtk_cell_area_context_real_reset (GtkCellAreaContext *context)
255{
256 GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (self: context);
257
258 g_object_freeze_notify (G_OBJECT (context));
259
260 if (priv->min_width != 0)
261 {
262 priv->min_width = 0;
263 g_object_notify (G_OBJECT (context), property_name: "minimum-width");
264 }
265
266 if (priv->nat_width != 0)
267 {
268 priv->nat_width = 0;
269 g_object_notify (G_OBJECT (context), property_name: "natural-width");
270 }
271
272 if (priv->min_height != 0)
273 {
274 priv->min_height = 0;
275 g_object_notify (G_OBJECT (context), property_name: "minimum-height");
276 }
277
278 if (priv->nat_height != 0)
279 {
280 priv->nat_height = 0;
281 g_object_notify (G_OBJECT (context), property_name: "natural-height");
282 }
283
284 priv->alloc_width = 0;
285 priv->alloc_height = 0;
286
287 g_object_thaw_notify (G_OBJECT (context));
288}
289
290static void
291gtk_cell_area_context_real_allocate (GtkCellAreaContext *context,
292 int width,
293 int height)
294{
295 GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (self: context);
296
297 priv->alloc_width = width;
298 priv->alloc_height = height;
299}
300
301/*************************************************************
302 * API *
303 *************************************************************/
304/**
305 * gtk_cell_area_context_get_area:
306 * @context: a `GtkCellAreaContext`
307 *
308 * Fetches the `GtkCellArea` this @context was created by.
309 *
310 * This is generally unneeded by layouting widgets; however,
311 * it is important for the context implementation itself to
312 * fetch information about the area it is being used for.
313 *
314 * For instance at `GtkCellAreaContextClass.allocate()` time
315 * it’s important to know details about any cell spacing
316 * that the `GtkCellArea` is configured with in order to
317 * compute a proper allocation.
318 *
319 * Returns: (transfer none): the `GtkCellArea` this context was created by.
320 */
321GtkCellArea *
322gtk_cell_area_context_get_area (GtkCellAreaContext *context)
323{
324 GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (self: context);
325
326 g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), NULL);
327
328 return priv->cell_area;
329}
330
331/**
332 * gtk_cell_area_context_reset:
333 * @context: a `GtkCellAreaContext`
334 *
335 * Resets any previously cached request and allocation
336 * data.
337 *
338 * When underlying `GtkTreeModel` data changes its
339 * important to reset the context if the content
340 * size is allowed to shrink. If the content size
341 * is only allowed to grow (this is usually an option
342 * for views rendering large data stores as a measure
343 * of optimization), then only the row that changed
344 * or was inserted needs to be (re)requested with
345 * gtk_cell_area_get_preferred_width().
346 *
347 * When the new overall size of the context requires
348 * that the allocated size changes (or whenever this
349 * allocation changes at all), the variable row
350 * sizes need to be re-requested for every row.
351 *
352 * For instance, if the rows are displayed all with
353 * the same width from top to bottom then a change
354 * in the allocated width necessitates a recalculation
355 * of all the displayed row heights using
356 * gtk_cell_area_get_preferred_height_for_width().
357 */
358void
359gtk_cell_area_context_reset (GtkCellAreaContext *context)
360{
361 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
362
363 GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->reset (context);
364}
365
366/**
367 * gtk_cell_area_context_allocate:
368 * @context: a `GtkCellAreaContext`
369 * @width: the allocated width for all `GtkTreeModel` rows rendered
370 * with @context, or -1
371 * @height: the allocated height for all `GtkTreeModel` rows rendered
372 * with @context, or -1
373 *
374 * Allocates a width and/or a height for all rows which are to be
375 * rendered with @context.
376 *
377 * Usually allocation is performed only horizontally or sometimes
378 * vertically since a group of rows are usually rendered side by
379 * side vertically or horizontally and share either the same width
380 * or the same height. Sometimes they are allocated in both horizontal
381 * and vertical orientations producing a homogeneous effect of the
382 * rows. This is generally the case for `GtkTreeView` when
383 * `GtkTreeView:fixed-height-mode` is enabled.
384 */
385void
386gtk_cell_area_context_allocate (GtkCellAreaContext *context,
387 int width,
388 int height)
389{
390 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
391
392 GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->allocate (context, width, height);
393}
394
395/**
396 * gtk_cell_area_context_get_preferred_width:
397 * @context: a `GtkCellAreaContext`
398 * @minimum_width: (out) (optional): location to store the minimum width
399 * @natural_width: (out) (optional): location to store the natural width
400 *
401 * Gets the accumulative preferred width for all rows which have been
402 * requested with this context.
403 *
404 * After gtk_cell_area_context_reset() is called and/or before ever
405 * requesting the size of a `GtkCellArea`, the returned values are 0.
406 */
407void
408gtk_cell_area_context_get_preferred_width (GtkCellAreaContext *context,
409 int *minimum_width,
410 int *natural_width)
411{
412 GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (self: context);
413
414 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
415
416 if (minimum_width)
417 *minimum_width = priv->min_width;
418
419 if (natural_width)
420 *natural_width = priv->nat_width;
421}
422
423/**
424 * gtk_cell_area_context_get_preferred_height:
425 * @context: a `GtkCellAreaContext`
426 * @minimum_height: (out) (optional): location to store the minimum height
427 * @natural_height: (out) (optional): location to store the natural height
428 *
429 * Gets the accumulative preferred height for all rows which have been
430 * requested with this context.
431 *
432 * After gtk_cell_area_context_reset() is called and/or before ever
433 * requesting the size of a `GtkCellArea`, the returned values are 0.
434 */
435void
436gtk_cell_area_context_get_preferred_height (GtkCellAreaContext *context,
437 int *minimum_height,
438 int *natural_height)
439{
440 GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (self: context);
441
442 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
443
444 if (minimum_height)
445 *minimum_height = priv->min_height;
446
447 if (natural_height)
448 *natural_height = priv->nat_height;
449}
450
451/**
452 * gtk_cell_area_context_get_preferred_height_for_width:
453 * @context: a `GtkCellAreaContext`
454 * @width: a proposed width for allocation
455 * @minimum_height: (out) (optional): location to store the minimum height
456 * @natural_height: (out) (optional): location to store the natural height
457 *
458 * Gets the accumulative preferred height for @width for all rows
459 * which have been requested for the same said @width with this context.
460 *
461 * After gtk_cell_area_context_reset() is called and/or before ever
462 * requesting the size of a `GtkCellArea`, the returned values are -1.
463 */
464void
465gtk_cell_area_context_get_preferred_height_for_width (GtkCellAreaContext *context,
466 int width,
467 int *minimum_height,
468 int *natural_height)
469{
470 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
471
472 if (GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_height_for_width)
473 GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_height_for_width (context,
474 width,
475 minimum_height,
476 natural_height);
477}
478
479/**
480 * gtk_cell_area_context_get_preferred_width_for_height:
481 * @context: a `GtkCellAreaContext`
482 * @height: a proposed height for allocation
483 * @minimum_width: (out) (optional): location to store the minimum width
484 * @natural_width: (out) (optional): location to store the natural width
485 *
486 * Gets the accumulative preferred width for @height for all rows which
487 * have been requested for the same said @height with this context.
488 *
489 * After gtk_cell_area_context_reset() is called and/or before ever
490 * requesting the size of a `GtkCellArea`, the returned values are -1.
491 */
492void
493gtk_cell_area_context_get_preferred_width_for_height (GtkCellAreaContext *context,
494 int height,
495 int *minimum_width,
496 int *natural_width)
497{
498 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
499
500 if (GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_width_for_height)
501 GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->get_preferred_width_for_height (context,
502 height,
503 minimum_width,
504 natural_width);
505}
506
507/**
508 * gtk_cell_area_context_get_allocation:
509 * @context: a `GtkCellAreaContext`
510 * @width: (out) (optional): location to store the allocated width
511 * @height: (out) (optional): location to store the allocated height
512 *
513 * Fetches the current allocation size for @context.
514 *
515 * If the context was not allocated in width or height, or if the
516 * context was recently reset with gtk_cell_area_context_reset(),
517 * the returned value will be -1.
518 */
519void
520gtk_cell_area_context_get_allocation (GtkCellAreaContext *context,
521 int *width,
522 int *height)
523{
524 GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (self: context);
525
526 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
527
528 if (width)
529 *width = priv->alloc_width;
530
531 if (height)
532 *height = priv->alloc_height;
533}
534
535/**
536 * gtk_cell_area_context_push_preferred_width:
537 * @context: a `GtkCellAreaContext`
538 * @minimum_width: the proposed new minimum width for @context
539 * @natural_width: the proposed new natural width for @context
540 *
541 * Causes the minimum and/or natural width to grow if the new
542 * proposed sizes exceed the current minimum and natural width.
543 *
544 * This is used by `GtkCellAreaContext` implementations during
545 * the request process over a series of `GtkTreeModel` rows to
546 * progressively push the requested width over a series of
547 * gtk_cell_area_get_preferred_width() requests.
548 */
549void
550gtk_cell_area_context_push_preferred_width (GtkCellAreaContext *context,
551 int minimum_width,
552 int natural_width)
553{
554 GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (self: context);
555
556 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
557
558 g_object_freeze_notify (G_OBJECT (context));
559
560 if (minimum_width > priv->min_width)
561 {
562 priv->min_width = minimum_width;
563
564 g_object_notify (G_OBJECT (context), property_name: "minimum-width");
565 }
566
567 if (natural_width > priv->nat_width)
568 {
569 priv->nat_width = natural_width;
570
571 g_object_notify (G_OBJECT (context), property_name: "natural-width");
572 }
573
574 g_object_thaw_notify (G_OBJECT (context));
575}
576
577/**
578 * gtk_cell_area_context_push_preferred_height:
579 * @context: a `GtkCellAreaContext`
580 * @minimum_height: the proposed new minimum height for @context
581 * @natural_height: the proposed new natural height for @context
582 *
583 * Causes the minimum and/or natural height to grow if the new
584 * proposed sizes exceed the current minimum and natural height.
585 *
586 * This is used by `GtkCellAreaContext` implementations during
587 * the request process over a series of `GtkTreeModel` rows to
588 * progressively push the requested height over a series of
589 * gtk_cell_area_get_preferred_height() requests.
590 */
591void
592gtk_cell_area_context_push_preferred_height (GtkCellAreaContext *context,
593 int minimum_height,
594 int natural_height)
595{
596 GtkCellAreaContextPrivate *priv = gtk_cell_area_context_get_instance_private (self: context);
597
598 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
599
600 g_object_freeze_notify (G_OBJECT (context));
601
602 if (minimum_height > priv->min_height)
603 {
604 priv->min_height = minimum_height;
605
606 g_object_notify (G_OBJECT (context), property_name: "minimum-height");
607 }
608
609 if (natural_height > priv->nat_height)
610 {
611 priv->nat_height = natural_height;
612
613 g_object_notify (G_OBJECT (context), property_name: "natural-height");
614 }
615
616 g_object_thaw_notify (G_OBJECT (context));
617}
618

source code of gtk/gtk/gtkcellareacontext.c