gtk2/gtk/gtkcssvalue.c
Alexander Larsson 1ceed037b2 Don't use GValue in GtkCssValue
We now have complete coverage in the GtkCssValue API for type
handling, so drop the GValue from internal storage and just create
new ones when needed.
2012-03-08 16:39:49 +01:00

616 lines
14 KiB
C

/* GTK - The GIMP Toolkit
* Copyright (C) 2011 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkcssvalueprivate.h"
#include "gtktypebuiltins.h"
#include "gtkgradient.h"
#include <cairo-gobject.h>
#include "gtkprivatetypebuiltins.h"
#include "fallback-c89.c"
struct _GtkCssValue
{
volatile gint ref_count;
GType type;
union {
gpointer ptr;
gint gint;
double dbl;
} u;
};
G_DEFINE_BOXED_TYPE (GtkCssValue, _gtk_css_value, _gtk_css_value_ref, _gtk_css_value_unref)
static GtkCssValue *
_gtk_css_value_new (GType type)
{
GtkCssValue *value;
value = g_slice_new0 (GtkCssValue);
value->ref_count = 1;
value->type = type;
return value;
}
GtkCssValue *
_gtk_css_value_new_from_gvalue (const GValue *g_value)
{
GtkCssValue *value;
GType type;
g_return_val_if_fail (g_value != NULL, NULL);
type = G_VALUE_TYPE (g_value);
/* Make sure we reuse the int/number singletons */
if (type == G_TYPE_INT)
value = _gtk_css_value_new_from_int (g_value_get_int (g_value));
else if (type == GTK_TYPE_CSS_NUMBER)
value = _gtk_css_value_new_from_number (g_value_get_boxed (g_value));
else
{
value = _gtk_css_value_new (type);
if (g_type_is_a (type, G_TYPE_OBJECT))
value->u.ptr = g_value_dup_object (g_value);
else if (g_type_is_a (type, G_TYPE_BOXED))
value->u.ptr = g_value_dup_boxed (g_value);
else if (g_type_is_a (type, G_TYPE_INT))
value->u.gint = g_value_get_int (g_value);
else if (g_type_is_a (type, G_TYPE_BOOLEAN))
value->u.gint = g_value_get_boolean (g_value);
else if (g_type_is_a (type, G_TYPE_ENUM))
value->u.gint = g_value_get_enum (g_value);
else if (g_type_is_a (type, G_TYPE_STRING))
value->u.ptr = g_value_dup_string (g_value);
else if (g_type_is_a (type, G_TYPE_DOUBLE))
value->u.dbl = g_value_get_double (g_value);
else
g_assert_not_reached ();
}
return value;
}
GtkCssValue *
_gtk_css_value_new_take_gvalue (GValue *g_value)
{
GtkCssValue *value;
GType type;
g_return_val_if_fail (g_value != NULL, NULL);
type = G_VALUE_TYPE (g_value);
/* Make sure we reuse the int/number singletons */
if (type == G_TYPE_INT)
{
value = _gtk_css_value_new_from_int (g_value_get_int (g_value));
g_value_unset (g_value);
}
else if (type == GTK_TYPE_CSS_NUMBER)
{
value = _gtk_css_value_new_from_number (g_value_get_boxed (g_value));
g_value_unset (g_value);
}
else
{
value = _gtk_css_value_new (type);
if (g_type_is_a (type, G_TYPE_OBJECT))
value->u.ptr = g_value_get_object (g_value);
else if (g_type_is_a (type, G_TYPE_BOXED))
value->u.ptr = g_value_get_boxed (g_value);
else if (g_type_is_a (type, G_TYPE_INT))
value->u.gint = g_value_get_int (g_value);
else if (g_type_is_a (type, G_TYPE_BOOLEAN))
value->u.gint = g_value_get_boolean (g_value);
else if (g_type_is_a (type, G_TYPE_ENUM))
value->u.gint = g_value_get_enum (g_value);
else if (g_type_is_a (type, G_TYPE_STRING))
value->u.ptr = g_value_dup_string (g_value);
else if (g_type_is_a (type, G_TYPE_DOUBLE))
value->u.dbl = g_value_get_double (g_value);
else
g_assert_not_reached ();
}
return value;
}
GtkCssValue *
_gtk_css_value_new_from_int (gint val)
{
GtkCssValue *value;
static GtkCssValue *singletons[4] = {NULL};
if (val >= 0 && val < G_N_ELEMENTS (singletons))
{
if (singletons[val] == NULL)
{
value = _gtk_css_value_new (G_TYPE_INT);
value->u.gint = val;
singletons[val] = value;
}
return _gtk_css_value_ref (singletons[val]);
}
value = _gtk_css_value_new (G_TYPE_INT);
value->u.gint = val;
return value;
}
GtkCssValue *
_gtk_css_value_new_take_string (char *string)
{
GtkCssValue *value;
value = _gtk_css_value_new (G_TYPE_STRING);
value->u.ptr = string;
return value;
}
GtkCssValue *
_gtk_css_value_new_from_string (const char *string)
{
GtkCssValue *value;
value = _gtk_css_value_new (G_TYPE_STRING);
value->u.ptr = g_strdup (string);
return value;
}
static gpointer
g_boxed_copy0 (GType boxed_type,
gconstpointer src_boxed)
{
if (src_boxed == NULL)
return NULL;
return g_boxed_copy (boxed_type, src_boxed);
}
GtkCssValue *
_gtk_css_value_new_from_border (const GtkBorder *border)
{
GtkCssValue *value;
value = _gtk_css_value_new (GTK_TYPE_BORDER);
value->u.ptr = g_boxed_copy0 (GTK_TYPE_BORDER, border);
return value;
}
GtkCssValue *
_gtk_css_value_new_take_pattern (cairo_pattern_t *v)
{
GtkCssValue *value;
value = _gtk_css_value_new (CAIRO_GOBJECT_TYPE_PATTERN);
value->u.ptr = v;
return value;
}
GtkCssValue *
_gtk_css_value_new_from_pattern (const cairo_pattern_t *v)
{
GtkCssValue *value;
value = _gtk_css_value_new (CAIRO_GOBJECT_TYPE_PATTERN);
value->u.ptr = g_boxed_copy0 (CAIRO_GOBJECT_TYPE_PATTERN, v);
return value;
}
GtkCssValue *
_gtk_css_value_new_take_shadow (GtkShadow *v)
{
GtkCssValue *value;
value = _gtk_css_value_new (GTK_TYPE_SHADOW);
value->u.ptr = v;
return value;
}
GtkCssValue *
_gtk_css_value_new_take_font_description (PangoFontDescription *v)
{
GtkCssValue *value;
value = _gtk_css_value_new (PANGO_TYPE_FONT_DESCRIPTION);
value->u.ptr = v;
return value;
}
GtkCssValue *
_gtk_css_value_new_take_image (GtkCssImage *v)
{
GtkCssValue *value;
value = _gtk_css_value_new (GTK_TYPE_CSS_IMAGE);
value->u.ptr = v;
return value;
}
GtkCssValue *
_gtk_css_value_new_from_number (const GtkCssNumber *v)
{
GtkCssValue *value;
static GtkCssValue *zero_singleton = NULL;
static GtkCssValue *px_singletons[5] = {NULL};
if (v->unit == GTK_CSS_NUMBER &&
v->value == 0)
{
if (zero_singleton == NULL)
{
value = _gtk_css_value_new (GTK_TYPE_CSS_NUMBER);
value->u.ptr = g_boxed_copy0 (GTK_TYPE_CSS_NUMBER, v);
zero_singleton = value;
}
return _gtk_css_value_ref (zero_singleton);
}
if (v->unit == GTK_CSS_PX &&
(v->value == 0 ||
v->value == 1 ||
v->value == 2 ||
v->value == 3 ||
v->value == 4))
{
int i = round (v->value);
if (px_singletons[i] == NULL)
{
value = _gtk_css_value_new (GTK_TYPE_CSS_NUMBER);
value->u.ptr = g_boxed_copy0 (GTK_TYPE_CSS_NUMBER, v);
px_singletons[i] = value;
}
return _gtk_css_value_ref (px_singletons[i]);
}
value = _gtk_css_value_new (GTK_TYPE_CSS_NUMBER);
value->u.ptr = g_boxed_copy0 (GTK_TYPE_CSS_NUMBER, v);
return value;
}
GtkCssValue *
_gtk_css_value_new_from_rgba (const GdkRGBA *v)
{
GtkCssValue *value;
value = _gtk_css_value_new (GDK_TYPE_RGBA);
value->u.ptr = g_boxed_copy0 (GDK_TYPE_RGBA, v);
return value;
}
GtkCssValue *
_gtk_css_value_new_from_color (const GdkColor *v)
{
GtkCssValue *value;
value = _gtk_css_value_new (GDK_TYPE_COLOR);
value->u.ptr = g_boxed_copy0 (GDK_TYPE_COLOR, v);
return value;
}
GtkCssValue *
_gtk_css_value_new_from_background_size (const GtkCssBackgroundSize *v)
{
GtkCssValue *value;
value = _gtk_css_value_new (GTK_TYPE_CSS_BACKGROUND_SIZE);
value->u.ptr = g_boxed_copy0 (GTK_TYPE_CSS_BACKGROUND_SIZE, v);
return value;
}
GtkCssValue *
_gtk_css_value_new_take_symbolic_color (GtkSymbolicColor *v)
{
GtkCssValue *value;
value = _gtk_css_value_new (GTK_TYPE_SYMBOLIC_COLOR);
value->u.ptr = v;
return value;
}
GtkCssValue *
_gtk_css_value_ref (GtkCssValue *value)
{
g_return_val_if_fail (value != NULL, NULL);
g_atomic_int_add (&value->ref_count, 1);
return value;
}
void
_gtk_css_value_unref (GtkCssValue *value)
{
GType type;
if (value == NULL)
return;
if (!g_atomic_int_dec_and_test (&value->ref_count))
return;
type = value->type;
if (g_type_is_a (type, G_TYPE_OBJECT) && value->u.ptr != NULL)
g_object_unref (value->u.ptr);
else if (g_type_is_a (type, G_TYPE_BOXED) && value->u.ptr != NULL)
g_boxed_free (type, value->u.ptr);
else if (g_type_is_a (type, G_TYPE_STRING))
g_free (value->u.ptr);
g_slice_free (GtkCssValue, value);
}
GType
_gtk_css_value_get_content_type (GtkCssValue *value)
{
return value->type;
}
gboolean
_gtk_css_value_holds (GtkCssValue *value, GType type)
{
return g_type_is_a (value->type, type);
}
static void
fill_gvalue (GtkCssValue *value,
GValue *g_value)
{
GType type;
type = value->type;
if (g_type_is_a (type, G_TYPE_OBJECT))
g_value_set_object (g_value, value->u.ptr);
else if (g_type_is_a (type, G_TYPE_BOXED))
g_value_set_boxed (g_value, value->u.ptr);
else if (g_type_is_a (type, G_TYPE_INT))
g_value_set_int (g_value, value->u.gint);
else if (g_type_is_a (type, G_TYPE_BOOLEAN))
g_value_set_boolean (g_value, value->u.gint);
else if (g_type_is_a (type, G_TYPE_ENUM))
g_value_set_enum (g_value, value->u.gint);
else if (g_type_is_a (type, G_TYPE_STRING))
g_value_set_string (g_value, value->u.ptr);
else if (g_type_is_a (type, G_TYPE_DOUBLE))
g_value_set_double (g_value, value->u.dbl);
else
g_assert_not_reached ();
}
void
_gtk_css_value_init_gvalue (GtkCssValue *value,
GValue *g_value)
{
if (value != NULL)
{
g_value_init (g_value, value->type);
fill_gvalue (value, g_value);
}
}
void
_gtk_css_value_to_gvalue (GtkCssValue *value,
GValue *g_value)
{
if (value->type == G_VALUE_TYPE (g_value))
fill_gvalue (value, g_value);
else if (g_value_type_transformable (value->type, G_VALUE_TYPE (g_value)))
{
GValue v = G_VALUE_INIT;
_gtk_css_value_init_gvalue (value, &v);
g_value_transform (&v, g_value);
g_value_unset (&v);
}
else
g_warning ("can't convert css value of type `%s' as value of type `%s'",
g_type_name (value->type),
G_VALUE_TYPE_NAME (g_value));
}
gboolean
_gtk_css_value_is_special (GtkCssValue *value)
{
return _gtk_css_value_holds (value, GTK_TYPE_CSS_SPECIAL_VALUE);
}
GtkCssSpecialValue
_gtk_css_value_get_special_kind (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_SPECIAL_VALUE), 0);
return value->u.gint;
}
GtkCssNumber *
_gtk_css_value_get_number (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_NUMBER), NULL);
return value->u.ptr;
}
GtkSymbolicColor *
_gtk_css_value_get_symbolic_color (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_SYMBOLIC_COLOR), NULL);
return value->u.ptr;
}
int
_gtk_css_value_get_int (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_INT), 0);
return value->u.gint;
}
double
_gtk_css_value_get_double (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_DOUBLE), 0);
return value->u.dbl;
}
const char *
_gtk_css_value_get_string (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_STRING), 0);
return value->u.ptr;
}
gpointer
_gtk_css_value_dup_object (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_OBJECT), NULL);
if (value->u.ptr)
return g_object_ref (value->u.ptr);
return NULL;
}
gpointer
_gtk_css_value_get_object (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_OBJECT), NULL);
return value->u.ptr;
}
gpointer
_gtk_css_value_get_boxed (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_BOXED), NULL);
return value->u.ptr;
}
const char **
_gtk_css_value_get_strv (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, G_TYPE_STRV), NULL);
return value->u.ptr;
}
GtkCssImage *
_gtk_css_value_get_image (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_IMAGE), NULL);
return value->u.ptr;
}
GtkBorderStyle
_gtk_css_value_get_border_style (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_BORDER_STYLE), 0);
return value->u.gint;
}
GtkCssBackgroundSize *
_gtk_css_value_get_background_size (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_BACKGROUND_SIZE), NULL);
return value->u.ptr;
}
GtkCssBorderImageRepeat *
_gtk_css_value_get_border_image_repeat (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT), NULL);
return value->u.ptr;
}
GtkCssBorderCornerRadius *
_gtk_css_value_get_border_corner_radius (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_CSS_BORDER_CORNER_RADIUS), NULL);
return value->u.ptr;
}
PangoFontDescription *
_gtk_css_value_get_font_description (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, PANGO_TYPE_FONT_DESCRIPTION), 0);
return value->u.ptr;
}
PangoStyle
_gtk_css_value_get_pango_style (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, PANGO_TYPE_STYLE), 0);
return value->u.gint;
}
PangoVariant
_gtk_css_value_get_pango_variant (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, PANGO_TYPE_VARIANT), 0);
return value->u.gint;
}
PangoWeight
_gtk_css_value_get_pango_weight (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, PANGO_TYPE_WEIGHT), 0);
return value->u.gint;
}
GdkRGBA *
_gtk_css_value_get_rgba (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, GDK_TYPE_RGBA), NULL);
return value->u.ptr;
}
cairo_pattern_t *
_gtk_css_value_get_pattern (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, CAIRO_GOBJECT_TYPE_PATTERN), NULL);
return value->u.ptr;
}
GtkGradient *
_gtk_css_value_get_gradient (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_GRADIENT), NULL);
return value->u.ptr;
}
GtkShadow *
_gtk_css_value_get_shadow (GtkCssValue *value)
{
g_return_val_if_fail (_gtk_css_value_holds (value, GTK_TYPE_SHADOW), NULL);
return value->u.ptr;
}