diff --git a/gtk/gtkcssparser.c b/gtk/gtkcssparser.c index 44b00c305c..6c362533dd 100644 --- a/gtk/gtkcssparser.c +++ b/gtk/gtkcssparser.c @@ -529,6 +529,123 @@ _gtk_css_parser_try_double (GtkCssParser *parser, return TRUE; } +gboolean +_gtk_css_parser_has_number (GtkCssParser *parser) +{ + /* ahem */ + return strchr ("+-0123456789.", parser->data[0]) != NULL; +} + +gboolean +_gtk_css_parser_read_number (GtkCssParser *parser, + GtkCssNumber *number, + GtkCssNumberParseFlags flags) +{ + static const struct { + const char *name; + GtkCssUnit unit; + GtkCssNumberParseFlags required_flags; + } units[] = { + { "px", GTK_CSS_PX, GTK_CSS_PARSE_LENGTH }, + { "pt", GTK_CSS_PT, GTK_CSS_PARSE_LENGTH }, + { "em", GTK_CSS_EM, GTK_CSS_PARSE_LENGTH }, + { "ex", GTK_CSS_EX, GTK_CSS_PARSE_LENGTH }, + { "pc", GTK_CSS_PC, GTK_CSS_PARSE_LENGTH }, + { "in", GTK_CSS_IN, GTK_CSS_PARSE_LENGTH }, + { "cm", GTK_CSS_CM, GTK_CSS_PARSE_LENGTH }, + { "mm", GTK_CSS_MM, GTK_CSS_PARSE_LENGTH } + }; + char *end, *unit; + + g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE); + g_return_val_if_fail (number != NULL, FALSE); + + errno = 0; + number->unit = GTK_CSS_NUMBER; + number->value = g_ascii_strtod (parser->data, &end); + if (errno) + { + _gtk_css_parser_error (parser, "not a number: %s", g_strerror (errno)); + return FALSE; + } + if (parser->data == end) + { + _gtk_css_parser_error (parser, "not a number"); + return FALSE; + } + + parser->data = end; + + if (flags & GTK_CSS_POSITIVE_ONLY && + number->value < 0) + { + _gtk_css_parser_error (parser, "negative values are not allowed."); + return FALSE; + } + + unit = _gtk_css_parser_try_ident (parser, FALSE); + + if (unit) + { + guint i; + + for (i = 0; i < G_N_ELEMENTS (units); i++) + { + if (flags & units[i].required_flags && + g_ascii_strcasecmp (unit, units[i].name) == 0) + break; + } + + if (i >= G_N_ELEMENTS (units)) + { + _gtk_css_parser_error (parser, "`%s' is not a valid unit.", unit); + g_free (unit); + return FALSE; + } + + number->unit = units[i].unit; + g_free (unit); + } + else + { + if ((flags & GTK_CSS_PARSE_PERCENT) && + _gtk_css_parser_try (parser, "%", FALSE)) + { + number->unit = GTK_CSS_PERCENT; + } + else if (number->value == 0.0) + { + if (flags & GTK_CSS_PARSE_NUMBER) + number->unit = GTK_CSS_NUMBER; + else if (flags & GTK_CSS_PARSE_LENGTH) + number->unit = GTK_CSS_PX; + else + number->unit = GTK_CSS_PERCENT; + } + else if (flags & GTK_CSS_NUMBER_AS_PIXELS) + { + GError *error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_DEPRECATED, + "Not using units is deprecated. Assuming 'px'."); + _gtk_css_parser_take_error (parser, error); + number->unit = GTK_CSS_PX; + } + else if (flags & GTK_CSS_PARSE_NUMBER) + { + number->unit = GTK_CSS_NUMBER; + } + else + { + _gtk_css_parser_error (parser, "Unit is missing."); + return FALSE; + } + } + + _gtk_css_parser_skip_whitespace (parser); + + return TRUE; +} + /* XXX: we should introduce GtkCssLenght that deals with * different kind of units */ gboolean diff --git a/gtk/gtkcssparserprivate.h b/gtk/gtkcssparserprivate.h index d267530aa8..e5661d54ca 100644 --- a/gtk/gtkcssparserprivate.h +++ b/gtk/gtkcssparserprivate.h @@ -20,10 +20,19 @@ #ifndef __GTK_CSS_PARSER_PRIVATE_H__ #define __GTK_CSS_PARSER_PRIVATE_H__ +#include "gtk/gtkcsstypesprivate.h" #include G_BEGIN_DECLS +typedef enum /*< skip >*/ { + GTK_CSS_POSITIVE_ONLY = (1 << 0), + GTK_CSS_PARSE_PERCENT = (1 << 1), + GTK_CSS_PARSE_NUMBER = (1 << 2), + GTK_CSS_NUMBER_AS_PIXELS = (1 << 3), + GTK_CSS_PARSE_LENGTH = (1 << 4) +} GtkCssNumberParseFlags; + typedef struct _GtkCssParser GtkCssParser; typedef void (* GtkCssParserErrorFunc) (GtkCssParser *parser, @@ -78,7 +87,10 @@ gboolean _gtk_css_parser_try_enum (GtkCssParser *parser GType enum_type, int *value); -void _gtk_css_parser_skip_whitespace (GtkCssParser *parser); +gboolean _gtk_css_parser_has_number (GtkCssParser *parser); +gboolean _gtk_css_parser_read_number (GtkCssParser *parser, + GtkCssNumber *number, + GtkCssNumberParseFlags flags); char * _gtk_css_parser_read_string (GtkCssParser *parser); char * _gtk_css_parser_read_value (GtkCssParser *parser); GtkSymbolicColor *_gtk_css_parser_read_symbolic_color @@ -86,6 +98,7 @@ GtkSymbolicColor *_gtk_css_parser_read_symbolic_color GFile * _gtk_css_parser_read_url (GtkCssParser *parser, GFile *base); +void _gtk_css_parser_skip_whitespace (GtkCssParser *parser); void _gtk_css_parser_resync (GtkCssParser *parser, gboolean sync_at_semicolon, char terminator); diff --git a/gtk/gtkcssstylefuncs.c b/gtk/gtkcssstylefuncs.c index 0045f93996..02873ec708 100644 --- a/gtk/gtkcssstylefuncs.c +++ b/gtk/gtkcssstylefuncs.c @@ -1058,6 +1058,13 @@ border_image_repeat_value_print (const GValue *value, } } +static void +css_number_print (const GValue *value, + GString *string) +{ + _gtk_css_number_print (g_value_get_boxed (value), string); +} + static gboolean enum_value_parse (GtkCssParser *parser, GFile *base, @@ -1233,6 +1240,10 @@ gtk_css_style_funcs_init (void) shadow_value_parse, shadow_value_print, shadow_value_compute); + register_conversion_function (GTK_TYPE_CSS_NUMBER, + NULL, + css_number_print, + NULL); register_conversion_function (G_TYPE_ENUM, enum_value_parse, enum_value_print, diff --git a/gtk/gtkcsstypes.c b/gtk/gtkcsstypes.c index 674fd02d65..c2929d6266 100644 --- a/gtk/gtkcsstypes.c +++ b/gtk/gtkcsstypes.c @@ -20,6 +20,7 @@ #include "config.h" #include "gtkcsstypesprivate.h" +#include "gtkstylecontextprivate.h" #define DEFINE_BOXED_TYPE_WITH_COPY_FUNC(TypeName, type_name) \ \ @@ -33,3 +34,89 @@ G_DEFINE_BOXED_TYPE (TypeName, type_name, type_name ## _copy, g_free) DEFINE_BOXED_TYPE_WITH_COPY_FUNC (GtkCssBorderCornerRadius, _gtk_css_border_corner_radius) DEFINE_BOXED_TYPE_WITH_COPY_FUNC (GtkCssBorderImageRepeat, _gtk_css_border_image_repeat) +DEFINE_BOXED_TYPE_WITH_COPY_FUNC (GtkCssNumber, _gtk_css_number) + +void +_gtk_css_number_init (GtkCssNumber *number, + double value, + GtkCssUnit unit) +{ + number->value = value; + number->unit = unit; +} + +void +_gtk_css_number_compute (GtkCssNumber *dest, + const GtkCssNumber *src, + GtkStyleContext *context) +{ + switch (src->unit) + { + default: + g_assert_not_reached(); + /* fall through */ + case GTK_CSS_PERCENT: + case GTK_CSS_NUMBER: + case GTK_CSS_PX: + dest->value = src->value; + dest->unit = src->unit; + break; + case GTK_CSS_PT: + dest->value = src->value * 96.0 / 72.0; + dest->unit = GTK_CSS_PX; + break; + case GTK_CSS_PC: + dest->value = src->value * 96.0 / 72.0 * 12.0; + dest->unit = GTK_CSS_PX; + break; + case GTK_CSS_IN: + dest->value = src->value * 96.0; + dest->unit = GTK_CSS_PX; + break; + case GTK_CSS_CM: + dest->value = src->value * 96.0 * 0.39370078740157477; + dest->unit = GTK_CSS_PX; + break; + case GTK_CSS_MM: + dest->value = src->value * 96.0 * 0.039370078740157477; + dest->unit = GTK_CSS_PX; + break; + case GTK_CSS_EM: + dest->value = src->value * g_value_get_double (_gtk_style_context_peek_property (context, "font-size")); + dest->unit = GTK_CSS_PX; + break; + case GTK_CSS_EX: + /* for now we pretend ex is half of em */ + dest->value = src->value * g_value_get_double (_gtk_style_context_peek_property (context, "font-size")); + dest->unit = GTK_CSS_PX; + break; + } +} + +void +_gtk_css_number_print (const GtkCssNumber *number, + GString *string) +{ + char buf[G_ASCII_DTOSTR_BUF_SIZE]; + + g_return_if_fail (number != NULL); + g_return_if_fail (string != NULL); + + const char *names[] = { + /* [GTK_CSS_NUMBER] = */ "", + /* [GTK_CSS_PERCENT] = */ "%", + /* [GTK_CSS_PX] = */ "px", + /* [GTK_CSS_PT] = */ "pt", + /* [GTK_CSS_EM] = */ "em", + /* [GTK_CSS_EX] = */ "ex", + /* [GTK_CSS_PC] = */ "pc", + /* [GTK_CSS_IN] = */ "in", + /* [GTK_CSS_CM] = */ "cm", + /* [GTK_CSS_MM] = */ "mm" + }; + + g_ascii_dtostr (buf, sizeof (buf), number->value); + g_string_append (string, buf); + if (number->value != 0.0) + g_string_append (string, names[number->unit]); +} diff --git a/gtk/gtkcsstypesprivate.h b/gtk/gtkcsstypesprivate.h index 244f98db76..6422d9a02c 100644 --- a/gtk/gtkcsstypesprivate.h +++ b/gtk/gtkcsstypesprivate.h @@ -21,6 +21,7 @@ #define __GTK_CSS_TYPES_PRIVATE_H__ #include +#include G_BEGIN_DECLS @@ -77,9 +78,31 @@ typedef enum /*< skip >*/ { GTK_CSS_BOTTOM_LEFT } GtkCssCorner; +typedef enum /*< skip >*/ { + /* CSS term: */ + GTK_CSS_NUMBER, + /* CSS term: */ + GTK_CSS_PERCENT, + /* CSS term: */ + GTK_CSS_PX, + GTK_CSS_PT, + GTK_CSS_EM, + GTK_CSS_EX, + GTK_CSS_PC, + GTK_CSS_IN, + GTK_CSS_CM, + GTK_CSS_MM +} GtkCssUnit; + +typedef struct _GtkCssNumber GtkCssNumber; typedef struct _GtkCssBorderCornerRadius GtkCssBorderCornerRadius; typedef struct _GtkCssBorderImageRepeat GtkCssBorderImageRepeat; +struct _GtkCssNumber { + gdouble value; + GtkCssUnit unit; +}; + struct _GtkCssBorderCornerRadius { double horizontal; double vertical; @@ -92,9 +115,22 @@ struct _GtkCssBorderImageRepeat { #define GTK_TYPE_CSS_BORDER_CORNER_RADIUS _gtk_css_border_corner_radius_get_type () #define GTK_TYPE_CSS_BORDER_IMAGE_REPEAT _gtk_css_border_image_repeat_get_type () +#define GTK_TYPE_CSS_NUMBER _gtk_css_number_get_type () GType _gtk_css_border_corner_radius_get_type (void); GType _gtk_css_border_image_repeat_get_type (void); +GType _gtk_css_number_get_type (void); + +#define GTK_CSS_NUMBER_INIT(_value,_unit) { (_value), (_unit) } +void _gtk_css_number_init (GtkCssNumber *number, + double value, + GtkCssUnit unit); +void _gtk_css_number_compute (GtkCssNumber *dest, + const GtkCssNumber *src, + GtkStyleContext *context); +void _gtk_css_number_print (const GtkCssNumber *number, + GString *string); + G_END_DECLS