diff --git a/gtk/gtkcsscomputedvalues.c b/gtk/gtkcsscomputedvalues.c index c5b6038cac..d41b240bed 100644 --- a/gtk/gtkcsscomputedvalues.c +++ b/gtk/gtkcsscomputedvalues.c @@ -20,14 +20,88 @@ #include "config.h" +#include +#include + #include "gtkcsscomputedvaluesprivate.h" #include "gtkcssstylepropertyprivate.h" #include "gtkcsstypesprivate.h" #include "gtkprivatetypebuiltins.h" +#include "gtkshadowprivate.h" +#include "gtkanimationdescription.h" +#include "gtkcssimageprivate.h" G_DEFINE_TYPE (GtkCssComputedValues, _gtk_css_computed_values, G_TYPE_OBJECT) + +typedef GValue GtkCssValue; + +static GHashTable *gtk_css_values; + +static guint gtk_css_value_hash (GtkCssValue *css_value); +static gboolean gtk_css_value_equal (GtkCssValue *css_value_a, GtkCssValue *css_value_b); + +static GtkCssValue * +gtk_css_value_ref (GtkCssValue *v) +{ + v->data[1].v_int++; + return v; +} + +static void +gtk_css_value_unref (GtkCssValue *v) +{ + if (--v->data[1].v_int == 0) + { + g_hash_table_remove (gtk_css_values, v); + g_value_unset ((GValue *)v); + } +} + +static GtkCssValue * +gtk_css_value_dup_value (const GValue *v) +{ + GtkCssValue *new; + + if (v == NULL || !G_IS_VALUE (v)) + return NULL; + + new = g_hash_table_lookup (gtk_css_values, v); + if (new) + return gtk_css_value_ref (new); + + new = g_new0 (GtkCssValue, 1); + g_value_init ((GValue *)new, G_VALUE_TYPE (v)); + g_value_copy (v, (GValue *)new); + new->data[1].v_int = 1; + + g_hash_table_insert (gtk_css_values, new, new); + + return new; +} + + +static GtkCssValue * +gtk_css_value_ref_value (GValue *v) +{ + + if (v == NULL || !G_IS_VALUE (v)) + return NULL; + + /* Some magic to detect if the GValue is already a GtkCssValue so we can just ref it */ + if (v->data[1].v_int > 0) + return gtk_css_value_ref ((GtkCssValue *)v); + + return gtk_css_value_dup_value (v); +} + +const GValue * +gtk_css_value_peek (GtkCssValue *v) +{ + return (const GValue *)v; +} + static void gtk_css_computed_values_dispose (GObject *object) { @@ -35,7 +109,7 @@ gtk_css_computed_values_dispose (GObject *object) if (values->values) { - g_array_free (values->values, TRUE); + g_ptr_array_unref (values->values); values->values = NULL; } if (values->sections) @@ -47,18 +121,282 @@ gtk_css_computed_values_dispose (GObject *object) G_OBJECT_CLASS (_gtk_css_computed_values_parent_class)->dispose (object); } +static gboolean +strv_equal (char **a, char **b) +{ + int i; + + if (a == b) + return TRUE; + + if (a == NULL || b == NULL) + return FALSE; + + for (i = 0; a[i] != NULL && b[i] != NULL; i++) + { + if (strcmp (a[i], b[i]) != 0) + return FALSE; + } + return a[i] == NULL && b[i] == NULL; +} + +static guint +strv_hash (char **v) +{ + int i; + guint hash; + + if (v == NULL) + return 0; + + hash = 0; + for (i = 0; v[i] != NULL; i++) + hash ^= g_str_hash (v[i]); + return hash; +} + +static gboolean +gtk_css_value_equal (GtkCssValue *css_value_a, GtkCssValue *css_value_b) +{ + GType type; + const GValue *a = gtk_css_value_peek (css_value_a); + const GValue *b = gtk_css_value_peek (css_value_b); + + if (a == b) + return TRUE; + + if (a == NULL || b == NULL) + return FALSE; + + if (a->g_type != b->g_type) + return FALSE; + + type = a->g_type; + if (type == G_TYPE_INT || type == G_TYPE_BOOLEAN) + { + return a->data[0].v_int == b->data[0].v_int; + } + else if (type == G_TYPE_DOUBLE) + { + return a->data[0].v_double == b->data[0].v_double; + } + else if (type == G_TYPE_LONG || + G_TYPE_IS_ENUM (type) || + G_TYPE_IS_FLAGS (type)) + { + return a->data[0].v_long == b->data[0].v_long; + } + else if (type == GDK_TYPE_RGBA) + { + return gdk_rgba_equal (a->data[0].v_pointer, + b->data[0].v_pointer); + } + else if (type == G_TYPE_STRV) + { + return strv_equal (a->data[0].v_pointer, + b->data[0].v_pointer); + } + else if (type == GTK_TYPE_CSS_NUMBER) + { + return _gtk_css_number_equal (a->data[0].v_pointer, + b->data[0].v_pointer); + } + else if (type == GTK_TYPE_CSS_BORDER_IMAGE_REPEAT) + { + GtkCssBorderImageRepeat *aa = a->data[0].v_pointer; + GtkCssBorderImageRepeat *bb = b->data[0].v_pointer; + + if (aa == bb) + return TRUE; + if (aa == NULL || bb == NULL) + return FALSE; + + return + aa->vrepeat == bb->vrepeat && + aa->hrepeat == bb->hrepeat; + } + else if (type == GTK_TYPE_CSS_BORDER_CORNER_RADIUS) + { + GtkCssBorderCornerRadius *aa = a->data[0].v_pointer; + GtkCssBorderCornerRadius *bb = b->data[0].v_pointer; + + if (aa == bb) + return TRUE; + if (aa == NULL || bb == NULL) + return FALSE; + + return + _gtk_css_number_equal (&aa->horizontal, &bb->horizontal) && + _gtk_css_number_equal (&aa->vertical, &bb->vertical); + } + else if (type == GTK_TYPE_BORDER) + { + GtkBorder *aa = a->data[0].v_pointer; + GtkBorder *bb = b->data[0].v_pointer; + + if (aa == bb) + return TRUE; + if (aa == NULL || bb == NULL) + return FALSE; + + return + aa->left == bb->left && + aa->right == bb->right && + aa->top == bb->top && + aa->bottom == bb->bottom; + } + else if (type == GTK_TYPE_CSS_BACKGROUND_SIZE) + { + GtkCssBackgroundSize *aa = a->data[0].v_pointer; + GtkCssBackgroundSize *bb = b->data[0].v_pointer; + + if (aa == bb) + return TRUE; + if (aa == NULL || bb == NULL) + return FALSE; + + return + _gtk_css_number_equal (&aa->width, &bb->width) && + _gtk_css_number_equal (&aa->height, &bb->height) && + aa->cover == bb->cover && + aa->contain == bb->contain; + } + else if (type == GTK_TYPE_SHADOW || + type == G_TYPE_PTR_ARRAY || + type == CAIRO_GOBJECT_TYPE_PATTERN || + type == GTK_TYPE_THEMING_ENGINE || + type == GTK_TYPE_ANIMATION_DESCRIPTION|| + type == GTK_TYPE_CSS_IMAGE) + { + /* These are refcounted, compare by pointer */ + return a->data[0].v_pointer == b->data[0].v_pointer; + } + else + { + g_error ("Can't handle CSS type %s\n", g_type_name (type)); + } + + return FALSE; +} + + +static guint +gtk_css_value_hash (GtkCssValue *css_value) +{ + GType type; + const GValue *v = gtk_css_value_peek (css_value); + + if (v == NULL) + return 0; + + if (v->g_type == 0) + return 0; + + type = v->g_type; + + if (type == G_TYPE_INT || type == G_TYPE_BOOLEAN) + { + return (guint)v->data[0].v_int; + } + else if (type == G_TYPE_DOUBLE) + { + return (guint)v->data[0].v_double; + } + else if (type == G_TYPE_LONG || + G_TYPE_IS_ENUM (type) || + G_TYPE_IS_FLAGS (type)) + { + return (guint)v->data[0].v_long; + } + else if (type == GDK_TYPE_RGBA) + { + return gdk_rgba_hash (v->data[0].v_pointer); + } + else if (type == G_TYPE_STRV) + { + return strv_hash (v->data[0].v_pointer); + } + else if (type == GTK_TYPE_CSS_NUMBER) + { + return _gtk_css_number_hash (v->data[0].v_pointer); + } + else if (type == GTK_TYPE_CSS_BORDER_IMAGE_REPEAT) + { + GtkCssBorderImageRepeat *vv = v->data[0].v_pointer; + + if (vv == NULL) + return 0; + + return ((guint)vv->vrepeat) ^ ((guint)vv->hrepeat); + } + else if (type == GTK_TYPE_CSS_BORDER_CORNER_RADIUS) + { + GtkCssBorderCornerRadius *vv = v->data[0].v_pointer; + + if (vv == NULL) + return 0; + + return + _gtk_css_number_hash (&vv->horizontal) ^ + _gtk_css_number_hash (&vv->vertical); + } + else if (type == GTK_TYPE_BORDER) + { + GtkBorder *vv = v->data[0].v_pointer; + + if (vv == NULL) + return 0; + + return + ((guint)vv->left) ^ + (((guint)vv->right) << 16) ^ + ((guint)vv->top) ^ + (((guint)vv->bottom) << 16); + } + else if (type == GTK_TYPE_CSS_BACKGROUND_SIZE) + { + GtkCssBackgroundSize *vv = v->data[0].v_pointer; + + if (vv == NULL) + return 0; + + return + _gtk_css_number_hash (&vv->width) ^ + _gtk_css_number_hash (&vv->height) ^ + (((guint)vv->cover) << 9) ^ + (((guint)vv->contain) << 9); + } + else if (type == GTK_TYPE_SHADOW || + type == G_TYPE_PTR_ARRAY || + type == CAIRO_GOBJECT_TYPE_PATTERN || + type == GTK_TYPE_THEMING_ENGINE || + type == GTK_TYPE_ANIMATION_DESCRIPTION|| + type == GTK_TYPE_CSS_IMAGE) + { + /* These are refcounted, compare by pointer */ + return GPOINTER_TO_INT (v->data[0].v_pointer); + } + else + { + g_error ("Can't handle CSS type %s\n", g_type_name (type)); + } + + return FALSE; +} + static void _gtk_css_computed_values_class_init (GtkCssComputedValuesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = gtk_css_computed_values_dispose; + + gtk_css_values = g_hash_table_new ((GHashFunc)gtk_css_value_hash, (GEqualFunc)gtk_css_value_equal); } static void _gtk_css_computed_values_init (GtkCssComputedValues *computed_values) { - } GtkCssComputedValues * @@ -92,12 +430,9 @@ _gtk_css_computed_values_compute_value (GtkCssComputedValues *values, parent = gtk_style_context_get_parent (context); if (values->values == NULL) - { - values->values = g_array_new (FALSE, TRUE, sizeof (GValue)); - g_array_set_clear_func (values->values, (GDestroyNotify) g_value_unset); - } + values->values = g_ptr_array_new_with_free_func ((GDestroyNotify)gtk_css_value_unref); if (id <= values->values->len) - g_array_set_size (values->values, id + 1); + g_ptr_array_set_size (values->values, id + 1); /* http://www.w3.org/TR/css3-cascade/#cascade * Then, for every element, the value for each property can be found @@ -158,22 +493,27 @@ _gtk_css_computed_values_compute_value (GtkCssComputedValues *values, specified = _gtk_css_style_property_get_initial_value (prop); } + + /* Clear existing value, can't reuse as it may be shared */ + if (g_ptr_array_index (values->values, id) != NULL) + gtk_css_value_unref (g_ptr_array_index (values->values, id)); + if (specified) { + GValue value = G_VALUE_INIT; _gtk_css_style_property_compute_value (prop, - &g_array_index (values->values, GValue, id), + &value, context, specified); + g_ptr_array_index (values->values, id) = gtk_css_value_dup_value (&value); + g_value_unset (&value); } else { const GValue *parent_value; - GValue *value = &g_array_index (values->values, GValue, id); - /* Set NULL here and do the inheritance upon lookup? */ parent_value = _gtk_style_context_peek_property (parent, _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop))); - g_value_init (value, G_VALUE_TYPE (parent_value)); - g_value_copy (parent_value, value); + g_ptr_array_index (values->values, id) = gtk_css_value_ref_value ((GValue *)parent_value); } if (section) @@ -193,23 +533,19 @@ _gtk_css_computed_values_set_value (GtkCssComputedValues *values, const GValue *value, GtkCssSection *section) { - GValue *set; - g_return_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values)); g_return_if_fail (value == NULL || G_IS_VALUE (value)); if (values->values == NULL) - { - values->values = g_array_new (FALSE, TRUE, sizeof (GValue)); - g_array_set_clear_func (values->values, (GDestroyNotify) g_value_unset); - } + values->values = g_ptr_array_new_with_free_func ((GDestroyNotify)gtk_css_value_unref); if (id <= values->values->len) - g_array_set_size (values->values, id + 1); + g_ptr_array_set_size (values->values, id + 1); + /* Clear existing value, can't reuse as it may be shared */ + if (g_ptr_array_index (values->values, id) != NULL) + gtk_css_value_unref (g_ptr_array_index (values->values, id)); - set = &g_array_index (values->values, GValue, id); - g_value_init (set, G_VALUE_TYPE (value)); - g_value_copy (value, set); + g_ptr_array_index (values->values, id) = gtk_css_value_ref_value ((GValue *)value); if (section) { @@ -226,19 +562,13 @@ const GValue * _gtk_css_computed_values_get_value (GtkCssComputedValues *values, guint id) { - const GValue *v; - g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL); if (values->values == NULL || id >= values->values->len) return NULL; - v = &g_array_index (values->values, GValue, id); - if (!G_IS_VALUE (v)) - return NULL; - - return v; + return gtk_css_value_peek (g_ptr_array_index (values->values, id)); } const GValue * diff --git a/gtk/gtkcsscomputedvaluesprivate.h b/gtk/gtkcsscomputedvaluesprivate.h index 74a15ec10e..dff18ae0dc 100644 --- a/gtk/gtkcsscomputedvaluesprivate.h +++ b/gtk/gtkcsscomputedvaluesprivate.h @@ -42,7 +42,7 @@ struct _GtkCssComputedValues { GObject parent; - GArray *values; + GPtrArray *values; GPtrArray *sections; };