forked from AuroraMiddleware/gtk
40283e7c27
... 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.
1134 lines
30 KiB
C
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);
|
|
}
|
|
|