]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellareaiter.c
Added GtkCellAreaIter class
[~andy/gtk] / gtk / gtkcellareaiter.c
1 /* gtkcellareaiter.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 #include "config.h"
25 #include "gtkintl.h"
26 #include "gtkmarshalers.h"
27 #include "gtkorientable.h"
28 #include "gtkcelllayout.h"
29 #include "gtkcellareaiter.h"
30
31 /* GObjectClass */
32 static void      gtk_cell_area_iter_finalize                       (GObject            *object);
33 static void      gtk_cell_area_iter_get_property                   (GObject           *object,
34                                                                    guint               prop_id,
35                                                                    GValue             *value,
36                                                                    GParamSpec         *pspec);
37
38 /* GtkCellAreaIterClass */
39 static void      gtk_cell_area_iter_real_flush_preferred_width            (GtkCellAreaIter *iter);
40 static void      gtk_cell_area_iter_real_flush_preferred_height_for_width (GtkCellAreaIter *iter,
41                                                                            gint             width);
42 static void      gtk_cell_area_iter_real_flush_preferred_height           (GtkCellAreaIter *iter);
43 static void      gtk_cell_area_iter_real_flush_preferred_width_for_height (GtkCellAreaIter *iter,
44                                                                            gint             height);
45
46 /* CachedSize management */
47 typedef struct {
48   gint min_size;
49   gint nat_size;
50 } CachedSize;
51
52 static CachedSize *cached_size_new  (gint min_size, gint nat_size);
53 static void        cached_size_free (CachedSize *size);
54
55 struct _GtkCellAreaIterPrivate
56 {
57   gint        min_width;
58   gint        nat_width;
59   gint        min_height;
60   gint        nat_height;
61
62   GHashTable *widths;
63   GHashTable *heights;
64 };
65
66 enum {
67   PROP_0,
68   PROP_MIN_WIDTH,
69   PROP_NAT_WIDTH,
70   PROP_MIN_HEIGHT,
71   PROP_NAT_HEIGHT
72 };
73
74 enum {
75   SIGNAL_WIDTH_CHANGED,
76   SIGNAL_HEIGHT_CHANGED,
77   LAST_SIGNAL
78 };
79
80 static guint cell_area_iter_signals[LAST_SIGNAL] = { 0 };
81
82 G_DEFINE_TYPE (GtkCellAreaIter, gtk_cell_area_iter, G_TYPE_OBJECT);
83
84 static void
85 gtk_cell_area_iter_init (GtkCellAreaIter *iter)
86 {
87   GtkCellAreaIterPrivate *priv;
88
89   iter->priv = G_TYPE_INSTANCE_GET_PRIVATE (iter,
90                                             GTK_TYPE_CELL_AREA_ITER,
91                                             GtkCellAreaIterPrivate);
92   priv = iter->priv;
93
94   priv->min_width  = -1;
95   priv->nat_width  = -1;
96   priv->min_height = -1;
97   priv->nat_height = -1;
98   priv->widths     = g_hash_table_new_full (g_direct_hash, g_direct_equal,
99                                             NULL, (GDestroyNotify)cached_size_free);
100   priv->heights    = g_hash_table_new_full (g_direct_hash, g_direct_equal,
101                                             NULL, (GDestroyNotify)cached_size_free);
102 }
103
104 static void 
105 gtk_cell_area_iter_class_init (GtkCellAreaIterClass *class)
106 {
107   GObjectClass     *object_class = G_OBJECT_CLASS (class);
108
109   /* GObjectClass */
110   object_class->finalize     = gtk_cell_area_iter_finalize;
111   object_class->get_property = gtk_cell_area_iter_get_property;
112
113   class->flush_preferred_width            = gtk_cell_area_iter_real_flush_preferred_width;
114   class->flush_preferred_height_for_width = gtk_cell_area_iter_real_flush_preferred_height_for_width;
115   class->flush_preferred_height           = gtk_cell_area_iter_real_flush_preferred_height;
116   class->flush_preferred_width_for_height = gtk_cell_area_iter_real_flush_preferred_width_for_height;
117
118   cell_area_iter_signals[SIGNAL_HEIGHT_CHANGED] =
119     g_signal_new (I_("height-changed"),
120                   G_TYPE_FROM_CLASS (object_class),
121                   G_SIGNAL_RUN_LAST,
122                   0, /* Class offset (just a notification, no class handler) */
123                   NULL, NULL,
124                   _gtk_marshal_VOID__INT_INT_INT,
125                   G_TYPE_NONE, 3,
126                   G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
127
128   cell_area_iter_signals[SIGNAL_WIDTH_CHANGED] =
129     g_signal_new (I_("width-changed"),
130                   G_TYPE_FROM_CLASS (object_class),
131                   G_SIGNAL_RUN_LAST,
132                   0, /* Class offset (just a notification, no class handler) */
133                   NULL, NULL,
134                   _gtk_marshal_VOID__INT_INT_INT,
135                   G_TYPE_NONE, 3,
136                   G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
137
138   g_object_class_install_property (object_class,
139                                    PROP_MIN_WIDTH,
140                                    g_param_spec_int ("minimum-width",
141                                                      P_("Minimum Width"),
142                                                      P_("Minimum cached width"),
143                                                      -1,
144                                                      G_MAXINT,
145                                                      -1,
146                                                      G_PARAM_READABLE));
147
148   g_object_class_install_property (object_class,
149                                    PROP_NAT_WIDTH,
150                                    g_param_spec_int ("natural-width",
151                                                      P_("Minimum Width"),
152                                                      P_("Minimum cached width"),
153                                                      -1,
154                                                      G_MAXINT,
155                                                      -1,
156                                                      G_PARAM_READABLE));
157
158   g_object_class_install_property (object_class,
159                                    PROP_MIN_HEIGHT,
160                                    g_param_spec_int ("minimum-height",
161                                                      P_("Minimum Height"),
162                                                      P_("Minimum cached height"),
163                                                      -1,
164                                                      G_MAXINT,
165                                                      -1,
166                                                      G_PARAM_READABLE));
167
168   g_object_class_install_property (object_class,
169                                    PROP_NAT_HEIGHT,
170                                    g_param_spec_int ("natural-height",
171                                                      P_("Minimum Height"),
172                                                      P_("Minimum cached height"),
173                                                      -1,
174                                                      G_MAXINT,
175                                                      -1,
176                                                      G_PARAM_READABLE));
177
178   g_type_class_add_private (object_class, sizeof (GtkCellAreaIterPrivate));
179 }
180
181
182
183 /*************************************************************
184  *                      Cached Sizes                         *
185  *************************************************************/
186 static CachedSize *
187 cached_size_new (gint min_size, 
188                  gint nat_size)
189 {
190   CachedSize *size = g_slice_new (CachedSize);
191
192   size->min_size = min_size;
193   size->nat_size = nat_size;
194
195   return size;
196 }
197
198 static void
199 cached_size_free (CachedSize *size)
200 {
201   g_slice_free (CachedSize, size);
202 }
203
204 /*************************************************************
205  *                      GObjectClass                         *
206  *************************************************************/
207 static void
208 gtk_cell_area_iter_finalize (GObject *object)
209 {
210   GtkCellAreaIter        *iter = GTK_CELL_AREA_ITER (object);
211   GtkCellAreaIterPrivate *priv = iter->priv;
212
213   g_hash_table_destroy (priv->widths);
214   g_hash_table_destroy (priv->heights);
215
216   G_OBJECT_CLASS (gtk_cell_area_iter_parent_class)->finalize (object);
217 }
218
219 static void
220 gtk_cell_area_iter_get_property (GObject     *object,
221                                  guint        prop_id,
222                                  GValue      *value,
223                                  GParamSpec  *pspec)
224 {
225   GtkCellAreaIter        *iter = GTK_CELL_AREA_ITER (object);
226   GtkCellAreaIterPrivate *priv = iter->priv;
227
228   switch (prop_id)
229     {
230     case PROP_MIN_WIDTH:
231       g_value_set_int (value, priv->min_width);
232       break;
233     case PROP_NAT_WIDTH:
234       g_value_set_int (value, priv->nat_width);
235       break;
236     case PROP_MIN_HEIGHT:
237       g_value_set_int (value, priv->min_height);
238       break;
239     case PROP_NAT_HEIGHT:
240       g_value_set_int (value, priv->nat_height);
241       break;
242     default:
243       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
244       break;
245     }
246 }
247
248 /*************************************************************
249  *                    GtkCellAreaIterClass                   *
250  *************************************************************/
251 static void
252 gtk_cell_area_iter_real_flush_preferred_width (GtkCellAreaIter *iter)
253 {
254   GtkCellAreaIterPrivate *priv = iter->priv;
255   
256   priv->min_width = -1;
257   priv->nat_width = -1;
258
259   g_object_freeze_notify (G_OBJECT (iter));
260   g_object_notify (G_OBJECT (iter), "minimum-width");
261   g_object_notify (G_OBJECT (iter), "natural-width");
262   g_object_thaw_notify (G_OBJECT (iter));
263 }
264
265 static void
266 gtk_cell_area_iter_real_flush_preferred_height_for_width (GtkCellAreaIter *iter,
267                                                           gint             width)
268 {
269   GtkCellAreaIterPrivate *priv = iter->priv;
270
271   /* Flush all sizes for special -1 value */
272   if (width < 0)
273     g_hash_table_remove_all (priv->heights);
274   else
275     g_hash_table_remove (priv->heights, GINT_TO_POINTER (width));
276
277   /* XXX Should we bother signalling removed values as "size-changed" signals ? */
278 }
279
280 static void
281 gtk_cell_area_iter_real_flush_preferred_height (GtkCellAreaIter *iter)
282 {
283   GtkCellAreaIterPrivate *priv = iter->priv;
284   
285   priv->min_height = -1;
286   priv->nat_height = -1;
287
288   g_object_freeze_notify (G_OBJECT (iter));
289   g_object_notify (G_OBJECT (iter), "minimum-height");
290   g_object_notify (G_OBJECT (iter), "natural-height");
291   g_object_thaw_notify (G_OBJECT (iter));
292 }
293
294 static void
295 gtk_cell_area_iter_real_flush_preferred_width_for_height (GtkCellAreaIter *iter,
296                                                           gint             height)
297 {
298   GtkCellAreaIterPrivate *priv = iter->priv;
299
300   /* Flush all sizes for special -1 value */
301   if (height < 0)
302     g_hash_table_remove_all (priv->widths);
303   else
304     g_hash_table_remove (priv->widths, GINT_TO_POINTER (height));
305
306   /* XXX Should we bother signalling removed values as "size-changed" signals ? */
307 }
308
309 /*************************************************************
310  *                            API                            *
311  *************************************************************/
312
313 void
314 gtk_cell_area_iter_get_preferred_width (GtkCellAreaIter *iter,
315                                         gint            *minimum_width,
316                                         gint            *natural_width)
317 {
318   GtkCellAreaIterPrivate *priv;
319
320   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
321
322   priv = iter->priv;
323
324   if (minimum_width)
325     *minimum_width = priv->min_width;
326
327   if (natural_width)
328     *natural_width = priv->nat_width;
329 }
330
331 void
332 gtk_cell_area_iter_get_preferred_height_for_width (GtkCellAreaIter *iter,
333                                                    gint             for_width,
334                                                    gint            *minimum_height,
335                                                    gint            *natural_height)
336 {
337   GtkCellAreaIterPrivate *priv;
338   CachedSize             *size;
339
340   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
341
342   priv = iter->priv;
343
344   size = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width));
345
346   if (size)
347     {
348       if (minimum_height)
349         *minimum_height = size->min_size;
350
351       if (natural_height)
352         *natural_height = size->nat_size;
353     }
354   else
355     {
356       if (minimum_height)
357         *minimum_height = -1;
358
359       if (natural_height)
360         *natural_height = -1;
361     }
362 }
363
364 void
365 gtk_cell_area_iter_get_preferred_height (GtkCellAreaIter *iter,
366                                          gint            *minimum_height,
367                                          gint            *natural_height)
368 {
369   GtkCellAreaIterPrivate *priv;
370
371   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
372
373   priv = iter->priv;
374
375   if (minimum_height)
376     *minimum_height = priv->min_height;
377
378   if (natural_height)
379     *natural_height = priv->nat_height;
380 }
381
382 void
383 gtk_cell_area_iter_get_preferred_width_for_height (GtkCellAreaIter *iter,
384                                                    gint             for_height,
385                                                    gint            *minimum_width,
386                                                    gint            *natural_width)
387 {
388   GtkCellAreaIterPrivate *priv;
389   CachedSize             *size;
390
391   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
392
393   priv = iter->priv;
394
395   size = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height));
396
397   if (size)
398     {
399       if (minimum_width)
400         *minimum_width = size->min_size;
401
402       if (natural_width)
403         *natural_width = size->nat_size;
404     }
405   else
406     {
407       if (minimum_width)
408         *minimum_width = -1;
409
410       if (natural_width)
411         *natural_width = -1;
412     }
413 }
414
415
416 void
417 gtk_cell_area_iter_push_preferred_width (GtkCellAreaIter *iter,
418                                          gint             minimum_width,
419                                          gint             natural_width)
420 {
421   GtkCellAreaIterPrivate *priv;
422
423   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
424
425   priv = iter->priv;
426
427   g_object_freeze_notify (G_OBJECT (iter));
428
429   if (minimum_width > priv->min_width)
430     {
431       priv->min_width = minimum_width;
432
433       g_object_notify (G_OBJECT (iter), "minimum-width");
434     }
435
436   if (natural_width > priv->nat_width)
437     {
438       priv->nat_width = natural_width;
439
440       g_object_notify (G_OBJECT (iter), "natural-width");
441     }
442
443   g_object_thaw_notify (G_OBJECT (iter));
444 }
445
446 void
447 gtk_cell_area_iter_push_preferred_height_for_width (GtkCellAreaIter *iter,
448                                                     gint             for_width,
449                                                     gint             minimum_height,
450                                                     gint             natural_height)
451 {
452   GtkCellAreaIterPrivate *priv;
453   CachedSize             *size;
454   gboolean                changed = FALSE;
455
456   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
457
458   priv = iter->priv;
459
460   size = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width));
461
462   if (!size)
463     {
464       size = cached_size_new (minimum_height, natural_height);
465
466       g_hash_table_insert (priv->heights, GINT_TO_POINTER (for_width), size);
467
468       changed = TRUE;
469     }
470   else
471     {
472       if (minimum_height > size->min_size)
473         {
474           size->min_size = minimum_height;
475           changed = TRUE;
476         }
477
478       if (natural_height > size->nat_size)
479         {
480           size->nat_size = natural_height;
481           changed = TRUE;
482         }
483     }
484   
485   if (changed)
486     g_signal_emit (iter, cell_area_iter_signals[SIGNAL_HEIGHT_CHANGED], 0, 
487                    for_width, size->min_size, size->nat_size);
488 }
489
490 void
491 gtk_cell_area_iter_push_preferred_height (GtkCellAreaIter *iter,
492                                           gint             minimum_height,
493                                           gint             natural_height)
494 {
495   GtkCellAreaIterPrivate *priv;
496   
497   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
498
499   priv = iter->priv;
500
501   g_object_freeze_notify (G_OBJECT (iter));
502
503   if (minimum_height > priv->min_height)
504     {
505       priv->min_height = minimum_height;
506
507       g_object_notify (G_OBJECT (iter), "minimum-height");
508     }
509
510   if (natural_height > priv->nat_height)
511     {
512       priv->nat_height = natural_height;
513
514       g_object_notify (G_OBJECT (iter), "natural-height");
515     }
516
517   g_object_thaw_notify (G_OBJECT (iter));
518 }
519
520 void
521 gtk_cell_area_iter_push_preferred_width_for_height (GtkCellAreaIter *iter,
522                                                     gint             for_height,
523                                                     gint             minimum_width,
524                                                     gint             natural_width)
525 {
526   GtkCellAreaIterPrivate *priv;
527   CachedSize             *size;
528   gboolean                changed = FALSE;
529
530   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
531
532   priv = iter->priv;
533
534   size = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height));
535
536   if (!size)
537     {
538       size = cached_size_new (minimum_width, natural_width);
539
540       g_hash_table_insert (priv->widths, GINT_TO_POINTER (for_height), size);
541
542       changed = TRUE;
543     }
544   else
545     {
546       if (minimum_width > size->min_size)
547         {
548           size->min_size = minimum_width;
549           changed = TRUE;
550         }
551
552       if (natural_width > size->nat_size)
553         {
554           size->nat_size = natural_width;
555           changed = TRUE;
556         }
557     }
558   
559   if (changed)
560     g_signal_emit (iter, cell_area_iter_signals[SIGNAL_WIDTH_CHANGED], 0, 
561                    for_height, size->min_size, size->nat_size);
562 }
563
564 void
565 gtk_cell_area_iter_flush (GtkCellAreaIter *iter)
566 {
567   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
568
569   gtk_cell_area_iter_flush_preferred_width (iter);
570   gtk_cell_area_iter_flush_preferred_height_for_width (iter, -1);
571   gtk_cell_area_iter_flush_preferred_height (iter);
572   gtk_cell_area_iter_flush_preferred_width_for_height (iter, -1);
573 }
574
575 void
576 gtk_cell_area_iter_flush_preferred_width (GtkCellAreaIter *iter)
577 {
578   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
579
580   GTK_CELL_AREA_ITER_GET_CLASS (iter)->flush_preferred_width (iter);
581 }
582
583 void
584 gtk_cell_area_iter_flush_preferred_height_for_width (GtkCellAreaIter *iter,
585                                                      gint             for_width)
586 {
587   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
588
589   GTK_CELL_AREA_ITER_GET_CLASS (iter)->flush_preferred_height_for_width (iter, for_width);
590 }
591
592 void
593 gtk_cell_area_iter_flush_preferred_height (GtkCellAreaIter *iter)
594 {
595   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
596
597   GTK_CELL_AREA_ITER_GET_CLASS (iter)->flush_preferred_height (iter);
598 }
599
600 void
601 gtk_cell_area_iter_flush_preferred_width_for_height (GtkCellAreaIter *iter,
602                                                      gint             for_height)
603 {
604   g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
605
606   GTK_CELL_AREA_ITER_GET_CLASS (iter)->flush_preferred_width_for_height (iter, for_height);
607 }