[gtk+] css: Overhaul value parsing
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] css: Overhaul value parsing
- Date: Wed, 18 May 2011 20:26:13 +0000 (UTC)
commit a94ea9a4e338f98750550ce7c17ce0c4bff80b79
Author: Benjamin Otte <otte redhat com>
Date: Fri Apr 8 16:08:28 2011 +0200
css: Overhaul value parsing
Value parsing only sometimes emitted errors. Sometimes it didn't emit
errors but ignored the value, sometimes it took a default, sometimes it
converted it to something it deemed suitable.
While refactoring, I moved the whole GValue <=> char * conversion
routines to a separate file, to make navigating the core css provider
easier.
gtk/Makefile.am | 2 +
gtk/gtkcssprovider.c | 1599 +++++-----------------------------------
gtk/gtkcssstringfuncs.c | 1543 ++++++++++++++++++++++++++++++++++++++
gtk/gtkcssstringfuncsprivate.h | 43 ++
4 files changed, 1791 insertions(+), 1396 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index b558763..6ca52ec 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -387,6 +387,7 @@ gtk_private_h_sources = \
gtkbuttonprivate.h \
gtkcellareaboxcontextprivate.h \
gtkcssproviderprivate.h \
+ gtkcssstringfuncsprivate.h \
gtkcustompaperunixdialog.h \
gtkdndcursors.h \
gtkentryprivate.h \
@@ -512,6 +513,7 @@ gtk_base_c_sources = \
gtkcomboboxtext.c \
gtkcontainer.c \
gtkcssprovider.c \
+ gtkcssstringfuncs.c \
gtkdialog.c \
gtkdrawingarea.c \
gtkeditable.c \
diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c
index 7ec2d30..c03e749 100644
--- a/gtk/gtkcssprovider.c
+++ b/gtk/gtkcssprovider.c
@@ -27,10 +27,8 @@
#include "gtkcssproviderprivate.h"
-#include "gtkanimationdescription.h"
-#include "gtk9slice.h"
-#include "gtkgradient.h"
-#include "gtkthemingengine.h"
+#include "gtkcssstringfuncsprivate.h"
+#include "gtksymboliccolor.h"
#include "gtkstyleprovider.h"
#include "gtkstylecontextprivate.h"
#include "gtkbindings.h"
@@ -841,13 +839,12 @@ static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
static void scanner_apply_scope (GScanner *scanner,
ParserScope scope);
static void css_provider_reset_parser (GtkCssProvider *css_provider);
-static gboolean css_provider_parse_value (GtkCssProvider *css_provider,
- const gchar *value_str,
- GValue *value);
static gboolean gtk_css_provider_load_from_path_internal (GtkCssProvider *css_provider,
const gchar *path,
gboolean reset,
GError **error);
+static void gtk_css_provider_take_error (GtkCssProvider *provider,
+ GError *error);
GQuark
gtk_css_provider_error_quark (void)
@@ -1486,13 +1483,17 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider,
((info->state & state) != 0 &&
(info->state & ~(state)) == 0)))
{
- const gchar *val_str;
+ GError *error = NULL;
- val_str = g_value_get_string (val);
- found = TRUE;
+ found = _gtk_css_value_from_string (value,
+ NULL,
+ g_value_get_string (val),
+ &error);
- css_provider_parse_value (GTK_CSS_PROVIDER (provider), val_str, value);
- break;
+ if (found)
+ break;
+
+ gtk_css_provider_take_error (GTK_CSS_PROVIDER (provider), error);
}
}
@@ -1587,19 +1588,6 @@ gtk_css_provider_take_error (GtkCssProvider *provider,
}
static void
-gtk_css_provider_error_literal (GtkCssProvider *provider,
- GQuark domain,
- gint code,
- const char *message)
-{
- GError *error;
-
- error = g_error_new_literal (domain, code, message);
-
- gtk_css_provider_take_error (provider, error);
-}
-
-static void
gtk_css_provider_error (GtkCssProvider *provider,
GQuark domain,
gint code,
@@ -1944,1286 +1932,177 @@ parse_selector (GtkCssProvider *css_provider,
is_class = (scanner->token == '.');
- g_scanner_get_next_token (scanner);
-
- if (scanner->token != G_TOKEN_IDENTIFIER)
- return G_TOKEN_IDENTIFIER;
-
- selector_path_prepend_glob (path);
- selector_path_prepend_combinator (path, COMBINATOR_CHILD);
-
- if (is_class)
- parse_classes (path, scanner->value.v_identifier);
- else
- {
- if ((pos = strchr (scanner->value.v_identifier, '.')) != NULL)
- *pos = '\0';
-
- selector_path_prepend_name (path, scanner->value.v_identifier);
-
- /* Parse any remaining classes */
- if (pos)
- parse_classes (path, pos + 1);
- }
- }
- else if (is_widget_class_name (scanner->value.v_identifier))
- {
- gchar *pos;
-
- if ((pos = strchr (scanner->value.v_identifier, '#')) != NULL ||
- (pos = strchr (scanner->value.v_identifier, '.')) != NULL)
- {
- gchar *type_name, *name;
- gboolean is_class;
-
- is_class = (*pos == '.');
-
- /* Widget type and name/class put together */
- name = pos + 1;
- *pos = '\0';
- type_name = scanner->value.v_identifier;
-
- selector_path_prepend_type (path, type_name);
-
- /* This is only so there is a direct relationship
- * between widget type and its name.
- */
- selector_path_prepend_combinator (path, COMBINATOR_CHILD);
-
- if (is_class)
- parse_classes (path, name);
- else
- {
- if ((pos = strchr (name, '.')) != NULL)
- *pos = '\0';
-
- selector_path_prepend_name (path, name);
-
- /* Parse any remaining classes */
- if (pos)
- parse_classes (path, pos + 1);
- }
- }
- else
- selector_path_prepend_type (path, scanner->value.v_identifier);
- }
- else if (_gtk_style_context_check_region_name (scanner->value.v_identifier))
- {
- GtkRegionFlags flags = 0;
- gchar *region_name;
-
- region_name = g_strdup (scanner->value.v_identifier);
-
- if (g_scanner_peek_next_token (scanner) == ':')
- {
- ParserSymbol symbol;
-
- g_scanner_get_next_token (scanner);
- css_provider_push_scope (css_provider, SCOPE_PSEUDO_CLASS);
-
- /* Check for the next token being nth-child, parse in that
- * case, and fallback into common state parsing if not.
- */
- if (g_scanner_peek_next_token (scanner) != G_TOKEN_SYMBOL)
- return G_TOKEN_SYMBOL;
-
- symbol = GPOINTER_TO_INT (scanner->next_value.v_symbol);
-
- if (symbol == SYMBOL_FIRST_CHILD ||
- symbol == SYMBOL_LAST_CHILD ||
- symbol == SYMBOL_NTH_CHILD ||
- symbol == SYMBOL_SORTED_CHILD)
- {
- GTokenType token;
-
- if ((token = parse_nth_child (css_provider, scanner, &flags)) != G_TOKEN_NONE)
- return token;
-
- css_provider_pop_scope (css_provider);
- }
- else
- {
- css_provider_pop_scope (css_provider);
- selector_path_prepend_region (path, region_name, 0);
- g_free (region_name);
- break;
- }
- }
-
- selector_path_prepend_region (path, region_name, flags);
- g_free (region_name);
- }
- else if (scanner->value.v_identifier[0] == '*')
- selector_path_prepend_glob (path);
- else
- return G_TOKEN_IDENTIFIER;
-
- g_scanner_get_next_token (scanner);
-
- if (scanner->token == '>')
- {
- selector_path_prepend_combinator (path, COMBINATOR_CHILD);
- g_scanner_get_next_token (scanner);
- }
- }
-
- if (scanner->token == ':')
- {
- /* Add glob selector if path is empty */
- if (selector_path_depth (path) == 0)
- selector_path_prepend_glob (path);
-
- css_provider_push_scope (css_provider, SCOPE_PSEUDO_CLASS);
-
- while (scanner->token == ':')
- {
- GTokenType token;
-
- if ((token = parse_pseudo_class (css_provider, scanner, path)) != G_TOKEN_NONE)
- return token;
-
- g_scanner_get_next_token (scanner);
- }
-
- css_provider_pop_scope (css_provider);
- }
-
- return G_TOKEN_NONE;
-}
-
-#define SKIP_SPACES(s) while (s[0] == ' ' || s[0] == '\t' || s[0] == '\n') s++;
-#define SKIP_SPACES_BACK(s) while (s[0] == ' ' || s[0] == '\t' || s[0] == '\n') s--;
-
-static GtkSymbolicColor *
-symbolic_color_parse_str (const gchar *string,
- gchar **end_ptr)
-{
- GtkSymbolicColor *symbolic_color = NULL;
- gchar *str;
-
- str = (gchar *) string;
- *end_ptr = str;
-
- if (str[0] == '@')
- {
- const gchar *end;
- gchar *name;
-
- str++;
- end = str;
-
- while (*end == '-' || *end == '_' || g_ascii_isalnum (*end))
- end++;
-
- name = g_strndup (str, end - str);
- symbolic_color = gtk_symbolic_color_new_name (name);
- g_free (name);
-
- *end_ptr = (gchar *) end;
- }
- else if (g_str_has_prefix (str, "lighter") ||
- g_str_has_prefix (str, "darker"))
- {
- GtkSymbolicColor *param_color;
- gboolean is_lighter = FALSE;
-
- is_lighter = g_str_has_prefix (str, "lighter");
-
- if (is_lighter)
- str += strlen ("lighter");
- else
- str += strlen ("darker");
-
- SKIP_SPACES (str);
-
- if (*str != '(')
- {
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- str++;
- SKIP_SPACES (str);
- param_color = symbolic_color_parse_str (str, end_ptr);
-
- if (!param_color)
- return NULL;
-
- str = *end_ptr;
- SKIP_SPACES (str);
- *end_ptr = (gchar *) str;
-
- if (*str != ')')
- {
- gtk_symbolic_color_unref (param_color);
- return NULL;
- }
-
- if (is_lighter)
- symbolic_color = gtk_symbolic_color_new_shade (param_color, 1.3);
- else
- symbolic_color = gtk_symbolic_color_new_shade (param_color, 0.7);
-
- gtk_symbolic_color_unref (param_color);
- (*end_ptr)++;
- }
- else if (g_str_has_prefix (str, "shade") ||
- g_str_has_prefix (str, "alpha"))
- {
- GtkSymbolicColor *param_color;
- gboolean is_shade = FALSE;
- gdouble factor;
-
- is_shade = g_str_has_prefix (str, "shade");
-
- if (is_shade)
- str += strlen ("shade");
- else
- str += strlen ("alpha");
-
- SKIP_SPACES (str);
-
- if (*str != '(')
- {
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- str++;
- SKIP_SPACES (str);
- param_color = symbolic_color_parse_str (str, end_ptr);
-
- if (!param_color)
- return NULL;
-
- str = *end_ptr;
- SKIP_SPACES (str);
-
- if (str[0] != ',')
- {
- gtk_symbolic_color_unref (param_color);
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- str++;
- SKIP_SPACES (str);
- factor = g_ascii_strtod (str, end_ptr);
-
- str = *end_ptr;
- SKIP_SPACES (str);
- *end_ptr = (gchar *) str;
-
- if (str[0] != ')')
- {
- gtk_symbolic_color_unref (param_color);
- return NULL;
- }
-
- if (is_shade)
- symbolic_color = gtk_symbolic_color_new_shade (param_color, factor);
- else
- symbolic_color = gtk_symbolic_color_new_alpha (param_color, factor);
-
- gtk_symbolic_color_unref (param_color);
- (*end_ptr)++;
- }
- else if (g_str_has_prefix (str, "mix"))
- {
- GtkSymbolicColor *color1, *color2;
- gdouble factor;
-
- str += strlen ("mix");
- SKIP_SPACES (str);
-
- if (*str != '(')
- {
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- str++;
- SKIP_SPACES (str);
- color1 = symbolic_color_parse_str (str, end_ptr);
-
- if (!color1)
- return NULL;
-
- str = *end_ptr;
- SKIP_SPACES (str);
-
- if (str[0] != ',')
- {
- gtk_symbolic_color_unref (color1);
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- str++;
- SKIP_SPACES (str);
- color2 = symbolic_color_parse_str (str, end_ptr);
-
- if (!color2 || *end_ptr[0] != ',')
- {
- gtk_symbolic_color_unref (color1);
- return NULL;
- }
-
- str = *end_ptr;
- SKIP_SPACES (str);
-
- if (str[0] != ',')
- {
- gtk_symbolic_color_unref (color1);
- gtk_symbolic_color_unref (color2);
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- str++;
- SKIP_SPACES (str);
- factor = g_ascii_strtod (str, end_ptr);
-
- str = *end_ptr;
- SKIP_SPACES (str);
- *end_ptr = (gchar *) str;
-
- if (str[0] != ')')
- {
- gtk_symbolic_color_unref (color1);
- gtk_symbolic_color_unref (color2);
- return NULL;
- }
-
- symbolic_color = gtk_symbolic_color_new_mix (color1, color2, factor);
- gtk_symbolic_color_unref (color1);
- gtk_symbolic_color_unref (color2);
- (*end_ptr)++;
- }
- else
- {
- GdkRGBA color;
- gchar *color_str;
- const gchar *end;
-
- end = str + 1;
-
- if (str[0] == '#')
- {
- /* Color in hex format */
- while (g_ascii_isxdigit (*end))
- end++;
- }
- else if (g_str_has_prefix (str, "rgb"))
- {
- /* color in rgb/rgba format */
- while (*end != ')' && *end != '\0')
- end++;
-
- if (*end == ')')
- end++;
- }
- else
- {
- /* Color name */
- while (*end != '\0' &&
- (g_ascii_isalnum (*end) || *end == ' '))
- end++;
- }
-
- color_str = g_strndup (str, end - str);
- *end_ptr = (gchar *) end;
-
- if (!gdk_rgba_parse (&color, color_str))
- {
- g_free (color_str);
- return NULL;
- }
-
- symbolic_color = gtk_symbolic_color_new_literal (&color);
- g_free (color_str);
- }
-
- return symbolic_color;
-}
-
-static GtkSymbolicColor *
-symbolic_color_parse (const gchar *str)
-{
- GtkSymbolicColor *color;
- gchar *end;
-
- color = symbolic_color_parse_str (str, &end);
-
- if (*end != '\0')
- {
- if (color)
- {
- gtk_symbolic_color_unref (color);
- color = NULL;
- }
- }
-
- return color;
-}
-
-static GtkGradient *
-gradient_parse_str (const gchar *str,
- gchar **end_ptr)
-{
- GtkGradient *gradient = NULL;
- gdouble coords[6];
- gchar *end;
- guint i;
-
- if (g_str_has_prefix (str, "-gtk-gradient"))
- {
- cairo_pattern_type_t type;
-
- str += strlen ("-gtk-gradient");
- SKIP_SPACES (str);
-
- if (*str != '(')
- {
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- str++;
- SKIP_SPACES (str);
-
- /* Parse gradient type */
- if (g_str_has_prefix (str, "linear"))
- {
- type = CAIRO_PATTERN_TYPE_LINEAR;
- str += strlen ("linear");
- }
- else if (g_str_has_prefix (str, "radial"))
- {
- type = CAIRO_PATTERN_TYPE_RADIAL;
- str += strlen ("radial");
- }
- else
- {
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- SKIP_SPACES (str);
-
- /* Parse start/stop position parameters */
- for (i = 0; i < 2; i++)
- {
- if (*str != ',')
- {
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- str++;
- SKIP_SPACES (str);
-
- if (strncmp (str, "left", 4) == 0)
- {
- coords[i * 3] = 0;
- str += strlen ("left");
- }
- else if (strncmp (str, "right", 5) == 0)
- {
- coords[i * 3] = 1;
- str += strlen ("right");
- }
- else if (strncmp (str, "center", 6) == 0)
- {
- coords[i * 3] = 0.5;
- str += strlen ("center");
- }
- else
- {
- coords[i * 3] = g_ascii_strtod (str, &end);
-
- if (str == end)
- {
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- str = end;
- }
-
- SKIP_SPACES (str);
-
- if (strncmp (str, "top", 3) == 0)
- {
- coords[(i * 3) + 1] = 0;
- str += strlen ("top");
- }
- else if (strncmp (str, "bottom", 6) == 0)
- {
- coords[(i * 3) + 1] = 1;
- str += strlen ("bottom");
- }
- else if (strncmp (str, "center", 6) == 0)
- {
- coords[(i * 3) + 1] = 0.5;
- str += strlen ("center");
- }
- else
- {
- coords[(i * 3) + 1] = g_ascii_strtod (str, &end);
-
- if (str == end)
- {
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- str = end;
- }
-
- SKIP_SPACES (str);
-
- if (type == CAIRO_PATTERN_TYPE_RADIAL)
- {
- /* Parse radius */
- if (*str != ',')
- {
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- str++;
- SKIP_SPACES (str);
-
- coords[(i * 3) + 2] = g_ascii_strtod (str, &end);
- str = end;
-
- SKIP_SPACES (str);
- }
- }
-
- if (type == CAIRO_PATTERN_TYPE_LINEAR)
- gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
- else
- gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
- coords[3], coords[4], coords[5]);
-
- while (*str == ',')
- {
- GtkSymbolicColor *color;
- gdouble position;
-
- if (*str != ',')
- {
- *end_ptr = (gchar *) str;
- return gradient;
- }
-
- str++;
- SKIP_SPACES (str);
-
- if (g_str_has_prefix (str, "from"))
- {
- position = 0;
- str += strlen ("from");
- SKIP_SPACES (str);
-
- if (*str != '(')
- {
- *end_ptr = (gchar *) str;
- return gradient;
- }
- }
- else if (g_str_has_prefix (str, "to"))
- {
- position = 1;
- str += strlen ("to");
- SKIP_SPACES (str);
-
- if (*str != '(')
- {
- *end_ptr = (gchar *) str;
- return gradient;
- }
- }
- else if (g_str_has_prefix (str, "color-stop"))
- {
- str += strlen ("color-stop");
- SKIP_SPACES (str);
-
- if (*str != '(')
- {
- *end_ptr = (gchar *) str;
- return gradient;
- }
-
- str++;
- SKIP_SPACES (str);
-
- position = g_ascii_strtod (str, &end);
-
- str = end;
- SKIP_SPACES (str);
-
- if (*str != ',')
- {
- *end_ptr = (gchar *) str;
- return gradient;
- }
- }
- else
- {
- *end_ptr = (gchar *) str;
- return gradient;
- }
-
- str++;
- SKIP_SPACES (str);
-
- color = symbolic_color_parse_str (str, &end);
-
- str = end;
- SKIP_SPACES (str);
-
- if (*str != ')')
- {
- if (color)
- gtk_symbolic_color_unref (color);
- *end_ptr = (gchar *) str;
- return gradient;
- }
-
- str++;
- SKIP_SPACES (str);
-
- if (color)
- {
- gtk_gradient_add_color_stop (gradient, position, color);
- gtk_symbolic_color_unref (color);
- }
- }
-
- if (*str != ')')
- {
- *end_ptr = (gchar *) str;
- return gradient;
- }
-
- str++;
- }
-
- *end_ptr = (gchar *) str;
-
- return gradient;
-}
-
-static gchar *
-path_parse_str (GtkCssProvider *css_provider,
- const gchar *str,
- gchar **end_ptr)
-{
- gchar *path, *chr;
- const gchar *start, *end;
- start = str;
-
- if (g_str_has_prefix (str, "url"))
- {
- str += strlen ("url");
- SKIP_SPACES (str);
-
- if (*str != '(')
- {
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- chr = strchr (str, ')');
- if (!chr)
- {
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- end = chr + 1;
-
- str++;
- SKIP_SPACES (str);
-
- if (*str == '"' || *str == '\'')
- {
- const gchar *p;
- p = str;
- str++;
-
- chr--;
- SKIP_SPACES_BACK (chr);
-
- if (*chr != *p || chr == p)
- {
- *end_ptr = (gchar *)str;
- return NULL;
- }
- }
- else
- {
- *end_ptr = (gchar *)str;
- return NULL;
- }
-
- path = g_strndup (str, chr - str);
- g_strstrip (path);
-
- *end_ptr = (gchar *)end;
- }
- else
- {
- path = g_strdup (str);
- *end_ptr = (gchar *)str + strlen (str);
- }
-
- /* Always return an absolute path */
- if (!g_path_is_absolute (path))
- {
- GtkCssProviderPrivate *priv;
- gchar *dirname, *full_path;
-
- priv = css_provider->priv;
-
- /* Use relative path to the current CSS file path, if any */
- if (priv->scanner->input_name)
- dirname = g_path_get_dirname (priv->scanner->input_name);
- else
- dirname = g_get_current_dir ();
-
- full_path = g_build_filename (dirname, path, NULL);
- g_free (path);
- g_free (dirname);
-
- path = full_path;
- }
-
- if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
- {
- gtk_css_provider_error (css_provider, G_FILE_ERROR, G_FILE_ERROR_EXIST,
- "File doesn't exist: %s", path);
- g_free (path);
- path = NULL;
- *end_ptr = (gchar *)start;
- }
-
- return path;
-}
-
-static gchar *
-path_parse (GtkCssProvider *css_provider,
- const gchar *str)
-{
- gchar *path;
- gchar *end;
-
- path = path_parse_str (css_provider, str, &end);
-
- if (!path)
- return NULL;
-
- if (*end != '\0')
- {
- gtk_css_provider_error (css_provider,
- GTK_CSS_PROVIDER_ERROR,
- GTK_CSS_PROVIDER_ERROR_FAILED,
- "Error parsing path");
- g_free (path);
- path = NULL;
- }
-
- return path;
-}
-
-static Gtk9Slice *
-slice_parse_str (GtkCssProvider *css_provider,
- const gchar *str,
- gchar **end_ptr)
-{
- gdouble distance_top, distance_bottom;
- gdouble distance_left, distance_right;
- GtkSliceSideModifier mods[2];
- GdkPixbuf *pixbuf;
- Gtk9Slice *slice;
- gchar *path;
- gint i = 0;
- GError *error = NULL;
-
- SKIP_SPACES (str);
-
- /* Parse image url */
- path = path_parse_str (css_provider, str, end_ptr);
-
- if (!path)
- return NULL;
-
- str = *end_ptr;
- SKIP_SPACES (str);
-
- /* Parse top/left/bottom/right distances */
- distance_top = g_ascii_strtod (str, end_ptr);
-
- str = *end_ptr;
- SKIP_SPACES (str);
-
- distance_right = g_ascii_strtod (str, end_ptr);
-
- str = *end_ptr;
- SKIP_SPACES (str);
-
- distance_bottom = g_ascii_strtod (str, end_ptr);
-
- str = *end_ptr;
- SKIP_SPACES (str);
-
- distance_left = g_ascii_strtod (str, end_ptr);
-
- str = *end_ptr;
- SKIP_SPACES (str);
-
- while (*str && i < 2)
- {
- if (g_str_has_prefix (str, "stretch"))
- {
- str += strlen ("stretch");
- mods[i] = GTK_SLICE_STRETCH;
- }
- else if (g_str_has_prefix (str, "repeat"))
- {
- str += strlen ("repeat");
- mods[i] = GTK_SLICE_REPEAT;
- }
- else
- {
- g_free (path);
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- SKIP_SPACES (str);
- i++;
- }
-
- *end_ptr = (gchar *) str;
-
- if (*str != '\0')
- {
- g_free (path);
- return NULL;
- }
-
- if (i != 2)
- {
- /* Fill in second modifier, same as the first */
- mods[1] = mods[0];
- }
-
- pixbuf = gdk_pixbuf_new_from_file (path, &error);
- g_free (path);
-
- if (!pixbuf)
- {
- gtk_css_provider_take_error (css_provider, error);
- *end_ptr = (gchar *) str;
- return NULL;
- }
-
- slice = _gtk_9slice_new (pixbuf,
- distance_top, distance_bottom,
- distance_left, distance_right,
- mods[0], mods[1]);
- g_object_unref (pixbuf);
-
- return slice;
-}
-
-static gdouble
-unit_parse_str (const gchar *str,
- gchar **end_str)
-{
- gdouble unit;
-
- SKIP_SPACES (str);
- unit = g_ascii_strtod (str, end_str);
- str = *end_str;
-
- /* Now parse the unit type, if any. We
- * don't admit spaces between these.
- */
- if (*str != ' ' && *str != '\0')
- {
- while (**end_str != ' ' && **end_str != '\0')
- (*end_str)++;
-
- /* Only handle pixels at the moment */
- if (strncmp (str, "px", 2) != 0)
- {
- gchar *type;
-
- type = g_strndup (str, *end_str - str);
- g_warning ("Unknown unit '%s', only pixel units are "
- "currently supported in CSS style", type);
- g_free (type);
- }
- }
-
- return unit;
-}
-
-static GtkBorder *
-border_parse_str (const gchar *str,
- gchar **end_str)
-{
- gdouble first, second, third, fourth;
- GtkBorder *border;
-
- border = gtk_border_new ();
-
- SKIP_SPACES (str);
- if (!g_ascii_isdigit (*str) && *str != '-')
- return border;
-
- first = unit_parse_str (str, end_str);
- str = *end_str;
- SKIP_SPACES (str);
-
- if (!g_ascii_isdigit (*str) && *str != '-')
- {
- border->left = border->right = border->top = border->bottom = (gint) first;
- *end_str = (gchar *) str;
- return border;
- }
-
- second = unit_parse_str (str, end_str);
- str = *end_str;
- SKIP_SPACES (str);
-
- if (!g_ascii_isdigit (*str) && *str != '-')
- {
- border->top = border->bottom = (gint) first;
- border->left = border->right = (gint) second;
- *end_str = (gchar *) str;
- return border;
- }
-
- third = unit_parse_str (str, end_str);
- str = *end_str;
- SKIP_SPACES (str);
-
- if (!g_ascii_isdigit (*str) && *str != '-')
- {
- border->top = (gint) first;
- border->left = border->right = (gint) second;
- border->bottom = (gint) third;
- *end_str = (gchar *) str;
- return border;
- }
-
- fourth = unit_parse_str (str, end_str);
-
- border->top = (gint) first;
- border->right = (gint) second;
- border->bottom = (gint) third;
- border->left = (gint) fourth;
-
- return border;
-}
-
-static void
-resolve_binding_sets (const gchar *value_str,
- GValue *value)
-{
- GPtrArray *array;
- gchar **bindings, **str;
-
- bindings = g_strsplit (value_str, ",", -1);
- array = g_ptr_array_new ();
-
- for (str = bindings; *str; str++)
- {
- GtkBindingSet *binding_set;
-
- binding_set = gtk_binding_set_find (g_strstrip (*str));
-
- if (!binding_set)
- continue;
-
- g_ptr_array_add (array, binding_set);
- }
-
- g_value_take_boxed (value, array);
- g_strfreev (bindings);
-}
-
-static gboolean
-css_provider_parse_value (GtkCssProvider *css_provider,
- const gchar *value_str,
- GValue *value)
-{
- GtkCssProviderPrivate *priv;
- GType type;
- gboolean parsed = TRUE;
- gchar *end = NULL;
-
- priv = css_provider->priv;
- type = G_VALUE_TYPE (value);
+ g_scanner_get_next_token (scanner);
- if (type == GDK_TYPE_RGBA ||
- type == GDK_TYPE_COLOR)
- {
- GdkRGBA rgba;
- GdkColor color;
-
- if (type == GDK_TYPE_RGBA &&
- gdk_rgba_parse (&rgba, value_str))
- g_value_set_boxed (value, &rgba);
- else if (type == GDK_TYPE_COLOR &&
- gdk_color_parse (value_str, &color))
- g_value_set_boxed (value, &color);
- else
- {
- GtkSymbolicColor *symbolic_color;
+ if (scanner->token != G_TOKEN_IDENTIFIER)
+ return G_TOKEN_IDENTIFIER;
- symbolic_color = symbolic_color_parse_str (value_str, &end);
+ selector_path_prepend_glob (path);
+ selector_path_prepend_combinator (path, COMBINATOR_CHILD);
- if (symbolic_color)
+ if (is_class)
+ parse_classes (path, scanner->value.v_identifier);
+ else
{
- g_value_unset (value);
- g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
- g_value_take_boxed (value, symbolic_color);
+ if ((pos = strchr (scanner->value.v_identifier, '.')) != NULL)
+ *pos = '\0';
+
+ selector_path_prepend_name (path, scanner->value.v_identifier);
+
+ /* Parse any remaining classes */
+ if (pos)
+ parse_classes (path, pos + 1);
}
- else
- parsed = FALSE;
}
- }
- else if (type == PANGO_TYPE_FONT_DESCRIPTION)
- {
- PangoFontDescription *font_desc;
+ else if (is_widget_class_name (scanner->value.v_identifier))
+ {
+ gchar *pos;
- font_desc = pango_font_description_from_string (value_str);
- g_value_take_boxed (value, font_desc);
- }
- else if (type == G_TYPE_BOOLEAN)
- {
- if (value_str[0] == '1' ||
- g_ascii_strcasecmp (value_str, "true") == 0)
- g_value_set_boolean (value, TRUE);
- else
- g_value_set_boolean (value, FALSE);
- }
- else if (type == G_TYPE_INT)
- g_value_set_int (value, atoi (value_str));
- else if (type == G_TYPE_UINT)
- g_value_set_uint (value, (guint) atoi (value_str));
- else if (type == G_TYPE_DOUBLE)
- g_value_set_double (value, g_ascii_strtod (value_str, NULL));
- else if (type == G_TYPE_FLOAT)
- g_value_set_float (value, (gfloat) g_ascii_strtod (value_str, NULL));
- else if (type == GTK_TYPE_THEMING_ENGINE)
- {
- GtkThemingEngine *engine;
+ if ((pos = strchr (scanner->value.v_identifier, '#')) != NULL ||
+ (pos = strchr (scanner->value.v_identifier, '.')) != NULL)
+ {
+ gchar *type_name, *name;
+ gboolean is_class;
- engine = gtk_theming_engine_load (value_str);
- if (engine)
- g_value_set_object (value, engine);
- else
- parsed = FALSE;
- }
- else if (type == GTK_TYPE_ANIMATION_DESCRIPTION)
- {
- GtkAnimationDescription *desc;
+ is_class = (*pos == '.');
- desc = _gtk_animation_description_from_string (value_str);
+ /* Widget type and name/class put together */
+ name = pos + 1;
+ *pos = '\0';
+ type_name = scanner->value.v_identifier;
- if (desc)
- g_value_take_boxed (value, desc);
- else
- parsed = FALSE;
- }
- else if (type == GTK_TYPE_BORDER)
- {
- GtkBorder *border;
+ selector_path_prepend_type (path, type_name);
- border = border_parse_str (value_str, &end);
- g_value_take_boxed (value, border);
- }
- else if (type == CAIRO_GOBJECT_TYPE_PATTERN)
- {
- GtkGradient *gradient;
+ /* This is only so there is a direct relationship
+ * between widget type and its name.
+ */
+ selector_path_prepend_combinator (path, COMBINATOR_CHILD);
+
+ if (is_class)
+ parse_classes (path, name);
+ else
+ {
+ if ((pos = strchr (name, '.')) != NULL)
+ *pos = '\0';
- gradient = gradient_parse_str (value_str, &end);
+ selector_path_prepend_name (path, name);
- if (gradient)
- {
- g_value_unset (value);
- g_value_init (value, GTK_TYPE_GRADIENT);
- g_value_take_boxed (value, gradient);
+ /* Parse any remaining classes */
+ if (pos)
+ parse_classes (path, pos + 1);
+ }
+ }
+ else
+ selector_path_prepend_type (path, scanner->value.v_identifier);
}
- else
+ else if (_gtk_style_context_check_region_name (scanner->value.v_identifier))
{
- gchar *path;
- GdkPixbuf *pixbuf;
+ GtkRegionFlags flags = 0;
+ gchar *region_name;
- path = path_parse_str (css_provider, value_str, &end);
+ region_name = g_strdup (scanner->value.v_identifier);
- if (path)
+ if (g_scanner_peek_next_token (scanner) == ':')
{
- pixbuf = gdk_pixbuf_new_from_file (path, NULL);
- g_free (path);
+ ParserSymbol symbol;
+
+ g_scanner_get_next_token (scanner);
+ css_provider_push_scope (css_provider, SCOPE_PSEUDO_CLASS);
+
+ /* Check for the next token being nth-child, parse in that
+ * case, and fallback into common state parsing if not.
+ */
+ if (g_scanner_peek_next_token (scanner) != G_TOKEN_SYMBOL)
+ return G_TOKEN_SYMBOL;
+
+ symbol = GPOINTER_TO_INT (scanner->next_value.v_symbol);
- if (pixbuf)
+ if (symbol == SYMBOL_FIRST_CHILD ||
+ symbol == SYMBOL_LAST_CHILD ||
+ symbol == SYMBOL_NTH_CHILD ||
+ symbol == SYMBOL_SORTED_CHILD)
{
- cairo_surface_t *surface;
- cairo_pattern_t *pattern;
- cairo_t *cr;
- cairo_matrix_t matrix;
-
- 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);
+ GTokenType token;
+
+ if ((token = parse_nth_child (css_provider, scanner, &flags)) != G_TOKEN_NONE)
+ return token;
+
+ css_provider_pop_scope (css_provider);
}
else
- parsed = FALSE;
+ {
+ css_provider_pop_scope (css_provider);
+ selector_path_prepend_region (path, region_name, 0);
+ g_free (region_name);
+ break;
+ }
}
- else
- parsed = FALSE;
+
+ selector_path_prepend_region (path, region_name, flags);
+ g_free (region_name);
}
- }
- else if (G_TYPE_IS_ENUM (type))
- {
- GEnumClass *enum_class;
- GEnumValue *enum_value;
+ else if (scanner->value.v_identifier[0] == '*')
+ selector_path_prepend_glob (path);
+ else
+ return G_TOKEN_IDENTIFIER;
- enum_class = g_type_class_ref (type);
- enum_value = g_enum_get_value_by_nick (enum_class, value_str);
+ g_scanner_get_next_token (scanner);
- if (!enum_value)
+ if (scanner->token == '>')
{
- gtk_css_provider_error (css_provider,
- GTK_CSS_PROVIDER_ERROR,
- GTK_CSS_PROVIDER_ERROR_FAILED,
- "Unknown value '%s' for enum type '%s'",
- value_str, g_type_name (type));
- parsed = FALSE;
+ selector_path_prepend_combinator (path, COMBINATOR_CHILD);
+ g_scanner_get_next_token (scanner);
}
- else
- g_value_set_enum (value, enum_value->value);
-
- g_type_class_unref (enum_class);
}
- else if (G_TYPE_IS_FLAGS (type))
- {
- GFlagsClass *flags_class;
- GFlagsValue *flag_value;
- guint flags = 0;
- gchar *ptr;
- flags_class = g_type_class_ref (type);
+ if (scanner->token == ':')
+ {
+ /* Add glob selector if path is empty */
+ if (selector_path_depth (path) == 0)
+ selector_path_prepend_glob (path);
- /* Parse comma separated values */
- ptr = strchr (value_str, ',');
+ css_provider_push_scope (css_provider, SCOPE_PSEUDO_CLASS);
- while (ptr && parsed)
+ while (scanner->token == ':')
{
- gchar *flag_str;
-
- *ptr = '\0';
- ptr++;
-
- flag_str = (gchar *) value_str;
- flag_value = g_flags_get_value_by_nick (flags_class,
- g_strstrip (flag_str));
+ GTokenType token;
- if (!flag_value)
- {
- gtk_css_provider_error (css_provider,
- GTK_CSS_PROVIDER_ERROR,
- GTK_CSS_PROVIDER_ERROR_FAILED,
- "Unknown flag '%s' for type '%s'",
- value_str, g_type_name (type));
- parsed = FALSE;
- }
- else
- flags |= flag_value->value;
+ if ((token = parse_pseudo_class (css_provider, scanner, path)) != G_TOKEN_NONE)
+ return token;
- value_str = ptr;
- ptr = strchr (value_str, ',');
+ g_scanner_get_next_token (scanner);
}
- /* Store last/only value */
- flag_value = g_flags_get_value_by_nick (flags_class, value_str);
-
- if (!flag_value)
- {
- gtk_css_provider_error (css_provider,
- GTK_CSS_PROVIDER_ERROR,
- GTK_CSS_PROVIDER_ERROR_FAILED,
- "Unknown flag '%s' for type '%s'",
- value_str, g_type_name (type));
- parsed = FALSE;
- }
- else
- flags |= flag_value->value;
+ css_provider_pop_scope (css_provider);
+ }
- if (parsed)
- g_value_set_enum (value, flags);
+ return G_TOKEN_NONE;
+}
- g_type_class_unref (flags_class);
- }
- else if (type == GTK_TYPE_9SLICE)
- {
- Gtk9Slice *slice;
+static void
+resolve_binding_sets (const gchar *value_str,
+ GValue *value)
+{
+ GPtrArray *array;
+ gchar **bindings, **str;
- slice = slice_parse_str (css_provider, value_str, &end);
+ bindings = g_strsplit (value_str, ",", -1);
+ array = g_ptr_array_new ();
- if (slice)
- g_value_take_boxed (value, slice);
- else
- parsed = FALSE;
- }
- else
+ for (str = bindings; *str; str++)
{
- gtk_css_provider_error (css_provider,
- GTK_CSS_PROVIDER_ERROR,
- GTK_CSS_PROVIDER_ERROR_FAILED,
- "Cannot parse string '%s' for type %s",
- value_str, g_type_name (type));
- parsed = FALSE;
- }
+ GtkBindingSet *binding_set;
- if (end)
- SKIP_SPACES (end);
+ binding_set = gtk_binding_set_find (g_strstrip (*str));
- if (end && *end)
- {
- parsed = FALSE;
+ if (!binding_set)
+ continue;
- gtk_css_provider_error_literal (css_provider,
- GTK_CSS_PROVIDER_ERROR,
- GTK_CSS_PROVIDER_ERROR_FAILED,
- "Failed to parse value");
+ g_ptr_array_add (array, binding_set);
}
- return parsed;
+ g_value_take_boxed (value, array);
+ g_strfreev (bindings);
}
static GTokenType
@@ -3250,6 +2129,7 @@ parse_rule (GtkCssProvider *css_provider,
{
GtkSymbolicColor *color;
gchar *color_name, *color_str;
+ GError *error = NULL;
/* Directive is a color mapping */
g_scanner_get_next_token (scanner);
@@ -3271,11 +2151,10 @@ parse_rule (GtkCssProvider *css_provider,
}
color_str = g_strstrip (scanner->value.v_identifier);
- color = symbolic_color_parse (color_str);
-
+ color = _gtk_css_parse_symbolic_color (color_str, &error);
if (!color)
{
- gtk_css_provider_invalid_token (css_provider, "Color definition");
+ gtk_css_provider_take_error (css_provider, error);
return G_TOKEN_IDENTIFIER;
}
@@ -3295,33 +2174,53 @@ parse_rule (GtkCssProvider *css_provider,
GSList *state_backup;
gboolean loaded;
gchar *path = NULL;
+ GFile *base, *actual;
+ char *dirname;
+ GError *error = NULL;
css_provider_push_scope (css_provider, SCOPE_VALUE);
g_scanner_get_next_token (scanner);
if (scanner->token == G_TOKEN_IDENTIFIER &&
g_str_has_prefix (scanner->value.v_identifier, "url"))
- path = path_parse (css_provider,
- g_strstrip (scanner->value.v_identifier));
+ path = g_strstrip (scanner->value.v_identifier);
else if (scanner->token == G_TOKEN_STRING)
- path = path_parse (css_provider,
- g_strstrip (scanner->value.v_string));
-
- if (path == NULL)
+ path = g_strstrip (scanner->value.v_string);
+ else
{
gtk_css_provider_invalid_token (css_provider, "File URL");
return G_TOKEN_IDENTIFIER;
}
+ if (priv->scanner->input_name)
+ dirname = g_path_get_dirname (priv->scanner->input_name);
+ else
+ dirname = g_get_current_dir ();
+
+ base = g_file_new_for_path (dirname);
+ g_free (dirname);
+
+ actual = _gtk_css_parse_url (base, path, NULL, &error);
+ g_object_unref (base);
+
+ if (actual == NULL)
+ {
+ gtk_css_provider_take_error (css_provider, error);
+ return G_TOKEN_IDENTIFIER;
+ }
+
css_provider_pop_scope (css_provider);
g_scanner_get_next_token (scanner);
if (scanner->token != ';')
{
- g_free (path);
+ g_object_unref (actual);
return ';';
}
+ path = g_file_get_path (actual);
+ g_object_unref (actual);
+
/* Snapshot current parser state and scanner in order to restore after importing */
state_backup = priv->state;
scanner_backup = priv->scanner;
@@ -3342,10 +2241,7 @@ parse_rule (GtkCssProvider *css_provider,
g_free (path);
if (!loaded)
- {
- gtk_css_provider_invalid_token (css_provider, "File URL");
- return G_TOKEN_IDENTIFIER;
- }
+ return G_TOKEN_IDENTIFIER;
else
return G_TOKEN_NONE;
}
@@ -3495,7 +2391,6 @@ parse_rule (GtkCssProvider *css_provider,
}
value_str = scanner->value.v_identifier;
- SKIP_SPACES (value_str);
g_strchomp (value_str);
if (pspec)
@@ -3524,10 +2419,6 @@ parse_rule (GtkCssProvider *css_provider,
g_value_set_string (val, value_str);
g_hash_table_insert (priv->cur_properties, prop, val);
}
- else if (!parse_func && css_provider_parse_value (css_provider, value_str, val))
- {
- g_hash_table_insert (priv->cur_properties, prop, val);
- }
else if (parse_func)
{
GError *error = NULL;
@@ -3539,12 +2430,38 @@ parse_rule (GtkCssProvider *css_provider,
}
else
{
- g_value_unset (val);
- g_slice_free (GValue, val);
- g_free (prop);
+ GError *error = NULL;
+ GFile *base;
+ char *dirname;
+ gboolean success;
+
+ if (priv->scanner->input_name)
+ dirname = g_path_get_dirname (priv->scanner->input_name);
+ else
+ dirname = g_get_current_dir ();
+
+ base = g_file_new_for_path (dirname);
+ g_free (dirname);
+
+ success = _gtk_css_value_from_string (val,
+ base,
+ value_str,
+ &error);
- gtk_css_provider_invalid_token (css_provider, "Property value");
- goto find_end_of_declaration;
+ g_object_unref (base);
+
+ if (success)
+ {
+ g_hash_table_insert (priv->cur_properties, prop, val);
+ }
+ else
+ {
+ g_value_unset (val);
+ g_slice_free (GValue, val);
+ g_free (prop);
+
+ gtk_css_provider_take_error (css_provider, error);
+ }
}
}
else if (prop[0] == '-')
@@ -4318,119 +3235,6 @@ gtk_css_provider_get_named (const gchar *name,
}
static void
-gtk_css_provider_print_value (const GValue *value,
- GString * str)
-{
- char *s;
-
- if (G_VALUE_TYPE (value) == GTK_TYPE_BORDER)
- {
- const GtkBorder *border = g_value_get_boxed (value);
-
- if (border == NULL)
- g_string_append (str, "none");
- else if (border->left != border->right)
- g_string_append_printf (str, "%d %d %d %d", border->top, border->right, border->bottom, border->left);
- else if (border->top != border->bottom)
- g_string_append_printf (str, "%d %d %d", border->top, border->right, border->bottom);
- else if (border->top != border->left)
- g_string_append_printf (str, "%d %d", border->top, border->right);
- else
- g_string_append_printf (str, "%d", border->top);
-
- return;
- }
- else if (G_VALUE_TYPE (value) == GDK_TYPE_RGBA)
- {
- const GdkRGBA *rgba = g_value_get_boxed (value);
- if (rgba == NULL)
- g_string_append (str, "none");
- else
- {
- s = gdk_rgba_to_string (g_value_get_boxed (value));
- g_string_append (str, s);
- g_free (s);
- }
- return;
- }
- else if (G_VALUE_TYPE (value) == GTK_TYPE_SYMBOLIC_COLOR)
- {
- GtkSymbolicColor *color = g_value_get_boxed (value);
-
- if (color == NULL)
- g_string_append (str, "none");
- else
- {
- s = gtk_symbolic_color_to_string (color);
- g_string_append (str, s);
- g_free (s);
- }
- return;
- }
- else if (G_VALUE_TYPE (value) == GTK_TYPE_ANIMATION_DESCRIPTION)
- {
- GtkAnimationDescription *desc = g_value_get_boxed (value);
-
- if (desc == NULL)
- g_string_append (str, "none");
- else
- {
- s = _gtk_animation_description_to_string (desc);
- g_string_append (str, s);
- g_free (s);
- }
- return;
- }
- else if (G_VALUE_TYPE (value) == GTK_TYPE_GRADIENT)
- {
- GtkGradient *gradient = g_value_get_boxed (value);
-
- if (gradient == NULL)
- g_string_append (str, "none");
- else
- {
- s = gtk_gradient_to_string (gradient);
- g_string_append (str, s);
- g_free (s);
- }
- return;
- }
- else if (G_VALUE_TYPE (value) == PANGO_TYPE_FONT_DESCRIPTION)
- {
- PangoFontDescription *desc = g_value_get_boxed (value);
-
- if (desc == NULL)
- g_string_append (str, "none");
- else
- {
- s = pango_font_description_to_string (desc);
- g_string_append (str, s);
- g_free (s);
- }
- return;
- }
-
- if (g_type_is_a (G_VALUE_TYPE (value), G_TYPE_ENUM))
- {
- GEnumClass *enum_class;
- GEnumValue *enum_value;
-
- enum_class = g_type_class_ref (G_VALUE_TYPE (value));
- enum_value = g_enum_get_value (enum_class, g_value_get_enum (value));
-
- g_string_append (str, enum_value->value_nick);
-
- g_type_class_unref (enum_class);
- return;
- }
-
- /* fall back to boring strdup in the worst case */
- s = g_strdup_value_contents (value);
- g_string_append (str, s);
- g_free (s);
-}
-
-static void
selector_path_print (const SelectorPath *path,
GString * str)
{
@@ -4539,6 +3343,7 @@ selector_style_info_print (const SelectorStyleInfo *info,
GString *str)
{
GList *keys, *walk;
+ char *s;
selector_path_print (info->path, str);
@@ -4556,7 +3361,9 @@ selector_style_info_print (const SelectorStyleInfo *info,
g_string_append (str, " ");
g_string_append (str, name);
g_string_append (str, ": ");
- gtk_css_provider_print_value (value, str);
+ s = _gtk_css_value_to_string (value);
+ g_string_append (str, s);
+ g_free (s);
g_string_append (str, ";\n");
}
diff --git a/gtk/gtkcssstringfuncs.c b/gtk/gtkcssstringfuncs.c
new file mode 100644
index 0000000..f995faf
--- /dev/null
+++ b/gtk/gtkcssstringfuncs.c
@@ -0,0 +1,1543 @@
+/* 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gtkcssstringfuncsprivate.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <cairo-gobject.h>
+
+#include "gtkcssprovider.h"
+
+/* the actual parsers we have */
+#include "gtkanimationdescription.h"
+#include "gtk9slice.h"
+#include "gtkgradient.h"
+#include "gtkthemingengine.h"
+
+typedef gboolean (* FromStringFunc) (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error);
+typedef char * (* ToStringFunc) (const GValue *value);
+
+static GHashTable *from_string_funcs = NULL;
+static GHashTable *to_string_funcs = NULL;
+
+static void
+register_conversion_function (GType type,
+ FromStringFunc from_string,
+ ToStringFunc to_string)
+{
+ if (from_string)
+ g_hash_table_insert (from_string_funcs, GSIZE_TO_POINTER (type), from_string);
+ if (to_string)
+ g_hash_table_insert (to_string_funcs, GSIZE_TO_POINTER (type), to_string);
+}
+
+static gboolean
+set_default_error (GError **error,
+ GType type)
+{
+ g_set_error (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Could not convert property value to type '%s'",
+ g_type_name (type));
+ return FALSE;
+}
+
+/*** IMPLEMENTATIONS ***/
+
+#define SKIP_SPACES(s) while (g_ascii_isspace (*(s))) (s)++
+#define SKIP_SPACES_BACK(s) while (g_ascii_isspace (*(s))) (s)--
+
+static GtkSymbolicColor *
+symbolic_color_parse_str (const gchar *string,
+ gchar **end_ptr)
+{
+ GtkSymbolicColor *symbolic_color = NULL;
+ gchar *str;
+
+ str = (gchar *) string;
+ *end_ptr = str;
+
+ if (str[0] == '@')
+ {
+ const gchar *end;
+ gchar *name;
+
+ str++;
+ end = str;
+
+ while (*end == '-' || *end == '_' || g_ascii_isalnum (*end))
+ end++;
+
+ name = g_strndup (str, end - str);
+ symbolic_color = gtk_symbolic_color_new_name (name);
+ g_free (name);
+
+ *end_ptr = (gchar *) end;
+ }
+ else if (g_str_has_prefix (str, "lighter") ||
+ g_str_has_prefix (str, "darker"))
+ {
+ GtkSymbolicColor *param_color;
+ gboolean is_lighter = FALSE;
+
+ is_lighter = g_str_has_prefix (str, "lighter");
+
+ if (is_lighter)
+ str += strlen ("lighter");
+ else
+ str += strlen ("darker");
+
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+ param_color = symbolic_color_parse_str (str, end_ptr);
+
+ if (!param_color)
+ return NULL;
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+ *end_ptr = (gchar *) str;
+
+ if (*str != ')')
+ {
+ gtk_symbolic_color_unref (param_color);
+ return NULL;
+ }
+
+ if (is_lighter)
+ symbolic_color = gtk_symbolic_color_new_shade (param_color, 1.3);
+ else
+ symbolic_color = gtk_symbolic_color_new_shade (param_color, 0.7);
+
+ gtk_symbolic_color_unref (param_color);
+ (*end_ptr)++;
+ }
+ else if (g_str_has_prefix (str, "shade") ||
+ g_str_has_prefix (str, "alpha"))
+ {
+ GtkSymbolicColor *param_color;
+ gboolean is_shade = FALSE;
+ gdouble factor;
+
+ is_shade = g_str_has_prefix (str, "shade");
+
+ if (is_shade)
+ str += strlen ("shade");
+ else
+ str += strlen ("alpha");
+
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+ param_color = symbolic_color_parse_str (str, end_ptr);
+
+ if (!param_color)
+ return NULL;
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+
+ if (str[0] != ',')
+ {
+ gtk_symbolic_color_unref (param_color);
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+ factor = g_ascii_strtod (str, end_ptr);
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+ *end_ptr = (gchar *) str;
+
+ if (str[0] != ')')
+ {
+ gtk_symbolic_color_unref (param_color);
+ return NULL;
+ }
+
+ if (is_shade)
+ symbolic_color = gtk_symbolic_color_new_shade (param_color, factor);
+ else
+ symbolic_color = gtk_symbolic_color_new_alpha (param_color, factor);
+
+ gtk_symbolic_color_unref (param_color);
+ (*end_ptr)++;
+ }
+ else if (g_str_has_prefix (str, "mix"))
+ {
+ GtkSymbolicColor *color1, *color2;
+ gdouble factor;
+
+ str += strlen ("mix");
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+ color1 = symbolic_color_parse_str (str, end_ptr);
+
+ if (!color1)
+ return NULL;
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+
+ if (str[0] != ',')
+ {
+ gtk_symbolic_color_unref (color1);
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+ color2 = symbolic_color_parse_str (str, end_ptr);
+
+ if (!color2 || *end_ptr[0] != ',')
+ {
+ gtk_symbolic_color_unref (color1);
+ return NULL;
+ }
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+
+ if (str[0] != ',')
+ {
+ gtk_symbolic_color_unref (color1);
+ gtk_symbolic_color_unref (color2);
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+ factor = g_ascii_strtod (str, end_ptr);
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+ *end_ptr = (gchar *) str;
+
+ if (str[0] != ')')
+ {
+ gtk_symbolic_color_unref (color1);
+ gtk_symbolic_color_unref (color2);
+ return NULL;
+ }
+
+ symbolic_color = gtk_symbolic_color_new_mix (color1, color2, factor);
+ gtk_symbolic_color_unref (color1);
+ gtk_symbolic_color_unref (color2);
+ (*end_ptr)++;
+ }
+ else
+ {
+ GdkRGBA color;
+ gchar *color_str;
+ const gchar *end;
+
+ end = str + 1;
+
+ if (str[0] == '#')
+ {
+ /* Color in hex format */
+ while (g_ascii_isxdigit (*end))
+ end++;
+ }
+ else if (g_str_has_prefix (str, "rgb"))
+ {
+ /* color in rgb/rgba format */
+ while (*end != ')' && *end != '\0')
+ end++;
+
+ if (*end == ')')
+ end++;
+ }
+ else
+ {
+ /* Color name */
+ while (*end != '\0' &&
+ (g_ascii_isalnum (*end) || *end == ' '))
+ end++;
+ }
+
+ color_str = g_strndup (str, end - str);
+ *end_ptr = (gchar *) end;
+
+ if (!gdk_rgba_parse (&color, color_str))
+ {
+ g_free (color_str);
+ return NULL;
+ }
+
+ symbolic_color = gtk_symbolic_color_new_literal (&color);
+ g_free (color_str);
+ }
+
+ return symbolic_color;
+}
+
+static gboolean
+rgba_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ GtkSymbolicColor *symbolic;
+ GdkRGBA rgba;
+
+ if (gdk_rgba_parse (&rgba, str))
+ {
+ g_value_set_boxed (value, &rgba);
+ return TRUE;
+ }
+
+ symbolic = _gtk_css_parse_symbolic_color (str, error);
+ if (symbolic == NULL)
+ return FALSE;
+
+ g_value_unset (value);
+ g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
+ g_value_take_boxed (value, symbolic);
+ return TRUE;
+}
+
+static char *
+rgba_value_to_string (const GValue *value)
+{
+ const GdkRGBA *rgba = g_value_get_boxed (value);
+
+ if (rgba == NULL)
+ return g_strdup ("none");
+
+ return gdk_rgba_to_string (rgba);
+}
+
+static gboolean
+color_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ GtkSymbolicColor *symbolic;
+ GdkColor color;
+
+ if (gdk_color_parse (str, &color))
+ {
+ g_value_set_boxed (value, &color);
+ return TRUE;
+ }
+
+ symbolic = _gtk_css_parse_symbolic_color (str, error);
+ if (symbolic == NULL)
+ return FALSE;
+
+ g_value_unset (value);
+ g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
+ g_value_take_boxed (value, symbolic);
+ return TRUE;
+}
+
+static char *
+color_value_to_string (const GValue *value)
+{
+ const GdkColor *color = g_value_get_boxed (value);
+
+ if (color == NULL)
+ return g_strdup ("none");
+
+ return gdk_color_to_string (color);
+}
+
+static gboolean
+symbolic_color_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ GtkSymbolicColor *symbolic;
+
+ symbolic = _gtk_css_parse_symbolic_color (str, error);
+ if (symbolic == NULL)
+ return FALSE;
+
+ g_value_take_boxed (value, symbolic);
+ return TRUE;
+}
+
+static char *
+symbolic_color_value_to_string (const GValue *value)
+{
+ GtkSymbolicColor *symbolic = g_value_get_boxed (value);
+
+ if (symbolic == NULL)
+ return g_strdup ("none");
+
+ return gtk_symbolic_color_to_string (symbolic);
+}
+
+static gboolean
+font_description_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ PangoFontDescription *font_desc;
+
+ font_desc = pango_font_description_from_string (str);
+ g_value_take_boxed (value, font_desc);
+ return TRUE;
+}
+
+static char *
+font_description_value_to_string (const GValue *value)
+{
+ const PangoFontDescription *desc = g_value_get_boxed (value);
+
+ if (desc == NULL)
+ return g_strdup ("none");
+
+ return pango_font_description_to_string (desc);
+}
+
+static gboolean
+boolean_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ if (g_ascii_strcasecmp (str, "true") == 0 ||
+ g_ascii_strcasecmp (str, "1") == 0)
+ {
+ g_value_set_boolean (value, TRUE);
+ return TRUE;
+ }
+ else if (g_ascii_strcasecmp (str, "false") == 0 ||
+ g_ascii_strcasecmp (str, "0") == 0)
+ {
+ g_value_set_boolean (value, FALSE);
+ return TRUE;
+ }
+
+ return set_default_error (error, G_VALUE_TYPE (value));
+}
+
+static char *
+boolean_value_to_string (const GValue *value)
+{
+ if (g_value_get_boolean (value))
+ return g_strdup ("true");
+ else
+ return g_strdup ("false");
+}
+
+static gboolean
+int_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ gint64 i;
+ char *end;
+
+ i = g_ascii_strtoll (str, &end, 10);
+
+ if (*end != '\0')
+ return set_default_error (error, G_VALUE_TYPE (value));
+
+ if (i > G_MAXINT || i < G_MININT)
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Number too big");
+ return FALSE;
+ }
+
+ g_value_set_int (value, i);
+ return TRUE;
+}
+
+static char *
+int_value_to_string (const GValue *value)
+{
+ return g_strdup_printf ("%d", g_value_get_int (value));
+}
+
+static gboolean
+uint_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ guint64 u;
+ char *end;
+
+ u = g_ascii_strtoull (str, &end, 10);
+
+ if (*end != '\0')
+ return set_default_error (error, G_VALUE_TYPE (value));
+
+ if (u > G_MAXUINT)
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Number too big");
+ return FALSE;
+ }
+
+ g_value_set_uint (value, u);
+ return TRUE;
+}
+
+static char *
+uint_value_to_string (const GValue *value)
+{
+ return g_strdup_printf ("%u", g_value_get_uint (value));
+}
+
+static gboolean
+double_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ double d;
+ char *end;
+
+ d = g_ascii_strtod (str, &end);
+
+ if (*end != '\0')
+ return set_default_error (error, G_VALUE_TYPE (value));
+
+ if (errno == ERANGE)
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Number not representable");
+ return FALSE;
+ }
+
+ g_value_set_double (value, d);
+ return TRUE;
+}
+
+static char *
+double_value_to_string (const GValue *value)
+{
+ char buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_ascii_dtostr (buf, sizeof (buf), g_value_get_double (value));
+
+ return g_strdup (buf);
+}
+
+static gboolean
+float_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ double d;
+ char *end;
+
+ d = g_ascii_strtod (str, &end);
+
+ if (*end != '\0')
+ return set_default_error (error, G_VALUE_TYPE (value));
+
+ if (errno == ERANGE)
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Number not representable");
+ return FALSE;
+ }
+
+ g_value_set_float (value, d);
+ return TRUE;
+}
+
+static char *
+float_value_to_string (const GValue *value)
+{
+ char buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_ascii_dtostr (buf, sizeof (buf), g_value_get_float (value));
+
+ return g_strdup (buf);
+}
+
+static gboolean
+theming_engine_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ GtkThemingEngine *engine;
+
+ engine = gtk_theming_engine_load (str);
+ if (engine == NULL)
+ {
+ g_set_error (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Themeing engine '%s' not found", str);
+ return FALSE;
+ }
+
+ g_value_set_object (value, engine);
+ return TRUE;
+}
+
+static char *
+theming_engine_value_to_string (const GValue *value)
+{
+ GtkThemingEngine *engine;
+ char *name;
+
+ engine = g_value_get_object (value);
+ if (engine == NULL)
+ return g_strdup ("none");
+
+ /* XXX: gtk_theming_engine_get_name()? */
+ g_object_get (engine, "name", &name, NULL);
+
+ return name;
+}
+
+static gboolean
+animation_description_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ GtkAnimationDescription *desc;
+
+ desc = _gtk_animation_description_from_string (str);
+
+ if (desc == NULL)
+ return set_default_error (error, G_VALUE_TYPE (value));
+
+ g_value_take_boxed (value, desc);
+ return TRUE;
+}
+
+static char *
+animation_description_value_to_string (const GValue *value)
+{
+ GtkAnimationDescription *desc = g_value_get_boxed (value);
+
+ if (desc == NULL)
+ return g_strdup ("none");
+
+ return _gtk_animation_description_to_string (desc);
+}
+
+static gboolean
+parse_border_value (const char *str,
+ gint16 *value,
+ const char **end,
+ GError **error)
+{
+ gint64 d;
+
+ d = g_ascii_strtoll (str, (char **) end, 10);
+
+ if (d > G_MAXINT16 || d < 0)
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Number out of range for border");
+ return FALSE;
+ }
+
+ if (str == *end)
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "No number given for border value");
+ return FALSE;
+ }
+
+ /* Skip optional unit type.
+ * We only handle pixels at the moment.
+ */
+ if (strncmp (*end, "px", 2) == 0)
+ *end += 2;
+
+ if (**end != '\0' &&
+ !g_ascii_isspace (**end))
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Junk at end of border value");
+ return FALSE;
+ }
+
+ SKIP_SPACES (*end);
+
+ *value = d;
+ return TRUE;
+}
+
+static gboolean
+border_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ GtkBorder *border;
+
+ border = gtk_border_new ();
+
+ if (!parse_border_value (str, &border->top, &str, error))
+ return FALSE;
+
+ if (*str == '\0')
+ border->right = border->top;
+ else
+ if (!parse_border_value (str, &border->right, &str, error))
+ return FALSE;
+
+ if (*str == '\0')
+ border->bottom = border->top;
+ else
+ if (!parse_border_value (str, &border->bottom, &str, error))
+ return FALSE;
+
+ if (*str == '\0')
+ border->left = border->right;
+ else
+ if (!parse_border_value (str, &border->left, &str, error))
+ return FALSE;
+
+ if (*str != '\0')
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Junk at end of border value");
+ return FALSE;
+ }
+
+ g_value_take_boxed (value, border);
+ return TRUE;
+}
+
+static char *
+border_value_to_string (const GValue *value)
+{
+ const GtkBorder *border = g_value_get_boxed (value);
+
+ if (border == NULL)
+ return g_strdup ("none");
+ else if (border->left != border->right)
+ return g_strdup_printf ("%d %d %d %d", border->top, border->right, border->bottom, border->left);
+ else if (border->top != border->bottom)
+ return g_strdup_printf ("%d %d %d", border->top, border->right, border->bottom);
+ else if (border->top != border->left)
+ return g_strdup_printf ("%d %d", border->top, border->right);
+ else
+ return g_strdup_printf ("%d", border->top);
+}
+
+static gboolean
+gradient_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ GtkGradient *gradient;
+ cairo_pattern_type_t type;
+ gdouble coords[6];
+ gchar *end;
+ guint i;
+
+ str += strlen ("-gtk-gradient");
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Expected '(' after '-gtk-gradient'");
+ return FALSE;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ /* Parse gradient type */
+ if (g_str_has_prefix (str, "linear"))
+ {
+ type = CAIRO_PATTERN_TYPE_LINEAR;
+ str += strlen ("linear");
+ }
+ else if (g_str_has_prefix (str, "radial"))
+ {
+ type = CAIRO_PATTERN_TYPE_RADIAL;
+ str += strlen ("radial");
+ }
+ else
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Gradient type must be 'radial' or 'linear'");
+ return FALSE;
+ }
+
+ SKIP_SPACES (str);
+
+ /* Parse start/stop position parameters */
+ for (i = 0; i < 2; i++)
+ {
+ if (*str != ',')
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Expected ','");
+ return FALSE;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ if (strncmp (str, "left", 4) == 0)
+ {
+ coords[i * 3] = 0;
+ str += strlen ("left");
+ }
+ else if (strncmp (str, "right", 5) == 0)
+ {
+ coords[i * 3] = 1;
+ str += strlen ("right");
+ }
+ else if (strncmp (str, "center", 6) == 0)
+ {
+ coords[i * 3] = 0.5;
+ str += strlen ("center");
+ }
+ else
+ {
+ coords[i * 3] = g_ascii_strtod (str, &end);
+
+ if (str == end)
+ return set_default_error (error, G_VALUE_TYPE (value));
+
+ str = end;
+ }
+
+ SKIP_SPACES (str);
+
+ if (strncmp (str, "top", 3) == 0)
+ {
+ coords[(i * 3) + 1] = 0;
+ str += strlen ("top");
+ }
+ else if (strncmp (str, "bottom", 6) == 0)
+ {
+ coords[(i * 3) + 1] = 1;
+ str += strlen ("bottom");
+ }
+ else if (strncmp (str, "center", 6) == 0)
+ {
+ coords[(i * 3) + 1] = 0.5;
+ str += strlen ("center");
+ }
+ else
+ {
+ coords[(i * 3) + 1] = g_ascii_strtod (str, &end);
+
+ if (str == end)
+ return set_default_error (error, G_VALUE_TYPE (value));
+
+ str = end;
+ }
+
+ SKIP_SPACES (str);
+
+ if (type == CAIRO_PATTERN_TYPE_RADIAL)
+ {
+ /* Parse radius */
+ if (*str != ',')
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Expected ','");
+ return FALSE;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ coords[(i * 3) + 2] = g_ascii_strtod (str, &end);
+ str = end;
+
+ SKIP_SPACES (str);
+ }
+ }
+
+ if (type == CAIRO_PATTERN_TYPE_LINEAR)
+ gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
+ else
+ gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
+ coords[3], coords[4], coords[5]);
+
+ while (*str == ',')
+ {
+ GtkSymbolicColor *color;
+ gdouble position;
+
+ str++;
+ SKIP_SPACES (str);
+
+ if (g_str_has_prefix (str, "from"))
+ {
+ position = 0;
+ str += strlen ("from");
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ g_object_unref (gradient);
+ return set_default_error (error, G_VALUE_TYPE (value));
+ }
+ }
+ else if (g_str_has_prefix (str, "to"))
+ {
+ position = 1;
+ str += strlen ("to");
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ g_object_unref (gradient);
+ return set_default_error (error, G_VALUE_TYPE (value));
+ }
+ }
+ else if (g_str_has_prefix (str, "color-stop"))
+ {
+ str += strlen ("color-stop");
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ g_object_unref (gradient);
+ return set_default_error (error, G_VALUE_TYPE (value));
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ position = g_ascii_strtod (str, &end);
+
+ str = end;
+ SKIP_SPACES (str);
+
+ if (*str != ',')
+ {
+ g_object_unref (gradient);
+ return set_default_error (error, G_VALUE_TYPE (value));
+ }
+ }
+ else
+ {
+ g_object_unref (gradient);
+ return set_default_error (error, G_VALUE_TYPE (value));
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ color = symbolic_color_parse_str (str, &end);
+
+ str = end;
+ SKIP_SPACES (str);
+
+ if (*str != ')')
+ {
+ if (color)
+ gtk_symbolic_color_unref (color);
+ g_object_unref (gradient);
+ return set_default_error (error, G_VALUE_TYPE (value));
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ if (color)
+ {
+ gtk_gradient_add_color_stop (gradient, position, color);
+ gtk_symbolic_color_unref (color);
+ }
+ }
+
+ if (*str != ')')
+ {
+ g_object_unref (gradient);
+ return set_default_error (error, G_VALUE_TYPE (value));
+ }
+
+ g_value_take_boxed (value, gradient);
+ return TRUE;
+}
+
+static char *
+gradient_value_to_string (const GValue *value)
+{
+ GtkGradient *gradient = g_value_get_boxed (value);
+
+ if (gradient == NULL)
+ return g_strdup ("none");
+
+ return gtk_gradient_to_string (gradient);
+}
+
+static gboolean
+pattern_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ if (g_str_has_prefix (str, "-gtk-gradient"))
+ {
+ g_value_unset (value);
+ g_value_init (value, GTK_TYPE_GRADIENT);
+ return gradient_value_from_string (str, base, value, error);
+ }
+ else
+ {
+ gchar *path;
+ GdkPixbuf *pixbuf;
+ GFile *file;
+
+ file = _gtk_css_parse_url (base, str, NULL, error);
+ 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)
+ return FALSE;
+
+ cairo_surface_t *surface;
+ cairo_pattern_t *pattern;
+ cairo_t *cr;
+ cairo_matrix_t matrix;
+
+ 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 gboolean
+slice_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ gdouble distance_top, distance_bottom;
+ gdouble distance_left, distance_right;
+ GtkSliceSideModifier mods[2];
+ GdkPixbuf *pixbuf;
+ Gtk9Slice *slice;
+ GFile *file;
+ gint i = 0;
+ char *path;
+
+ SKIP_SPACES (str);
+
+ /* Parse image url */
+ file = _gtk_css_parse_url (base, str, (char **) &str, error);
+ if (!file)
+ return FALSE;
+
+ SKIP_SPACES (str);
+
+ /* Parse top/left/bottom/right distances */
+ distance_top = g_ascii_strtod (str, (char **) &str);
+
+ SKIP_SPACES (str);
+
+ distance_right = g_ascii_strtod (str, (char **) &str);
+
+ SKIP_SPACES (str);
+
+ distance_bottom = g_ascii_strtod (str, (char **) &str);
+
+ SKIP_SPACES (str);
+
+ distance_left = g_ascii_strtod (str, (char **) &str);
+
+ SKIP_SPACES (str);
+
+ while (*str && i < 2)
+ {
+ if (g_str_has_prefix (str, "stretch"))
+ {
+ str += strlen ("stretch");
+ mods[i] = GTK_SLICE_STRETCH;
+ }
+ else if (g_str_has_prefix (str, "repeat"))
+ {
+ str += strlen ("repeat");
+ mods[i] = GTK_SLICE_REPEAT;
+ }
+ else
+ {
+ g_object_unref (file);
+ return set_default_error (error, G_VALUE_TYPE (value));
+ }
+
+ SKIP_SPACES (str);
+ i++;
+ }
+
+ if (*str != '\0')
+ {
+ g_object_unref (file);
+ return set_default_error (error, G_VALUE_TYPE (value));
+ }
+
+ if (i != 2)
+ {
+ /* Fill in second modifier, same as the first */
+ mods[1] = mods[0];
+ }
+
+ path = g_file_get_path (file);
+ pixbuf = gdk_pixbuf_new_from_file (path, error);
+ g_free (path);
+ if (!pixbuf)
+ return FALSE;
+
+ slice = _gtk_9slice_new (pixbuf,
+ distance_top, distance_bottom,
+ distance_left, distance_right,
+ mods[0], mods[1]);
+ g_object_unref (pixbuf);
+
+ g_value_take_boxed (value, slice);
+ return TRUE;
+}
+
+static gboolean
+enum_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+
+ enum_class = g_type_class_ref (G_VALUE_TYPE (value));
+ enum_value = g_enum_get_value_by_nick (enum_class, str);
+
+ if (!enum_value)
+ {
+ g_set_error (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_FAILED,
+ "Unknown value '%s' for enum type '%s'",
+ str, g_type_name (G_VALUE_TYPE (value)));
+ g_type_class_unref (enum_class);
+ return FALSE;
+ }
+
+ g_value_set_enum (value, enum_value->value);
+ g_type_class_unref (enum_class);
+ return TRUE;
+}
+
+static char *
+enum_value_to_string (const GValue *value)
+{
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+ char *s;
+
+ enum_class = g_type_class_ref (G_VALUE_TYPE (value));
+ enum_value = g_enum_get_value (enum_class, g_value_get_enum (value));
+
+ s = g_strdup (enum_value->value_nick);
+
+ g_type_class_unref (enum_class);
+
+ return s;
+}
+
+static gboolean
+flags_value_from_string (const char *str,
+ GFile *base,
+ GValue *value,
+ GError **error)
+{
+ GFlagsClass *flags_class;
+ GFlagsValue *flag_value;
+ guint flags = 0;
+ char **strv;
+ guint i;
+
+ strv = g_strsplit (str, ",", -1);
+
+ flags_class = g_type_class_ref (G_VALUE_TYPE (value));
+
+ for (i = 0; strv[i]; i++)
+ {
+ strv[i] = g_strstrip (strv[i]);
+
+ flag_value = g_flags_get_value_by_nick (flags_class, strv[i]);
+ if (!flag_value)
+ {
+ g_set_error (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_NAME,
+ "Unknown flag value '%s' for type '%s'",
+ strv[i], g_type_name (G_VALUE_TYPE (value)));
+ g_type_class_unref (flags_class);
+ return FALSE;
+ }
+
+ flags |= flag_value->value;
+ }
+
+ g_strfreev (strv);
+ g_type_class_unref (flags_class);
+
+ g_value_set_enum (value, flags);
+
+ return TRUE;
+}
+
+static char *
+flags_value_to_string (const GValue *value)
+{
+ GFlagsClass *flags_class;
+ GString *string;
+ guint i, flags;
+
+ flags_class = g_type_class_ref (G_VALUE_TYPE (value));
+ flags = g_value_get_flags (value);
+ string = g_string_new (NULL);
+
+ 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);
+
+ return g_string_free (string, FALSE);
+}
+
+/*** API ***/
+
+static void
+css_string_funcs_init (void)
+{
+ if (G_LIKELY (from_string_funcs != NULL))
+ return;
+
+ from_string_funcs = g_hash_table_new (NULL, NULL);
+ to_string_funcs = g_hash_table_new (NULL, NULL);
+
+ register_conversion_function (GDK_TYPE_RGBA,
+ rgba_value_from_string,
+ rgba_value_to_string);
+ register_conversion_function (GDK_TYPE_COLOR,
+ color_value_from_string,
+ color_value_to_string);
+ register_conversion_function (GTK_TYPE_SYMBOLIC_COLOR,
+ symbolic_color_value_from_string,
+ symbolic_color_value_to_string);
+ register_conversion_function (PANGO_TYPE_FONT_DESCRIPTION,
+ font_description_value_from_string,
+ font_description_value_to_string);
+ register_conversion_function (G_TYPE_BOOLEAN,
+ boolean_value_from_string,
+ boolean_value_to_string);
+ register_conversion_function (G_TYPE_INT,
+ int_value_from_string,
+ int_value_to_string);
+ register_conversion_function (G_TYPE_UINT,
+ uint_value_from_string,
+ uint_value_to_string);
+ register_conversion_function (G_TYPE_DOUBLE,
+ double_value_from_string,
+ double_value_to_string);
+ register_conversion_function (G_TYPE_FLOAT,
+ float_value_from_string,
+ float_value_to_string);
+ register_conversion_function (GTK_TYPE_THEMING_ENGINE,
+ theming_engine_value_from_string,
+ theming_engine_value_to_string);
+ register_conversion_function (GTK_TYPE_ANIMATION_DESCRIPTION,
+ animation_description_value_from_string,
+ animation_description_value_to_string);
+ register_conversion_function (GTK_TYPE_BORDER,
+ border_value_from_string,
+ border_value_to_string);
+ register_conversion_function (GTK_TYPE_GRADIENT,
+ gradient_value_from_string,
+ gradient_value_to_string);
+ register_conversion_function (CAIRO_GOBJECT_TYPE_PATTERN,
+ pattern_value_from_string,
+ NULL);
+ register_conversion_function (GTK_TYPE_9SLICE,
+ slice_value_from_string,
+ NULL);
+ register_conversion_function (G_TYPE_ENUM,
+ enum_value_from_string,
+ enum_value_to_string);
+ register_conversion_function (G_TYPE_FLAGS,
+ flags_value_from_string,
+ flags_value_to_string);
+}
+
+gboolean
+_gtk_css_value_from_string (GValue *value,
+ GFile *base,
+ const char *string,
+ GError **error)
+{
+ FromStringFunc func;
+
+ g_return_val_if_fail (string != NULL, FALSE);
+ g_return_val_if_fail (string[0] != 0, FALSE);
+
+ css_string_funcs_init ();
+
+ func = g_hash_table_lookup (from_string_funcs,
+ GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
+ if (func == NULL)
+ func = g_hash_table_lookup (from_string_funcs,
+ GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
+
+ if (func == NULL)
+ {
+ g_set_error (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Cannot convert to type '%s'",
+ g_type_name (G_VALUE_TYPE (value)));
+ return FALSE;
+ }
+
+ return (*func) (string, base, value, error);
+}
+
+char *
+_gtk_css_value_to_string (const GValue *value)
+{
+ ToStringFunc func;
+
+ css_string_funcs_init ();
+
+ func = g_hash_table_lookup (to_string_funcs,
+ GSIZE_TO_POINTER (G_VALUE_TYPE (value)));
+ if (func == NULL)
+ func = g_hash_table_lookup (to_string_funcs,
+ GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value))));
+
+ if (func)
+ return func (value);
+
+ return g_strdup_value_contents (value);
+}
+
+GtkSymbolicColor *
+_gtk_css_parse_symbolic_color (const char *str,
+ GError **error)
+{
+ GtkSymbolicColor *color;
+ gchar *end;
+
+ color = symbolic_color_parse_str (str, &end);
+
+ if (*end != '\0')
+ {
+ if (color)
+ {
+ gtk_symbolic_color_unref (color);
+ color = NULL;
+ }
+
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Failed to parse symbolic color");
+ }
+
+ return color;
+}
+
+GFile *
+_gtk_css_parse_url (GFile *base,
+ const char *str,
+ char **end,
+ GError **error)
+{
+ gchar *path, *chr;
+ GFile *file;
+
+ if (g_str_has_prefix (str, "url"))
+ {
+ str += strlen ("url");
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Expected '(' after 'url'");
+ return NULL;
+ }
+
+ chr = strchr (str, ')');
+ if (!chr)
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "No closing ')' found for 'url'");
+ return NULL;
+ }
+
+ if (end)
+ *end = chr + 1;
+
+ str++;
+ SKIP_SPACES (str);
+
+ if (*str == '"' || *str == '\'')
+ {
+ const gchar *p;
+ p = str;
+
+ str++;
+ chr--;
+ SKIP_SPACES_BACK (chr);
+
+ if (*chr != *p || chr == p)
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "Did not find closing quote for url");
+ return NULL;
+ }
+ }
+ else
+ {
+ g_set_error_literal (error,
+ GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_PROPERTY_VALUE,
+ "url not properly escaped");
+ return NULL;
+ }
+
+ path = g_strndup (str, chr - str);
+ g_strstrip (path);
+ }
+ else
+ {
+ path = g_strdup (str);
+ if (end)
+ *end = (gchar *) str + strlen (str);
+ }
+
+ file = g_file_resolve_relative_path (base, path);
+ g_free (path);
+
+ return file;
+}
diff --git a/gtk/gtkcssstringfuncsprivate.h b/gtk/gtkcssstringfuncsprivate.h
new file mode 100644
index 0000000..900d38c
--- /dev/null
+++ b/gtk/gtkcssstringfuncsprivate.h
@@ -0,0 +1,43 @@
+/* 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CSS_STRINGFUNCS_PRIVATE_H__
+#define __GTK_CSS_STRINGFUNCS_PRIVATE_H__
+
+#include <gtk/gtksymboliccolor.h>
+
+G_BEGIN_DECLS
+
+gboolean _gtk_css_value_from_string (GValue *value,
+ GFile *base,
+ const char *string,
+ GError **error);
+char * _gtk_css_value_to_string (const GValue *value);
+
+GtkSymbolicColor * _gtk_css_parse_symbolic_color (const char *str,
+ GError **error);
+
+GFile * _gtk_css_parse_url (GFile *base,
+ const char *str,
+ char **end,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_STRINGFUNCS_PRIVATE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]