forked from AuroraMiddleware/gtk
2e6bb2a0c9
The hack in gtk_style_context_get_font() was causing segfaults in
combobox code. This is not acceptable and I'm not awake enough to fix
it, so just reverting until it's fixed sanely is easiest.
This reverts commit cf6bfbdb17
.
948 lines
22 KiB
C
948 lines
22 KiB
C
/* GTK - The GIMP Toolkit
|
|
* Copyright (C) 2011 Benjamin Otte <otte@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 "gtkcssparserprivate.h"
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
/* just for the errors, yay! */
|
|
#include "gtkcssprovider.h"
|
|
|
|
#define NEWLINE_CHARS "\r\n"
|
|
#define WHITESPACE_CHARS "\f \t"
|
|
#define NMSTART "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
#define NMCHAR NMSTART "01234567890-_"
|
|
#define URLCHAR NMCHAR "!#$%&*~"
|
|
|
|
#define GTK_IS_CSS_PARSER(parser) ((parser) != NULL)
|
|
|
|
struct _GtkCssParser
|
|
{
|
|
const char *data;
|
|
GtkCssParserErrorFunc error_func;
|
|
gpointer user_data;
|
|
|
|
const char *line_start;
|
|
guint line;
|
|
};
|
|
|
|
GtkCssParser *
|
|
_gtk_css_parser_new (const char *data,
|
|
GtkCssParserErrorFunc error_func,
|
|
gpointer user_data)
|
|
{
|
|
GtkCssParser *parser;
|
|
|
|
g_return_val_if_fail (data != NULL, NULL);
|
|
|
|
parser = g_slice_new0 (GtkCssParser);
|
|
|
|
parser->data = data;
|
|
parser->error_func = error_func;
|
|
parser->user_data = user_data;
|
|
|
|
parser->line_start = data;
|
|
parser->line = 1;
|
|
|
|
return parser;
|
|
}
|
|
|
|
void
|
|
_gtk_css_parser_free (GtkCssParser *parser)
|
|
{
|
|
g_return_if_fail (GTK_IS_CSS_PARSER (parser));
|
|
|
|
g_slice_free (GtkCssParser, parser);
|
|
}
|
|
|
|
gboolean
|
|
_gtk_css_parser_is_eof (GtkCssParser *parser)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), TRUE);
|
|
|
|
return *parser->data == 0;
|
|
}
|
|
|
|
gboolean
|
|
_gtk_css_parser_begins_with (GtkCssParser *parser,
|
|
char c)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), TRUE);
|
|
|
|
return *parser->data == c;
|
|
}
|
|
|
|
guint
|
|
_gtk_css_parser_get_line (GtkCssParser *parser)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), 1);
|
|
|
|
return parser->line;
|
|
}
|
|
|
|
guint
|
|
_gtk_css_parser_get_position (GtkCssParser *parser)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), 1);
|
|
|
|
return parser->data - parser->line_start;
|
|
}
|
|
|
|
void
|
|
_gtk_css_parser_take_error (GtkCssParser *parser,
|
|
GError *error)
|
|
{
|
|
parser->error_func (parser, error, parser->user_data);
|
|
|
|
g_error_free (error);
|
|
}
|
|
|
|
void
|
|
_gtk_css_parser_error (GtkCssParser *parser,
|
|
const char *format,
|
|
...)
|
|
{
|
|
GError *error;
|
|
|
|
va_list args;
|
|
|
|
va_start (args, format);
|
|
error = g_error_new_valist (GTK_CSS_PROVIDER_ERROR,
|
|
GTK_CSS_PROVIDER_ERROR_SYNTAX,
|
|
format, args);
|
|
va_end (args);
|
|
|
|
_gtk_css_parser_take_error (parser, error);
|
|
}
|
|
|
|
static gboolean
|
|
gtk_css_parser_new_line (GtkCssParser *parser)
|
|
{
|
|
gboolean result = FALSE;
|
|
|
|
if (*parser->data == '\r')
|
|
{
|
|
result = TRUE;
|
|
parser->data++;
|
|
}
|
|
if (*parser->data == '\n')
|
|
{
|
|
result = TRUE;
|
|
parser->data++;
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
parser->line++;
|
|
parser->line_start = parser->data;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static gboolean
|
|
gtk_css_parser_skip_comment (GtkCssParser *parser)
|
|
{
|
|
if (parser->data[0] != '/' ||
|
|
parser->data[1] != '*')
|
|
return FALSE;
|
|
|
|
parser->data += 2;
|
|
|
|
while (*parser->data)
|
|
{
|
|
gsize len = strcspn (parser->data, NEWLINE_CHARS "/");
|
|
|
|
parser->data += len;
|
|
|
|
if (gtk_css_parser_new_line (parser))
|
|
continue;
|
|
|
|
parser->data++;
|
|
|
|
if (parser->data[-2] == '*')
|
|
return TRUE;
|
|
if (parser->data[0] == '*')
|
|
_gtk_css_parser_error (parser, "'/*' in comment block");
|
|
}
|
|
|
|
/* FIXME: position */
|
|
_gtk_css_parser_error (parser, "Unterminated comment");
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
_gtk_css_parser_skip_whitespace (GtkCssParser *parser)
|
|
{
|
|
size_t len;
|
|
|
|
while (*parser->data)
|
|
{
|
|
if (gtk_css_parser_new_line (parser))
|
|
continue;
|
|
|
|
len = strspn (parser->data, WHITESPACE_CHARS);
|
|
if (len)
|
|
{
|
|
parser->data += len;
|
|
continue;
|
|
}
|
|
|
|
if (!gtk_css_parser_skip_comment (parser))
|
|
break;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
_gtk_css_parser_try (GtkCssParser *parser,
|
|
const char *string,
|
|
gboolean skip_whitespace)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
|
|
g_return_val_if_fail (string != NULL, FALSE);
|
|
|
|
if (g_ascii_strncasecmp (parser->data, string, strlen (string)) != 0)
|
|
return FALSE;
|
|
|
|
parser->data += strlen (string);
|
|
|
|
if (skip_whitespace)
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
return TRUE;
|
|
}
|
|
|
|
static guint
|
|
get_xdigit (char c)
|
|
{
|
|
if (c >= 'a')
|
|
return c - 'a' + 10;
|
|
else if (c >= 'A')
|
|
return c - 'A' + 10;
|
|
else
|
|
return c - '0';
|
|
}
|
|
|
|
static void
|
|
_gtk_css_parser_unescape (GtkCssParser *parser,
|
|
GString *str)
|
|
{
|
|
guint i;
|
|
gunichar result = 0;
|
|
|
|
g_assert (*parser->data == '\\');
|
|
|
|
parser->data++;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
if (!g_ascii_isxdigit (parser->data[i]))
|
|
break;
|
|
|
|
result = (result << 4) + get_xdigit (parser->data[i]);
|
|
}
|
|
|
|
if (i != 0)
|
|
{
|
|
g_string_append_unichar (str, result);
|
|
parser->data += i;
|
|
|
|
/* NB: gtk_css_parser_new_line() forward data pointer itself */
|
|
if (!gtk_css_parser_new_line (parser) &&
|
|
*parser->data &&
|
|
strchr (WHITESPACE_CHARS, *parser->data))
|
|
parser->data++;
|
|
return;
|
|
}
|
|
|
|
if (gtk_css_parser_new_line (parser))
|
|
return;
|
|
|
|
g_string_append_c (str, *parser->data);
|
|
parser->data++;
|
|
|
|
return;
|
|
}
|
|
|
|
static gboolean
|
|
_gtk_css_parser_read_char (GtkCssParser *parser,
|
|
GString * str,
|
|
const char * allowed)
|
|
{
|
|
if (*parser->data == 0)
|
|
return FALSE;
|
|
|
|
if (strchr (allowed, *parser->data))
|
|
{
|
|
g_string_append_c (str, *parser->data);
|
|
parser->data++;
|
|
return TRUE;
|
|
}
|
|
if (*parser->data >= 127)
|
|
{
|
|
gsize len = g_utf8_skip[(guint) *(guchar *) parser->data];
|
|
|
|
g_string_append_len (str, parser->data, len);
|
|
parser->data += len;
|
|
return TRUE;
|
|
}
|
|
if (*parser->data == '\\')
|
|
{
|
|
_gtk_css_parser_unescape (parser, str);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
char *
|
|
_gtk_css_parser_try_name (GtkCssParser *parser,
|
|
gboolean skip_whitespace)
|
|
{
|
|
GString *name;
|
|
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
|
|
|
|
name = g_string_new (NULL);
|
|
|
|
while (_gtk_css_parser_read_char (parser, name, NMCHAR))
|
|
;
|
|
|
|
if (skip_whitespace)
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
|
|
return g_string_free (name, FALSE);
|
|
}
|
|
|
|
char *
|
|
_gtk_css_parser_try_ident (GtkCssParser *parser,
|
|
gboolean skip_whitespace)
|
|
{
|
|
const char *start;
|
|
GString *ident;
|
|
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
|
|
|
|
start = parser->data;
|
|
|
|
ident = g_string_new (NULL);
|
|
|
|
if (*parser->data == '-')
|
|
{
|
|
g_string_append_c (ident, '-');
|
|
parser->data++;
|
|
}
|
|
|
|
if (!_gtk_css_parser_read_char (parser, ident, NMSTART))
|
|
{
|
|
parser->data = start;
|
|
g_string_free (ident, TRUE);
|
|
return NULL;
|
|
}
|
|
|
|
while (_gtk_css_parser_read_char (parser, ident, NMCHAR))
|
|
;
|
|
|
|
if (skip_whitespace)
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
|
|
return g_string_free (ident, FALSE);
|
|
}
|
|
|
|
gboolean
|
|
_gtk_css_parser_is_string (GtkCssParser *parser)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
|
|
|
|
return *parser->data == '"' || *parser->data == '\'';
|
|
}
|
|
|
|
char *
|
|
_gtk_css_parser_read_string (GtkCssParser *parser)
|
|
{
|
|
GString *str;
|
|
char quote;
|
|
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
|
|
|
|
quote = *parser->data;
|
|
|
|
if (quote != '"' && quote != '\'')
|
|
return NULL;
|
|
|
|
parser->data++;
|
|
str = g_string_new (NULL);
|
|
|
|
while (TRUE)
|
|
{
|
|
gsize len = strcspn (parser->data, "\\'\"\n\r\f");
|
|
|
|
g_string_append_len (str, parser->data, len);
|
|
|
|
parser->data += len;
|
|
|
|
switch (*parser->data)
|
|
{
|
|
case '\\':
|
|
_gtk_css_parser_unescape (parser, str);
|
|
break;
|
|
case '"':
|
|
case '\'':
|
|
if (*parser->data == quote)
|
|
{
|
|
parser->data++;
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
g_string_append_c (str, *parser->data);
|
|
parser->data++;
|
|
break;
|
|
case '\0':
|
|
/* FIXME: position */
|
|
_gtk_css_parser_error (parser, "Missing end quote in string.");
|
|
g_string_free (str, TRUE);
|
|
return NULL;
|
|
default:
|
|
_gtk_css_parser_error (parser,
|
|
"Invalid character in string. Must be escaped.");
|
|
g_string_free (str, TRUE);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
return NULL;
|
|
}
|
|
|
|
char *
|
|
_gtk_css_parser_read_uri (GtkCssParser *parser)
|
|
{
|
|
char *result;
|
|
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
|
|
|
|
if (!_gtk_css_parser_try (parser, "url(", TRUE))
|
|
{
|
|
_gtk_css_parser_error (parser, "expected 'url('");
|
|
return NULL;
|
|
}
|
|
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
|
|
if (_gtk_css_parser_is_string (parser))
|
|
{
|
|
result = _gtk_css_parser_read_string (parser);
|
|
}
|
|
else
|
|
{
|
|
GString *str = g_string_new (NULL);
|
|
|
|
while (_gtk_css_parser_read_char (parser, str, URLCHAR))
|
|
;
|
|
result = g_string_free (str, FALSE);
|
|
if (result == NULL)
|
|
_gtk_css_parser_error (parser, "not a url");
|
|
}
|
|
|
|
if (result == NULL)
|
|
return NULL;
|
|
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
|
|
if (*parser->data != ')')
|
|
{
|
|
_gtk_css_parser_error (parser, "missing ')' for url");
|
|
g_free (result);
|
|
return NULL;
|
|
}
|
|
|
|
parser->data++;
|
|
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
|
|
return result;
|
|
}
|
|
|
|
gboolean
|
|
_gtk_css_parser_try_int (GtkCssParser *parser,
|
|
int *value)
|
|
{
|
|
gint64 result;
|
|
char *end;
|
|
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
|
|
g_return_val_if_fail (value != NULL, FALSE);
|
|
|
|
/* strtoll parses a plus, but we are not allowed to */
|
|
if (*parser->data == '+')
|
|
return FALSE;
|
|
|
|
errno = 0;
|
|
result = g_ascii_strtoll (parser->data, &end, 10);
|
|
if (errno)
|
|
return FALSE;
|
|
if (result > G_MAXINT || result < G_MININT)
|
|
return FALSE;
|
|
if (parser->data == end)
|
|
return FALSE;
|
|
|
|
parser->data = end;
|
|
*value = result;
|
|
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
_gtk_css_parser_try_uint (GtkCssParser *parser,
|
|
guint *value)
|
|
{
|
|
guint64 result;
|
|
char *end;
|
|
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
|
|
g_return_val_if_fail (value != NULL, FALSE);
|
|
|
|
errno = 0;
|
|
result = g_ascii_strtoull (parser->data, &end, 10);
|
|
if (errno)
|
|
return FALSE;
|
|
if (result > G_MAXUINT)
|
|
return FALSE;
|
|
if (parser->data == end)
|
|
return FALSE;
|
|
|
|
parser->data = end;
|
|
*value = result;
|
|
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
_gtk_css_parser_try_double (GtkCssParser *parser,
|
|
gdouble *value)
|
|
{
|
|
gdouble result;
|
|
char *end;
|
|
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
|
|
g_return_val_if_fail (value != NULL, FALSE);
|
|
|
|
errno = 0;
|
|
result = g_ascii_strtod (parser->data, &end);
|
|
if (errno)
|
|
return FALSE;
|
|
if (parser->data == end)
|
|
return FALSE;
|
|
|
|
parser->data = end;
|
|
*value = result;
|
|
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
typedef enum {
|
|
COLOR_RGBA,
|
|
COLOR_RGB,
|
|
COLOR_LIGHTER,
|
|
COLOR_DARKER,
|
|
COLOR_SHADE,
|
|
COLOR_ALPHA,
|
|
COLOR_MIX
|
|
} ColorType;
|
|
|
|
static GtkSymbolicColor *
|
|
gtk_css_parser_read_symbolic_color_function (GtkCssParser *parser,
|
|
ColorType color)
|
|
{
|
|
GtkSymbolicColor *symbolic;
|
|
GtkSymbolicColor *child1, *child2;
|
|
double value;
|
|
|
|
if (!_gtk_css_parser_try (parser, "(", TRUE))
|
|
{
|
|
_gtk_css_parser_error (parser, "Missing opening bracket in color definition");
|
|
return NULL;
|
|
}
|
|
|
|
if (color == COLOR_RGB || color == COLOR_RGBA)
|
|
{
|
|
GdkRGBA rgba;
|
|
double tmp;
|
|
guint i;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
|
|
{
|
|
_gtk_css_parser_error (parser, "Expected ',' in color definition");
|
|
return NULL;
|
|
}
|
|
|
|
if (!_gtk_css_parser_try_double (parser, &tmp))
|
|
{
|
|
_gtk_css_parser_error (parser, "Invalid number for color value");
|
|
return NULL;
|
|
}
|
|
if (_gtk_css_parser_try (parser, "%", TRUE))
|
|
tmp /= 100.0;
|
|
else
|
|
tmp /= 255.0;
|
|
if (i == 0)
|
|
rgba.red = tmp;
|
|
else if (i == 1)
|
|
rgba.green = tmp;
|
|
else if (i == 2)
|
|
rgba.blue = tmp;
|
|
else
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
if (color == COLOR_RGBA)
|
|
{
|
|
if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
|
|
{
|
|
_gtk_css_parser_error (parser, "Expected ',' in color definition");
|
|
return NULL;
|
|
}
|
|
|
|
if (!_gtk_css_parser_try_double (parser, &rgba.alpha))
|
|
{
|
|
_gtk_css_parser_error (parser, "Invalid number for alpha value");
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
rgba.alpha = 1.0;
|
|
|
|
symbolic = gtk_symbolic_color_new_literal (&rgba);
|
|
}
|
|
else
|
|
{
|
|
child1 = _gtk_css_parser_read_symbolic_color (parser);
|
|
if (child1 == NULL)
|
|
return NULL;
|
|
|
|
if (color == COLOR_MIX)
|
|
{
|
|
if (!_gtk_css_parser_try (parser, ",", TRUE))
|
|
{
|
|
_gtk_css_parser_error (parser, "Expected ',' in color definition");
|
|
gtk_symbolic_color_unref (child1);
|
|
return NULL;
|
|
}
|
|
|
|
child2 = _gtk_css_parser_read_symbolic_color (parser);
|
|
if (child2 == NULL)
|
|
{
|
|
g_object_unref (child1);
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
child2 = NULL;
|
|
|
|
if (color == COLOR_LIGHTER)
|
|
value = 1.3;
|
|
else if (color == COLOR_DARKER)
|
|
value = 0.7;
|
|
else
|
|
{
|
|
if (!_gtk_css_parser_try (parser, ",", TRUE))
|
|
{
|
|
_gtk_css_parser_error (parser, "Expected ',' in color definition");
|
|
gtk_symbolic_color_unref (child1);
|
|
if (child2)
|
|
gtk_symbolic_color_unref (child2);
|
|
return NULL;
|
|
}
|
|
|
|
if (!_gtk_css_parser_try_double (parser, &value))
|
|
{
|
|
_gtk_css_parser_error (parser, "Expected number in color definition");
|
|
gtk_symbolic_color_unref (child1);
|
|
if (child2)
|
|
gtk_symbolic_color_unref (child2);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
switch (color)
|
|
{
|
|
case COLOR_LIGHTER:
|
|
case COLOR_DARKER:
|
|
case COLOR_SHADE:
|
|
symbolic = gtk_symbolic_color_new_shade (child1, value);
|
|
break;
|
|
case COLOR_ALPHA:
|
|
symbolic = gtk_symbolic_color_new_alpha (child1, value);
|
|
break;
|
|
case COLOR_MIX:
|
|
symbolic = gtk_symbolic_color_new_mix (child1, child2, value);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
symbolic = NULL;
|
|
}
|
|
|
|
gtk_symbolic_color_unref (child1);
|
|
if (child2)
|
|
gtk_symbolic_color_unref (child2);
|
|
}
|
|
|
|
if (!_gtk_css_parser_try (parser, ")", TRUE))
|
|
{
|
|
gtk_symbolic_color_unref (symbolic);
|
|
return NULL;
|
|
}
|
|
|
|
return symbolic;
|
|
}
|
|
|
|
static GtkSymbolicColor *
|
|
gtk_css_parser_try_hash_color (GtkCssParser *parser)
|
|
{
|
|
if (parser->data[0] == '#' &&
|
|
g_ascii_isxdigit (parser->data[1]) &&
|
|
g_ascii_isxdigit (parser->data[2]) &&
|
|
g_ascii_isxdigit (parser->data[3]))
|
|
{
|
|
GdkRGBA rgba;
|
|
|
|
if (g_ascii_isxdigit (parser->data[4]) &&
|
|
g_ascii_isxdigit (parser->data[5]) &&
|
|
g_ascii_isxdigit (parser->data[6]))
|
|
{
|
|
rgba.red = ((get_xdigit (parser->data[1]) << 4) + get_xdigit (parser->data[2])) / 255.0;
|
|
rgba.green = ((get_xdigit (parser->data[3]) << 4) + get_xdigit (parser->data[4])) / 255.0;
|
|
rgba.blue = ((get_xdigit (parser->data[5]) << 4) + get_xdigit (parser->data[6])) / 255.0;
|
|
rgba.alpha = 1.0;
|
|
parser->data += 7;
|
|
}
|
|
else
|
|
{
|
|
rgba.red = get_xdigit (parser->data[1]) / 15.0;
|
|
rgba.green = get_xdigit (parser->data[2]) / 15.0;
|
|
rgba.blue = get_xdigit (parser->data[3]) / 15.0;
|
|
rgba.alpha = 1.0;
|
|
parser->data += 4;
|
|
}
|
|
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
|
|
return gtk_symbolic_color_new_literal (&rgba);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GtkSymbolicColor *
|
|
_gtk_css_parser_read_symbolic_color (GtkCssParser *parser)
|
|
{
|
|
GtkSymbolicColor *symbolic;
|
|
guint color;
|
|
const char *names[] = {"rgba", "rgb", "lighter", "darker", "shade", "alpha", "mix" };
|
|
char *name;
|
|
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
|
|
|
|
if (_gtk_css_parser_try (parser, "@", FALSE))
|
|
{
|
|
name = _gtk_css_parser_try_name (parser, TRUE);
|
|
|
|
if (name)
|
|
{
|
|
symbolic = gtk_symbolic_color_new_name (name);
|
|
}
|
|
else
|
|
{
|
|
_gtk_css_parser_error (parser, "'%s' is not a valid symbolic color name", name);
|
|
symbolic = NULL;
|
|
}
|
|
|
|
g_free (name);
|
|
return symbolic;
|
|
}
|
|
|
|
for (color = 0; color < G_N_ELEMENTS (names); color++)
|
|
{
|
|
if (_gtk_css_parser_try (parser, names[color], TRUE))
|
|
break;
|
|
}
|
|
|
|
if (color < G_N_ELEMENTS (names))
|
|
return gtk_css_parser_read_symbolic_color_function (parser, color);
|
|
|
|
symbolic = gtk_css_parser_try_hash_color (parser);
|
|
if (symbolic)
|
|
return symbolic;
|
|
|
|
name = _gtk_css_parser_try_name (parser, TRUE);
|
|
if (name)
|
|
{
|
|
GdkRGBA rgba;
|
|
|
|
if (gdk_rgba_parse (&rgba, name))
|
|
{
|
|
symbolic = gtk_symbolic_color_new_literal (&rgba);
|
|
}
|
|
else
|
|
{
|
|
_gtk_css_parser_error (parser, "'%s' is not a valid color name", name);
|
|
symbolic = NULL;
|
|
}
|
|
g_free (name);
|
|
return symbolic;
|
|
}
|
|
|
|
_gtk_css_parser_error (parser, "Not a color definition");
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
_gtk_css_parser_resync_internal (GtkCssParser *parser,
|
|
gboolean sync_at_semicolon,
|
|
gboolean read_sync_token,
|
|
char terminator)
|
|
{
|
|
gsize len;
|
|
|
|
do {
|
|
len = strcspn (parser->data, "\\\"'/()[]{};" NEWLINE_CHARS);
|
|
parser->data += len;
|
|
|
|
if (gtk_css_parser_new_line (parser))
|
|
continue;
|
|
|
|
if (_gtk_css_parser_is_string (parser))
|
|
{
|
|
/* Hrm, this emits errors, and i suspect it shouldn't... */
|
|
char *free_me = _gtk_css_parser_read_string (parser);
|
|
g_free (free_me);
|
|
continue;
|
|
}
|
|
|
|
if (gtk_css_parser_skip_comment (parser))
|
|
continue;
|
|
|
|
switch (*parser->data)
|
|
{
|
|
case '\\':
|
|
{
|
|
GString *ignore = g_string_new (NULL);
|
|
_gtk_css_parser_unescape (parser, ignore);
|
|
g_string_free (ignore, TRUE);
|
|
}
|
|
break;
|
|
case ';':
|
|
if (sync_at_semicolon && !read_sync_token)
|
|
return;
|
|
parser->data++;
|
|
if (sync_at_semicolon)
|
|
{
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
return;
|
|
}
|
|
break;
|
|
case '(':
|
|
parser->data++;
|
|
_gtk_css_parser_resync (parser, FALSE, ')');
|
|
parser->data++;
|
|
break;
|
|
case '[':
|
|
parser->data++;
|
|
_gtk_css_parser_resync (parser, FALSE, ']');
|
|
parser->data++;
|
|
break;
|
|
case '{':
|
|
parser->data++;
|
|
_gtk_css_parser_resync (parser, FALSE, '}');
|
|
parser->data++;
|
|
if (sync_at_semicolon || !terminator)
|
|
{
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
return;
|
|
}
|
|
break;
|
|
case '}':
|
|
case ')':
|
|
case ']':
|
|
if (terminator == *parser->data)
|
|
{
|
|
_gtk_css_parser_skip_whitespace (parser);
|
|
return;
|
|
}
|
|
parser->data++;
|
|
continue;
|
|
case '/':
|
|
default:
|
|
parser->data++;
|
|
break;
|
|
}
|
|
} while (*parser->data);
|
|
}
|
|
|
|
char *
|
|
_gtk_css_parser_read_value (GtkCssParser *parser)
|
|
{
|
|
const char *start;
|
|
char *result;
|
|
|
|
g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
|
|
|
|
start = parser->data;
|
|
|
|
/* This needs to be done better */
|
|
_gtk_css_parser_resync_internal (parser, TRUE, FALSE, '}');
|
|
|
|
result = g_strndup (start, parser->data - start);
|
|
if (result)
|
|
{
|
|
g_strchomp (result);
|
|
if (result[0] == 0)
|
|
{
|
|
g_free (result);
|
|
result = NULL;
|
|
}
|
|
}
|
|
|
|
if (result == NULL)
|
|
_gtk_css_parser_error (parser, "Expected a property value");
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
_gtk_css_parser_resync (GtkCssParser *parser,
|
|
gboolean sync_at_semicolon,
|
|
char terminator)
|
|
{
|
|
g_return_if_fail (GTK_IS_CSS_PARSER (parser));
|
|
|
|
_gtk_css_parser_resync_internal (parser, sync_at_semicolon, TRUE, terminator);
|
|
}
|