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