gtk2/gtk/gtkcssstylefuncs.c
Benjamin Otte 40283e7c27 cssvalue: Split out old value handling to new typed value
... and Make this new value be a real GValue, as we don't need to save
performance for these anymore (it's just used for custom properties).
And I'd rather have code work for all values then be optimized for no
reason.
2012-04-17 08:59:20 +02:00

1134 lines
30 KiB
C

/* GTK - The GIMP Toolkit
* Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
*
* 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 "gtkcssstylefuncsprivate.h"
#include <errno.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <cairo-gobject.h>
#include "gtkcssimagegradientprivate.h"
#include "gtkcssprovider.h"
#include "gtkcsstypedvalueprivate.h"
#include "gtkcsstypesprivate.h"
#include "gtkgradient.h"
#include "gtkprivatetypebuiltins.h"
#include "gtkstylecontextprivate.h"
#include "gtksymboliccolorprivate.h"
#include "gtkthemingengine.h"
#include "gtktypebuiltins.h"
#include "gtkwin32themeprivate.h"
/* this is in case round() is not provided by the compiler,
* such as in the case of C89 compilers, like MSVC
*/
#include "fallback-c89.c"
static GHashTable *parse_funcs = NULL;
static GHashTable *print_funcs = NULL;
static GHashTable *compute_funcs = NULL;
typedef gboolean (* GtkStyleParseFunc) (GtkCssParser *parser,
GFile *base,
GValue *value);
typedef void (* GtkStylePrintFunc) (const GValue *value,
GString *string);
typedef GtkCssValue * (* GtkStyleComputeFunc) (GtkStyleContext *context,
GtkCssValue *specified);
static void
register_conversion_function (GType type,
GtkStyleParseFunc parse,
GtkStylePrintFunc print,
GtkStyleComputeFunc compute)
{
if (parse)
g_hash_table_insert (parse_funcs, GSIZE_TO_POINTER (type), parse);
if (print)
g_hash_table_insert (print_funcs, GSIZE_TO_POINTER (type), print);
if (compute)
g_hash_table_insert (compute_funcs, GSIZE_TO_POINTER (type), compute);
}
static void
string_append_double (GString *string,
double d)
{
char buf[G_ASCII_DTOSTR_BUF_SIZE];
g_ascii_dtostr (buf, sizeof (buf), d);
g_string_append (string, buf);
}
static void
string_append_string (GString *str,
const char *string)
{
gsize len;
g_string_append_c (str, '"');
do {
len = strcspn (string, "\"\n\r\f");
g_string_append (str, string);
string += len;
switch (*string)
{
case '\0':
break;
case '\n':
g_string_append (str, "\\A ");
break;
case '\r':
g_string_append (str, "\\D ");
break;
case '\f':
g_string_append (str, "\\C ");
break;
case '\"':
g_string_append (str, "\\\"");
break;
default:
g_assert_not_reached ();
break;
}
} while (*string);
g_string_append_c (str, '"');
}
/*** IMPLEMENTATIONS ***/
static gboolean
enum_parse (GtkCssParser *parser,
GType type,
int *res)
{
char *str;
if (_gtk_css_parser_try_enum (parser, type, res))
return TRUE;
str = _gtk_css_parser_try_ident (parser, TRUE);
if (str == NULL)
{
_gtk_css_parser_error (parser, "Expected an identifier");
return FALSE;
}
_gtk_css_parser_error (parser,
"Unknown value '%s' for enum type '%s'",
str, g_type_name (type));
g_free (str);
return FALSE;
}
static void
enum_print (int value,
GType type,
GString *string)
{
GEnumClass *enum_class;
GEnumValue *enum_value;
enum_class = g_type_class_ref (type);
enum_value = g_enum_get_value (enum_class, value);
g_string_append (string, enum_value->value_nick);
g_type_class_unref (enum_class);
}
static gboolean
rgba_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
GtkSymbolicColor *symbolic;
GdkRGBA rgba;
symbolic = _gtk_symbolic_color_new_take_value (_gtk_css_symbolic_value_new (parser));
if (symbolic == NULL)
return FALSE;
if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
{
g_value_set_boxed (value, &rgba);
gtk_symbolic_color_unref (symbolic);
}
else
{
g_value_unset (value);
g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
g_value_take_boxed (value, symbolic);
}
return TRUE;
}
static void
rgba_value_print (const GValue *value,
GString *string)
{
const GdkRGBA *rgba = g_value_get_boxed (value);
if (rgba == NULL)
g_string_append (string, "none");
else
{
char *s = gdk_rgba_to_string (rgba);
g_string_append (string, s);
g_free (s);
}
}
static GtkCssValue *
rgba_value_compute (GtkStyleContext *context,
GtkCssValue *specified)
{
GdkRGBA white = { 1, 1, 1, 1 };
const GValue *value;
value = _gtk_css_typed_value_get (specified);
if (G_VALUE_HOLDS (value, GTK_TYPE_SYMBOLIC_COLOR))
{
GtkSymbolicColor *symbolic = g_value_get_boxed (value);
GValue new_value = G_VALUE_INIT;
GdkRGBA rgba;
if (!_gtk_style_context_resolve_color (context, symbolic, &rgba))
rgba = white;
g_value_init (&new_value, GDK_TYPE_RGBA);
g_value_set_boxed (&new_value, &rgba);
return _gtk_css_typed_value_new_take (&new_value);
}
else
return _gtk_css_value_ref (specified);
}
static gboolean
color_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
GtkSymbolicColor *symbolic;
GdkRGBA rgba;
symbolic = _gtk_symbolic_color_new_take_value (_gtk_css_symbolic_value_new (parser));
if (symbolic == NULL)
return FALSE;
if (gtk_symbolic_color_resolve (symbolic, NULL, &rgba))
{
GdkColor color;
color.red = rgba.red * 65535. + 0.5;
color.green = rgba.green * 65535. + 0.5;
color.blue = rgba.blue * 65535. + 0.5;
g_value_set_boxed (value, &color);
}
else
{
g_value_unset (value);
g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
g_value_take_boxed (value, symbolic);
}
return TRUE;
}
static void
color_value_print (const GValue *value,
GString *string)
{
const GdkColor *color = g_value_get_boxed (value);
if (color == NULL)
g_string_append (string, "none");
else
{
char *s = gdk_color_to_string (color);
g_string_append (string, s);
g_free (s);
}
}
static GtkCssValue *
color_value_compute (GtkStyleContext *context,
GtkCssValue *specified)
{
GdkRGBA rgba;
GdkColor color = { 0, 65535, 65535, 65535 };
const GValue *value;
value = _gtk_css_typed_value_get (specified);
if (G_VALUE_HOLDS (value, GTK_TYPE_SYMBOLIC_COLOR))
{
GValue new_value = G_VALUE_INIT;
if (_gtk_style_context_resolve_color (context,
g_value_get_boxed (value),
&rgba))
{
color.red = rgba.red * 65535. + 0.5;
color.green = rgba.green * 65535. + 0.5;
color.blue = rgba.blue * 65535. + 0.5;
}
g_value_init (&new_value, GDK_TYPE_COLOR);
g_value_set_boxed (&new_value, &color);
return _gtk_css_typed_value_new_take (&new_value);
}
else
return _gtk_css_value_ref (specified);
}
static gboolean
symbolic_color_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
GtkSymbolicColor *symbolic;
symbolic = _gtk_symbolic_color_new_take_value (_gtk_css_symbolic_value_new (parser));
if (symbolic == NULL)
return FALSE;
g_value_take_boxed (value, symbolic);
return TRUE;
}
static void
symbolic_color_value_print (const GValue *value,
GString *string)
{
GtkSymbolicColor *symbolic = g_value_get_boxed (value);
if (symbolic == NULL)
g_string_append (string, "none");
else
{
char *s = gtk_symbolic_color_to_string (symbolic);
g_string_append (string, s);
g_free (s);
}
}
static gboolean
font_description_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
PangoFontDescription *font_desc;
guint mask;
char *str;
str = _gtk_css_parser_read_value (parser);
if (str == NULL)
return FALSE;
font_desc = pango_font_description_from_string (str);
mask = pango_font_description_get_set_fields (font_desc);
/* These values are not really correct,
* but the fields must be set, so we set them to something */
if ((mask & PANGO_FONT_MASK_FAMILY) == 0)
pango_font_description_set_family_static (font_desc, "Sans");
if ((mask & PANGO_FONT_MASK_SIZE) == 0)
pango_font_description_set_size (font_desc, 10 * PANGO_SCALE);
g_free (str);
g_value_take_boxed (value, font_desc);
return TRUE;
}
static void
font_description_value_print (const GValue *value,
GString *string)
{
const PangoFontDescription *desc = g_value_get_boxed (value);
if (desc == NULL)
g_string_append (string, "none");
else
{
char *s = pango_font_description_to_string (desc);
g_string_append (string, s);
g_free (s);
}
}
static gboolean
boolean_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
if (_gtk_css_parser_try (parser, "true", TRUE) ||
_gtk_css_parser_try (parser, "1", TRUE))
{
g_value_set_boolean (value, TRUE);
return TRUE;
}
else if (_gtk_css_parser_try (parser, "false", TRUE) ||
_gtk_css_parser_try (parser, "0", TRUE))
{
g_value_set_boolean (value, FALSE);
return TRUE;
}
else
{
_gtk_css_parser_error (parser, "Expected a boolean value");
return FALSE;
}
}
static void
boolean_value_print (const GValue *value,
GString *string)
{
if (g_value_get_boolean (value))
g_string_append (string, "true");
else
g_string_append (string, "false");
}
static gboolean
int_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
gint i;
if (_gtk_css_parser_begins_with (parser, '-'))
{
int res = _gtk_win32_theme_int_parse (parser, base, &i);
if (res >= 0)
{
g_value_set_int (value, i);
return res > 0;
}
/* < 0 => continue */
}
if (!_gtk_css_parser_try_int (parser, &i))
{
_gtk_css_parser_error (parser, "Expected a valid integer value");
return FALSE;
}
g_value_set_int (value, i);
return TRUE;
}
static void
int_value_print (const GValue *value,
GString *string)
{
g_string_append_printf (string, "%d", g_value_get_int (value));
}
static gboolean
uint_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
guint u;
if (!_gtk_css_parser_try_uint (parser, &u))
{
_gtk_css_parser_error (parser, "Expected a valid unsigned value");
return FALSE;
}
g_value_set_uint (value, u);
return TRUE;
}
static void
uint_value_print (const GValue *value,
GString *string)
{
g_string_append_printf (string, "%u", g_value_get_uint (value));
}
static gboolean
double_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
gdouble d;
if (!_gtk_css_parser_try_double (parser, &d))
{
_gtk_css_parser_error (parser, "Expected a number");
return FALSE;
}
g_value_set_double (value, d);
return TRUE;
}
static void
double_value_print (const GValue *value,
GString *string)
{
string_append_double (string, g_value_get_double (value));
}
static gboolean
float_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
gdouble d;
if (!_gtk_css_parser_try_double (parser, &d))
{
_gtk_css_parser_error (parser, "Expected a number");
return FALSE;
}
g_value_set_float (value, d);
return TRUE;
}
static void
float_value_print (const GValue *value,
GString *string)
{
string_append_double (string, g_value_get_float (value));
}
static gboolean
string_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
char *str = _gtk_css_parser_read_string (parser);
if (str == NULL)
return FALSE;
g_value_take_string (value, str);
return TRUE;
}
static void
string_value_print (const GValue *value,
GString *str)
{
string_append_string (str, g_value_get_string (value));
}
static gboolean
theming_engine_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
GtkThemingEngine *engine;
char *str;
if (_gtk_css_parser_try (parser, "none", TRUE))
{
g_value_set_object (value, gtk_theming_engine_load (NULL));
return TRUE;
}
str = _gtk_css_parser_try_ident (parser, TRUE);
if (str == NULL)
{
_gtk_css_parser_error (parser, "Expected a valid theme name");
return FALSE;
}
engine = gtk_theming_engine_load (str);
if (engine == NULL)
{
_gtk_css_parser_error (parser, "Theming engine '%s' not found", str);
g_free (str);
return FALSE;
}
g_value_set_object (value, engine);
g_free (str);
return TRUE;
}
static void
theming_engine_value_print (const GValue *value,
GString *string)
{
GtkThemingEngine *engine;
char *name;
engine = g_value_get_object (value);
if (engine == NULL)
g_string_append (string, "none");
else
{
/* XXX: gtk_theming_engine_get_name()? */
g_object_get (engine, "name", &name, NULL);
g_string_append (string, name ? name : "none");
g_free (name);
}
}
static gboolean
border_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
GtkBorder border = { 0, };
guint i;
int numbers[4];
for (i = 0; i < G_N_ELEMENTS (numbers); i++)
{
if (_gtk_css_parser_begins_with (parser, '-'))
{
/* These are strictly speaking signed, but we want to be able to use them
for unsigned types too, as the actual ranges of values make this safe */
int res = _gtk_win32_theme_int_parse (parser, base, &numbers[i]);
if (res == 0) /* Parse error, report */
return FALSE;
if (res < 0) /* Nothing known to expand */
break;
}
else
{
if (!_gtk_css_parser_try_length (parser, &numbers[i]))
break;
}
}
if (i == 0)
{
_gtk_css_parser_error (parser, "Expected valid border");
return FALSE;
}
border.top = numbers[0];
if (i > 1)
border.right = numbers[1];
else
border.right = border.top;
if (i > 2)
border.bottom = numbers[2];
else
border.bottom = border.top;
if (i > 3)
border.left = numbers[3];
else
border.left = border.right;
g_value_set_boxed (value, &border);
return TRUE;
}
static void
border_value_print (const GValue *value, GString *string)
{
const GtkBorder *border = g_value_get_boxed (value);
if (border == NULL)
g_string_append (string, "none");
else if (border->left != border->right)
g_string_append_printf (string, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
else if (border->top != border->bottom)
g_string_append_printf (string, "%d %d %d", border->top, border->right, border->bottom);
else if (border->top != border->left)
g_string_append_printf (string, "%d %d", border->top, border->right);
else
g_string_append_printf (string, "%d", border->top);
}
static gboolean
gradient_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
GtkGradient *gradient;
gradient = _gtk_gradient_parse (parser);
if (gradient == NULL)
return FALSE;
g_value_take_boxed (value, gradient);
return TRUE;
}
static void
gradient_value_print (const GValue *value,
GString *string)
{
GtkGradient *gradient = g_value_get_boxed (value);
if (gradient == NULL)
g_string_append (string, "none");
else
{
char *s = gtk_gradient_to_string (gradient);
g_string_append (string, s);
g_free (s);
}
}
static gboolean
pattern_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
if (_gtk_css_parser_try (parser, "none", TRUE))
{
/* nothing to do here */
}
else if (_gtk_css_parser_begins_with (parser, '-'))
{
g_value_unset (value);
g_value_init (value, GTK_TYPE_GRADIENT);
return gradient_value_parse (parser, base, value);
}
else
{
GError *error = NULL;
gchar *path;
GdkPixbuf *pixbuf;
GFile *file;
cairo_surface_t *surface;
cairo_pattern_t *pattern;
cairo_t *cr;
cairo_matrix_t matrix;
file = _gtk_css_parser_read_url (parser, base);
if (file == NULL)
return FALSE;
path = g_file_get_path (file);
g_object_unref (file);
pixbuf = gdk_pixbuf_new_from_file (path, &error);
g_free (path);
if (pixbuf == NULL)
{
_gtk_css_parser_take_error (parser, error);
return FALSE;
}
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf));
cr = cairo_create (surface);
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
cairo_paint (cr);
pattern = cairo_pattern_create_for_surface (surface);
cairo_matrix_init_scale (&matrix,
gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf));
cairo_pattern_set_matrix (pattern, &matrix);
cairo_surface_destroy (surface);
cairo_destroy (cr);
g_object_unref (pixbuf);
g_value_take_boxed (value, pattern);
}
return TRUE;
}
static cairo_status_t
surface_write (void *closure,
const unsigned char *data,
unsigned int length)
{
g_byte_array_append (closure, data, length);
return CAIRO_STATUS_SUCCESS;
}
static void
surface_print (cairo_surface_t *surface,
GString * string)
{
#if CAIRO_HAS_PNG_FUNCTIONS
GByteArray *array;
char *base64;
array = g_byte_array_new ();
cairo_surface_write_to_png_stream (surface, surface_write, array);
base64 = g_base64_encode (array->data, array->len);
g_byte_array_free (array, TRUE);
g_string_append (string, "url(\"data:image/png;base64,");
g_string_append (string, base64);
g_string_append (string, "\")");
g_free (base64);
#else
g_string_append (string, "none /* you need cairo png functions enabled to make this work */");
#endif
}
static void
pattern_value_print (const GValue *value,
GString *string)
{
cairo_pattern_t *pattern;
cairo_surface_t *surface;
pattern = g_value_get_boxed (value);
if (pattern == NULL)
{
g_string_append (string, "none");
return;
}
switch (cairo_pattern_get_type (pattern))
{
case CAIRO_PATTERN_TYPE_SURFACE:
if (cairo_pattern_get_surface (pattern, &surface) != CAIRO_STATUS_SUCCESS)
{
g_assert_not_reached ();
}
surface_print (surface, string);
break;
case CAIRO_PATTERN_TYPE_SOLID:
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
default:
g_assert_not_reached ();
break;
}
}
static GtkCssValue *
pattern_value_compute (GtkStyleContext *context,
GtkCssValue *specified)
{
const GValue *value = _gtk_css_typed_value_get (specified);
if (G_VALUE_HOLDS (value, GTK_TYPE_GRADIENT))
{
GValue new_value = G_VALUE_INIT;
cairo_pattern_t *gradient;
gradient = gtk_gradient_resolve_for_context (g_value_get_boxed (value), context);
g_value_init (&new_value, CAIRO_GOBJECT_TYPE_PATTERN);
g_value_take_boxed (&new_value, gradient);
return _gtk_css_typed_value_new_take (&new_value);
}
else
return _gtk_css_value_ref (specified);
}
static gboolean
enum_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
int v;
if (enum_parse (parser, G_VALUE_TYPE (value), &v))
{
g_value_set_enum (value, v);
return TRUE;
}
return FALSE;
}
static void
enum_value_print (const GValue *value,
GString *string)
{
enum_print (g_value_get_enum (value), G_VALUE_TYPE (value), string);
}
static gboolean
flags_value_parse (GtkCssParser *parser,
GFile *base,
GValue *value)
{
GFlagsClass *flags_class;
GFlagsValue *flag_value;
guint flags = 0;
char *str;
flags_class = g_type_class_ref (G_VALUE_TYPE (value));
do {
str = _gtk_css_parser_try_ident (parser, TRUE);
if (str == NULL)
{
_gtk_css_parser_error (parser, "Expected an identifier");
g_type_class_unref (flags_class);
return FALSE;
}
flag_value = g_flags_get_value_by_nick (flags_class, str);
if (!flag_value)
{
_gtk_css_parser_error (parser,
"Unknown flag value '%s' for type '%s'",
str, g_type_name (G_VALUE_TYPE (value)));
/* XXX Do we want to return FALSE here? We can get
* forward-compatibility for new values this way
*/
g_free (str);
g_type_class_unref (flags_class);
return FALSE;
}
g_free (str);
}
while (_gtk_css_parser_try (parser, ",", FALSE));
g_type_class_unref (flags_class);
g_value_set_enum (value, flags);
return TRUE;
}
static void
flags_value_print (const GValue *value,
GString *string)
{
GFlagsClass *flags_class;
guint i, flags;
flags_class = g_type_class_ref (G_VALUE_TYPE (value));
flags = g_value_get_flags (value);
for (i = 0; i < flags_class->n_values; i++)
{
GFlagsValue *flags_value = &flags_class->values[i];
if (flags & flags_value->value)
{
if (string->len != 0)
g_string_append (string, ", ");
g_string_append (string, flags_value->value_nick);
}
}
g_type_class_unref (flags_class);
}
/*** API ***/
static void
gtk_css_style_funcs_init (void)
{
if (G_LIKELY (parse_funcs != NULL))
return;
parse_funcs = g_hash_table_new (NULL, NULL);
print_funcs = g_hash_table_new (NULL, NULL);
compute_funcs = g_hash_table_new (NULL, NULL);
register_conversion_function (GDK_TYPE_RGBA,
rgba_value_parse,
rgba_value_print,
rgba_value_compute);
register_conversion_function (GDK_TYPE_COLOR,
color_value_parse,
color_value_print,
color_value_compute);
register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
symbolic_color_value_parse,
symbolic_color_value_print,
NULL);
register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
font_description_value_parse,
font_description_value_print,
NULL);
register_conversion_function (G_TYPE_BOOLEAN,
boolean_value_parse,
boolean_value_print,
NULL);
register_conversion_function (G_TYPE_INT,
int_value_parse,
int_value_print,
NULL);
register_conversion_function (G_TYPE_UINT,
uint_value_parse,
uint_value_print,
NULL);
register_conversion_function (G_TYPE_DOUBLE,
double_value_parse,
double_value_print,
NULL);
register_conversion_function (G_TYPE_FLOAT,
float_value_parse,
float_value_print,
NULL);
register_conversion_function (G_TYPE_STRING,
string_value_parse,
string_value_print,
NULL);
register_conversion_function (GTK_TYPE_THEMING_ENGINE,
theming_engine_value_parse,
theming_engine_value_print,
NULL);
register_conversion_function (GTK_TYPE_BORDER,
border_value_parse,
border_value_print,
NULL);
register_conversion_function (GTK_TYPE_GRADIENT,
gradient_value_parse,
gradient_value_print,
NULL);
register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
pattern_value_parse,
pattern_value_print,
pattern_value_compute);
register_conversion_function (G_TYPE_ENUM,
enum_value_parse,
enum_value_print,
NULL);
register_conversion_function (G_TYPE_FLAGS,
flags_value_parse,
flags_value_print,
NULL);
}
/**
* _gtk_css_style_parse_value:
* @value: the value to parse into. Must be a valid initialized #GValue
* @parser: the parser to parse from
* @base: the base URL for @parser
*
* This is the generic parsing function used for CSS values. If the
* function fails to parse a value, it will emit an error on @parser,
* return %FALSE and not touch @value.
*
* Returns: %TRUE if parsing succeeded.
**/
gboolean
_gtk_css_style_parse_value (GValue *value,
GtkCssParser *parser,
GFile *base)
{
GtkStyleParseFunc func;
g_return_val_if_fail (value != NULL, FALSE);
g_return_val_if_fail (parser != NULL, FALSE);
gtk_css_style_funcs_init ();
func = g_hash_table_lookup (parse_funcs,
GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
if (func == NULL)
func = g_hash_table_lookup (parse_funcs,
GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
if (func == NULL)
{
_gtk_css_parser_error (parser,
"Cannot convert to type '%s'",
g_type_name (G_VALUE_TYPE (value)));
return FALSE;
}
return (*func) (parser, base, value);
}
/**
* _gtk_css_style_print_value:
* @value: an initialized GValue returned from _gtk_css_style_parse()
* @string: the string to print into
*
* Prints @value into @string as a CSS value. If @value is not a
* valid value, a random string will be printed instead.
**/
void
_gtk_css_style_print_value (const GValue *value,
GString *string)
{
GtkStylePrintFunc func;
gtk_css_style_funcs_init ();
func = g_hash_table_lookup (print_funcs,
GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
if (func == NULL)
func = g_hash_table_lookup (print_funcs,
GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
if (func == NULL)
{
char *s = g_strdup_value_contents (value);
g_string_append (string, s);
g_free (s);
return;
}
func (value, string);
}
/**
* _gtk_css_style_compute_value:
* @computed: (out): a value to be filled with the result
* @context: the context to use for computing the value
* @specified: the value to use for the computation
*
* Converts the @specified value into the @computed value using the
* information in @context. The values must have matching types, ie
* @specified must be a result of a call to
* _gtk_css_style_parse_value() with the same type as @computed.
**/
GtkCssValue *
_gtk_css_style_compute_value (GtkStyleContext *context,
GType target_type,
GtkCssValue *specified)
{
GtkStyleComputeFunc func;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
gtk_css_style_funcs_init ();
func = g_hash_table_lookup (compute_funcs,
GSIZE_TO_POINTER (target_type));
if (func == NULL)
func = g_hash_table_lookup (compute_funcs,
GSIZE_TO_POINTER (g_type_fundamental (target_type)));
if (func)
return func (context, specified);
else
return _gtk_css_value_ref (specified);
}