1 /* gtkcellareacontext.c
3 * Copyright (C) 2010 Openismus GmbH
6 * Tristan Van Berkom <tristanvb@openismus.com>
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.
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.
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.
26 #include "gtkmarshalers.h"
27 #include "gtkcellareacontext.h"
28 #include "gtkprivate.h"
31 static void gtk_cell_area_context_finalize (GObject *object);
32 static void gtk_cell_area_context_dispose (GObject *object);
33 static void gtk_cell_area_context_get_property (GObject *object,
37 static void gtk_cell_area_context_set_property (GObject *object,
42 /* GtkCellAreaContextClass */
43 static void gtk_cell_area_context_real_flush_preferred_width (GtkCellAreaContext *context);
44 static void gtk_cell_area_context_real_flush_preferred_height_for_width (GtkCellAreaContext *context,
46 static void gtk_cell_area_context_real_flush_preferred_height (GtkCellAreaContext *context);
47 static void gtk_cell_area_context_real_flush_preferred_width_for_height (GtkCellAreaContext *context,
49 static void gtk_cell_area_context_real_flush_allocation (GtkCellAreaContext *context);
50 static void gtk_cell_area_context_real_allocate_width (GtkCellAreaContext *context,
52 static void gtk_cell_area_context_real_allocate_height (GtkCellAreaContext *context,
55 /* CachedSize management */
61 static CachedSize *cached_size_new (gint min_size, gint nat_size);
62 static void cached_size_free (CachedSize *size);
64 struct _GtkCellAreaContextPrivate
66 GtkCellArea *cell_area;
90 SIGNAL_HEIGHT_CHANGED,
94 static guint cell_area_context_signals[LAST_SIGNAL] = { 0 };
96 G_DEFINE_TYPE (GtkCellAreaContext, gtk_cell_area_context, G_TYPE_OBJECT);
99 gtk_cell_area_context_init (GtkCellAreaContext *context)
101 GtkCellAreaContextPrivate *priv;
103 context->priv = G_TYPE_INSTANCE_GET_PRIVATE (context,
104 GTK_TYPE_CELL_AREA_CONTEXT,
105 GtkCellAreaContextPrivate);
106 priv = context->priv;
108 priv->min_width = -1;
109 priv->nat_width = -1;
110 priv->min_height = -1;
111 priv->nat_height = -1;
112 priv->widths = g_hash_table_new_full (g_direct_hash, g_direct_equal,
113 NULL, (GDestroyNotify)cached_size_free);
114 priv->heights = g_hash_table_new_full (g_direct_hash, g_direct_equal,
115 NULL, (GDestroyNotify)cached_size_free);
119 gtk_cell_area_context_class_init (GtkCellAreaContextClass *class)
121 GObjectClass *object_class = G_OBJECT_CLASS (class);
124 object_class->finalize = gtk_cell_area_context_finalize;
125 object_class->dispose = gtk_cell_area_context_dispose;
126 object_class->get_property = gtk_cell_area_context_get_property;
127 object_class->set_property = gtk_cell_area_context_set_property;
129 /* GtkCellAreaContextClass */
130 class->flush_preferred_width = gtk_cell_area_context_real_flush_preferred_width;
131 class->flush_preferred_height_for_width = gtk_cell_area_context_real_flush_preferred_height_for_width;
132 class->flush_preferred_height = gtk_cell_area_context_real_flush_preferred_height;
133 class->flush_preferred_width_for_height = gtk_cell_area_context_real_flush_preferred_width_for_height;
134 class->flush_allocation = gtk_cell_area_context_real_flush_allocation;
136 class->sum_preferred_width = NULL;
137 class->sum_preferred_height_for_width = NULL;
138 class->sum_preferred_height = NULL;
139 class->sum_preferred_width_for_height = NULL;
141 class->allocate_width = gtk_cell_area_context_real_allocate_width;
142 class->allocate_height = gtk_cell_area_context_real_allocate_height;
144 cell_area_context_signals[SIGNAL_HEIGHT_CHANGED] =
145 g_signal_new (I_("height-changed"),
146 G_TYPE_FROM_CLASS (object_class),
148 0, /* Class offset (just a notification, no class handler) */
150 _gtk_marshal_VOID__INT_INT_INT,
152 G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
154 cell_area_context_signals[SIGNAL_WIDTH_CHANGED] =
155 g_signal_new (I_("width-changed"),
156 G_TYPE_FROM_CLASS (object_class),
158 0, /* Class offset (just a notification, no class handler) */
160 _gtk_marshal_VOID__INT_INT_INT,
162 G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
164 g_object_class_install_property (object_class,
166 g_param_spec_object ("area",
168 P_("The Cell Area this context was created for"),
170 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
172 g_object_class_install_property (object_class,
174 g_param_spec_int ("minimum-width",
176 P_("Minimum cached width"),
182 g_object_class_install_property (object_class,
184 g_param_spec_int ("natural-width",
186 P_("Minimum cached width"),
192 g_object_class_install_property (object_class,
194 g_param_spec_int ("minimum-height",
195 P_("Minimum Height"),
196 P_("Minimum cached height"),
202 g_object_class_install_property (object_class,
204 g_param_spec_int ("natural-height",
205 P_("Minimum Height"),
206 P_("Minimum cached height"),
212 g_type_class_add_private (object_class, sizeof (GtkCellAreaContextPrivate));
217 /*************************************************************
219 *************************************************************/
221 cached_size_new (gint min_size,
224 CachedSize *size = g_slice_new (CachedSize);
226 size->min_size = min_size;
227 size->nat_size = nat_size;
233 cached_size_free (CachedSize *size)
235 g_slice_free (CachedSize, size);
238 /*************************************************************
240 *************************************************************/
242 gtk_cell_area_context_finalize (GObject *object)
244 GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object);
245 GtkCellAreaContextPrivate *priv = context->priv;
247 g_hash_table_destroy (priv->widths);
248 g_hash_table_destroy (priv->heights);
250 G_OBJECT_CLASS (gtk_cell_area_context_parent_class)->finalize (object);
254 gtk_cell_area_context_dispose (GObject *object)
256 GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object);
257 GtkCellAreaContextPrivate *priv = context->priv;
261 g_object_unref (priv->cell_area);
263 priv->cell_area = NULL;
266 G_OBJECT_CLASS (gtk_cell_area_context_parent_class)->dispose (object);
270 gtk_cell_area_context_set_property (GObject *object,
275 GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object);
276 GtkCellAreaContextPrivate *priv = context->priv;
281 priv->cell_area = g_value_dup_object (value);
284 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
290 gtk_cell_area_context_get_property (GObject *object,
295 GtkCellAreaContext *context = GTK_CELL_AREA_CONTEXT (object);
296 GtkCellAreaContextPrivate *priv = context->priv;
301 g_value_set_object (value, priv->cell_area);
304 g_value_set_int (value, priv->min_width);
307 g_value_set_int (value, priv->nat_width);
309 case PROP_MIN_HEIGHT:
310 g_value_set_int (value, priv->min_height);
312 case PROP_NAT_HEIGHT:
313 g_value_set_int (value, priv->nat_height);
316 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
321 /*************************************************************
322 * GtkCellAreaContextClass *
323 *************************************************************/
325 gtk_cell_area_context_real_flush_preferred_width (GtkCellAreaContext *context)
327 GtkCellAreaContextPrivate *priv = context->priv;
329 priv->min_width = -1;
330 priv->nat_width = -1;
332 g_object_freeze_notify (G_OBJECT (context));
333 g_object_notify (G_OBJECT (context), "minimum-width");
334 g_object_notify (G_OBJECT (context), "natural-width");
335 g_object_thaw_notify (G_OBJECT (context));
339 notify_invalid_height (gpointer width_ptr,
341 GtkCellAreaContext *context)
343 gint width = GPOINTER_TO_INT (width_ptr);
345 /* Notify size invalidated */
346 g_signal_emit (context, cell_area_context_signals[SIGNAL_HEIGHT_CHANGED],
351 gtk_cell_area_context_real_flush_preferred_height_for_width (GtkCellAreaContext *context,
354 GtkCellAreaContextPrivate *priv = context->priv;
356 /* Flush all sizes for special -1 value */
359 g_hash_table_foreach (priv->heights, (GHFunc)notify_invalid_height, context);
360 g_hash_table_remove_all (priv->heights);
364 g_hash_table_remove (priv->heights, GINT_TO_POINTER (width));
366 /* Notify size invalidated */
367 g_signal_emit (context, cell_area_context_signals[SIGNAL_HEIGHT_CHANGED],
373 gtk_cell_area_context_real_flush_preferred_height (GtkCellAreaContext *context)
375 GtkCellAreaContextPrivate *priv = context->priv;
377 priv->min_height = -1;
378 priv->nat_height = -1;
380 g_object_freeze_notify (G_OBJECT (context));
381 g_object_notify (G_OBJECT (context), "minimum-height");
382 g_object_notify (G_OBJECT (context), "natural-height");
383 g_object_thaw_notify (G_OBJECT (context));
387 notify_invalid_width (gpointer height_ptr,
389 GtkCellAreaContext *context)
391 gint height = GPOINTER_TO_INT (height_ptr);
393 /* Notify size invalidated */
394 g_signal_emit (context, cell_area_context_signals[SIGNAL_WIDTH_CHANGED],
399 gtk_cell_area_context_real_flush_preferred_width_for_height (GtkCellAreaContext *context,
402 GtkCellAreaContextPrivate *priv = context->priv;
404 /* Flush all sizes for special -1 value */
407 g_hash_table_foreach (priv->widths, (GHFunc)notify_invalid_width, context);
408 g_hash_table_remove_all (priv->widths);
412 g_hash_table_remove (priv->widths, GINT_TO_POINTER (height));
414 /* Notify size invalidated */
415 g_signal_emit (context, cell_area_context_signals[SIGNAL_WIDTH_CHANGED],
421 gtk_cell_area_context_real_flush_allocation (GtkCellAreaContext *context)
423 GtkCellAreaContextPrivate *priv = context->priv;
425 priv->alloc_width = 0;
426 priv->alloc_height = 0;
430 gtk_cell_area_context_real_allocate_width (GtkCellAreaContext *context,
433 GtkCellAreaContextPrivate *priv = context->priv;
435 priv->alloc_width = width;
439 gtk_cell_area_context_real_allocate_height (GtkCellAreaContext *context,
442 GtkCellAreaContextPrivate *priv = context->priv;
444 priv->alloc_height = height;
448 /*************************************************************
450 *************************************************************/
452 gtk_cell_area_context_get_area (GtkCellAreaContext *context)
454 GtkCellAreaContextPrivate *priv;
456 g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), NULL);
458 priv = context->priv;
460 return priv->cell_area;
464 gtk_cell_area_context_flush (GtkCellAreaContext *context)
466 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
468 gtk_cell_area_context_flush_preferred_width (context);
469 gtk_cell_area_context_flush_preferred_height_for_width (context, -1);
470 gtk_cell_area_context_flush_preferred_height (context);
471 gtk_cell_area_context_flush_preferred_width_for_height (context, -1);
472 gtk_cell_area_context_flush_allocation (context);
476 gtk_cell_area_context_flush_preferred_width (GtkCellAreaContext *context)
478 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
480 GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->flush_preferred_width (context);
484 gtk_cell_area_context_flush_preferred_height_for_width (GtkCellAreaContext *context,
487 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
489 GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->flush_preferred_height_for_width (context, for_width);
493 gtk_cell_area_context_flush_preferred_height (GtkCellAreaContext *context)
495 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
497 GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->flush_preferred_height (context);
501 gtk_cell_area_context_flush_preferred_width_for_height (GtkCellAreaContext *context,
504 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
506 GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->flush_preferred_width_for_height (context, for_height);
510 gtk_cell_area_context_flush_allocation (GtkCellAreaContext *context)
512 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
514 GTK_CELL_AREA_CONTEXT_GET_CLASS (context)->flush_allocation (context);
518 gtk_cell_area_context_sum_preferred_width (GtkCellAreaContext *context)
520 GtkCellAreaContextClass *class;
522 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
524 class = GTK_CELL_AREA_CONTEXT_GET_CLASS (context);
526 if (class->sum_preferred_width)
527 class->sum_preferred_width (context);
531 gtk_cell_area_context_sum_preferred_height_for_width (GtkCellAreaContext *context,
534 GtkCellAreaContextClass *class;
536 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
538 class = GTK_CELL_AREA_CONTEXT_GET_CLASS (context);
540 if (class->sum_preferred_height_for_width)
541 class->sum_preferred_height_for_width (context, for_width);
545 gtk_cell_area_context_sum_preferred_height (GtkCellAreaContext *context)
547 GtkCellAreaContextClass *class;
549 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
551 class = GTK_CELL_AREA_CONTEXT_GET_CLASS (context);
553 if (class->sum_preferred_height)
554 class->sum_preferred_height (context);
558 gtk_cell_area_context_sum_preferred_width_for_height (GtkCellAreaContext *context,
561 GtkCellAreaContextClass *class;
563 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
565 class = GTK_CELL_AREA_CONTEXT_GET_CLASS (context);
567 if (class->sum_preferred_width_for_height)
568 class->sum_preferred_width_for_height (context, for_height);
572 gtk_cell_area_context_allocate_width (GtkCellAreaContext *context,
575 GtkCellAreaContextClass *class;
577 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
579 class = GTK_CELL_AREA_CONTEXT_GET_CLASS (context);
581 class->allocate_width (context, width);
585 gtk_cell_area_context_allocate_height (GtkCellAreaContext *context,
588 GtkCellAreaContextClass *class;
590 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
592 class = GTK_CELL_AREA_CONTEXT_GET_CLASS (context);
594 class->allocate_height (context, height);
598 gtk_cell_area_context_get_preferred_width (GtkCellAreaContext *context,
602 GtkCellAreaContextPrivate *priv;
604 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
606 priv = context->priv;
609 *minimum_width = priv->min_width;
612 *natural_width = priv->nat_width;
616 gtk_cell_area_context_get_preferred_height_for_width (GtkCellAreaContext *context,
618 gint *minimum_height,
619 gint *natural_height)
621 GtkCellAreaContextPrivate *priv;
624 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
626 priv = context->priv;
628 size = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width));
633 *minimum_height = size->min_size;
636 *natural_height = size->nat_size;
641 *minimum_height = -1;
644 *natural_height = -1;
649 gtk_cell_area_context_get_preferred_height (GtkCellAreaContext *context,
650 gint *minimum_height,
651 gint *natural_height)
653 GtkCellAreaContextPrivate *priv;
655 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
657 priv = context->priv;
660 *minimum_height = priv->min_height;
663 *natural_height = priv->nat_height;
667 gtk_cell_area_context_get_preferred_width_for_height (GtkCellAreaContext *context,
672 GtkCellAreaContextPrivate *priv;
675 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
677 priv = context->priv;
679 size = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height));
684 *minimum_width = size->min_size;
687 *natural_width = size->nat_size;
700 gtk_cell_area_context_get_allocation (GtkCellAreaContext *context,
704 GtkCellAreaContextPrivate *priv;
706 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
708 priv = context->priv;
711 *width = priv->alloc_width;
714 *height = priv->alloc_height;
718 gtk_cell_area_context_push_preferred_width (GtkCellAreaContext *context,
722 GtkCellAreaContextPrivate *priv;
724 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
726 priv = context->priv;
728 g_object_freeze_notify (G_OBJECT (context));
730 if (minimum_width > priv->min_width)
732 priv->min_width = minimum_width;
734 g_object_notify (G_OBJECT (context), "minimum-width");
737 if (natural_width > priv->nat_width)
739 priv->nat_width = natural_width;
741 g_object_notify (G_OBJECT (context), "natural-width");
744 g_object_thaw_notify (G_OBJECT (context));
748 gtk_cell_area_context_push_preferred_height_for_width (GtkCellAreaContext *context,
753 GtkCellAreaContextPrivate *priv;
755 gboolean changed = FALSE;
757 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
759 priv = context->priv;
761 size = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width));
765 size = cached_size_new (minimum_height, natural_height);
767 g_hash_table_insert (priv->heights, GINT_TO_POINTER (for_width), size);
773 if (minimum_height > size->min_size)
775 size->min_size = minimum_height;
779 if (natural_height > size->nat_size)
781 size->nat_size = natural_height;
787 g_signal_emit (context, cell_area_context_signals[SIGNAL_HEIGHT_CHANGED], 0,
788 for_width, size->min_size, size->nat_size);
792 gtk_cell_area_context_push_preferred_height (GtkCellAreaContext *context,
796 GtkCellAreaContextPrivate *priv;
798 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
800 priv = context->priv;
802 g_object_freeze_notify (G_OBJECT (context));
804 if (minimum_height > priv->min_height)
806 priv->min_height = minimum_height;
808 g_object_notify (G_OBJECT (context), "minimum-height");
811 if (natural_height > priv->nat_height)
813 priv->nat_height = natural_height;
815 g_object_notify (G_OBJECT (context), "natural-height");
818 g_object_thaw_notify (G_OBJECT (context));
822 gtk_cell_area_context_push_preferred_width_for_height (GtkCellAreaContext *context,
827 GtkCellAreaContextPrivate *priv;
829 gboolean changed = FALSE;
831 g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context));
833 priv = context->priv;
835 size = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height));
839 size = cached_size_new (minimum_width, natural_width);
841 g_hash_table_insert (priv->widths, GINT_TO_POINTER (for_height), size);
847 if (minimum_width > size->min_size)
849 size->min_size = minimum_width;
853 if (natural_width > size->nat_size)
855 size->nat_size = natural_width;
861 g_signal_emit (context, cell_area_context_signals[SIGNAL_WIDTH_CHANGED], 0,
862 for_height, size->min_size, size->nat_size);