static gboolean parse_border_radius (GtkCssShorthandProperty *shorthand, GtkCssValue **values, GtkCssParser *parser) { GtkCssValue *x[4] = { NULL, }, *y[4] = { NULL, }; guint i; for (i = 0; i < 4; i++) { if (!gtk_css_number_value_can_parse (parser)) break; x[i] = _gtk_css_number_value_parse (parser, GTK_CSS_POSITIVE_ONLY | GTK_CSS_PARSE_PERCENT | GTK_CSS_NUMBER_AS_PIXELS | GTK_CSS_PARSE_LENGTH); if (x[i] == NULL) goto fail; } if (i == 0) { _gtk_css_parser_error (parser, "Expected a number"); goto fail; } /* The magic (i - 1) >> 1 below makes it take the correct value * according to spec. Feel free to check the 4 cases */ for (; i < 4; i++) x[i] = _gtk_css_value_ref (x[(i - 1) >> 1]); if (_gtk_css_parser_try (parser, "/", TRUE)) { for (i = 0; i < 4; i++) { if (!gtk_css_number_value_can_parse (parser)) break; y[i] = _gtk_css_number_value_parse (parser, GTK_CSS_POSITIVE_ONLY | GTK_CSS_PARSE_PERCENT | GTK_CSS_NUMBER_AS_PIXELS | GTK_CSS_PARSE_LENGTH); if (y[i] == NULL) goto fail; } if (i == 0) { _gtk_css_parser_error (parser, "Expected a number"); goto fail; } for (; i < 4; i++) y[i] = _gtk_css_value_ref (y[(i - 1) >> 1]); } else { for (i = 0; i < 4; i++)
static GtkCssValue * gtk_css_win32_size_value_parse_size (GtkCssValue *value, GtkCssParser *parser) { char *name; name = _gtk_css_parser_try_ident (parser, TRUE); if (name) { value->val.size.id = gtk_win32_get_sys_metric_id_for_name (name); if (value->val.size.id == -1) { _gtk_css_parser_error (parser, "'%s' is not a name for a win32 metric.", name); _gtk_css_value_unref (value); g_free (name); return NULL; } g_free (name); } else if (!_gtk_css_parser_try_int (parser, &value->val.size.id)) { _gtk_css_value_unref (value); _gtk_css_parser_error (parser, "Expected an integer ID"); return NULL; } return value; }
static GtkCssValue * gtk_css_win32_size_value_parse_part_size (GtkCssValue *value, GtkCssParser *parser) { if (!_gtk_css_parser_try_int (parser, &value->val.part.part)) { _gtk_css_value_unref (value); _gtk_css_parser_error (parser, "Expected an integer part ID"); return NULL; } if (! _gtk_css_parser_try (parser, ",", TRUE)) { _gtk_css_value_unref (value); _gtk_css_parser_error (parser, "Expected ','"); return NULL; } if (!_gtk_css_parser_try_int (parser, &value->val.part.state)) { _gtk_css_value_unref (value); _gtk_css_parser_error (parser, "Expected an integer state ID"); return NULL; } return value; }
static GtkCssValue * gtk_css_ease_value_parse_cubic_bezier (GtkCssParser *parser) { double values[4]; guint i; for (i = 0; i < 4; i++) { if (!_gtk_css_parser_try (parser, i ? "," : "(", TRUE)) { _gtk_css_parser_error (parser, "Expected '%s'", i ? "," : "("); return NULL; } if (!_gtk_css_parser_try_double (parser, &values[i])) { _gtk_css_parser_error (parser, "Expected a number"); return NULL; } if ((i == 0 || i == 2) && (values[i] < 0 || values[i] > 1.0)) { _gtk_css_parser_error (parser, "value %g out of range. Must be from 0.0 to 1.0", values[i]); return NULL; } } if (!_gtk_css_parser_try (parser, ")", TRUE)) { _gtk_css_parser_error (parser, "Missing closing ')' for cubic-bezier"); return NULL; } return _gtk_css_ease_value_new_cubic_bezier (values[0], values[1], values[2], values[3]); }
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 != '\'') { _gtk_css_parser_error (parser, "Expected a string."); 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; }
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 (len > 0 && 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; }
static gboolean enum_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GEnumClass *enum_class; GEnumValue *enum_value; char *str; str = _gtk_css_parser_try_ident (parser, TRUE); if (str == NULL) { _gtk_css_parser_error (parser, "Expected an identifier"); return FALSE; } 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_value_set_enum (value, enum_value->value); else _gtk_css_parser_error (parser, "Unknown value '%s' for enum type '%s'", str, g_type_name (G_VALUE_TYPE (value))); g_type_class_unref (enum_class); g_free (str); return enum_value != NULL; }
GFile * _gtk_css_parser_read_url (GtkCssParser *parser) { gchar *path; char *scheme; GFile *file; if (_gtk_css_parser_try (parser, "url", FALSE)) { if (!_gtk_css_parser_try (parser, "(", TRUE)) { _gtk_css_parser_skip_whitespace (parser); if (_gtk_css_parser_try (parser, "(", TRUE)) { _gtk_css_parser_error_full (parser, GTK_CSS_PROVIDER_ERROR_DEPRECATED, "Whitespace between 'url' and '(' is deprecated"); } else { _gtk_css_parser_error (parser, "Expected '(' after 'url'"); return NULL; } } path = _gtk_css_parser_read_string (parser); if (path == NULL) return NULL; if (!_gtk_css_parser_try (parser, ")", TRUE)) { _gtk_css_parser_error (parser, "No closing ')' found for 'url'"); g_free (path); return NULL; } scheme = g_uri_parse_scheme (path); if (scheme != NULL) { file = g_file_new_for_uri (path); g_free (path); g_free (scheme); return file; } } else { path = _gtk_css_parser_try_name (parser, TRUE); if (path == NULL) { _gtk_css_parser_error (parser, "Not a valid url"); return NULL; } } file = _gtk_css_parser_get_file_for_path (parser, path); g_free (path); return file; }
static gboolean theming_engine_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GtkThemingEngine *engine; char *str; str = _gtk_css_parser_try_ident (parser, TRUE); if (str == NULL) { _gtk_css_parser_error (parser, "Expected a valid theme name"); return FALSE; } engine = gtk_theming_engine_load (str); if (engine == NULL) { _gtk_css_parser_error (parser, "Themeing engine '%s' not found", str); g_free (str); return FALSE; } g_value_set_object (value, engine); g_free (str); return TRUE; }
GFile * _gtk_css_parse_url (GtkCssParser *parser, GFile *base) { gchar *path; GFile *file; if (_gtk_css_parser_try (parser, "url", FALSE)) { if (!_gtk_css_parser_try (parser, "(", TRUE)) { _gtk_css_parser_skip_whitespace (parser); if (_gtk_css_parser_try (parser, "(", TRUE)) { GError *error; error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_DEPRECATED, "Whitespace between 'url' and '(' is not allowed"); _gtk_css_parser_take_error (parser, error); } else { _gtk_css_parser_error (parser, "Expected '(' after 'url'"); return NULL; } } path = _gtk_css_parser_read_string (parser); if (path == NULL) return NULL; if (!_gtk_css_parser_try (parser, ")", TRUE)) { _gtk_css_parser_error (parser, "No closing ')' found for 'url'"); g_free (path); return NULL; } } else { path = _gtk_css_parser_try_name (parser, TRUE); if (path == NULL) { _gtk_css_parser_error (parser, "Not a valid url"); return NULL; } } file = g_file_resolve_relative_path (base, path); g_free (path); return file; }
static gboolean gtk_css_image_cross_fade_parse (GtkCssImage *image, GtkCssParser *parser) { GtkCssImageCrossFade *cross_fade = GTK_CSS_IMAGE_CROSS_FADE (image); if (!_gtk_css_parser_try (parser, "cross-fade(", TRUE)) { _gtk_css_parser_error (parser, "Expected 'cross-fade('"); return FALSE; } if (_gtk_css_parser_has_number (parser)) { GtkCssValue *number; number = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_PERCENT | GTK_CSS_POSITIVE_ONLY); if (number == NULL) return FALSE; cross_fade->progress = _gtk_css_number_value_get (number, 1); _gtk_css_value_unref (number); if (cross_fade->progress > 1.0) { _gtk_css_parser_error (parser, "Percentages over 100%% are not allowed"); return FALSE; } } else cross_fade->progress = 0.5; cross_fade->start = _gtk_css_image_new_parse (parser); if (cross_fade->start == NULL) return FALSE; if (_gtk_css_parser_try (parser, ",", TRUE)) { /* XXX: allow parsing colors here */ cross_fade->end = _gtk_css_image_new_parse (parser); if (cross_fade->end == NULL) return FALSE; } if (!_gtk_css_parser_try (parser, ")", TRUE)) { _gtk_css_parser_error (parser, "Missing closing bracket"); return FALSE; } return TRUE; }
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; }
GtkCssValue * _gtk_css_ease_value_parse (GtkCssParser *parser) { guint i; g_return_val_if_fail (parser != NULL, NULL); for (i = 0; i < G_N_ELEMENTS (parser_values); i++) { if (_gtk_css_parser_try (parser, parser_values[i].name, FALSE)) { if (parser_values[i].needs_custom) { if (parser_values[i].is_bezier) return gtk_css_ease_value_parse_cubic_bezier (parser); else return gtk_css_ease_value_parse_steps (parser); } _gtk_css_parser_skip_whitespace (parser); if (parser_values[i].is_bezier) return _gtk_css_ease_value_new_cubic_bezier (parser_values[i].values[0], parser_values[i].values[1], parser_values[i].values[2], parser_values[i].values[3]); else return _gtk_css_ease_value_new_steps (parser_values[i].values[0], parser_values[i].values[1] != 0.0); } } _gtk_css_parser_error (parser, "Unknown value"); return NULL; }
static gboolean parse_four_numbers (GtkCssShorthandProperty *shorthand, GtkCssValue **values, GtkCssParser *parser, GtkCssNumberParseFlags flags) { guint i; for (i = 0; i < 4; i++) { if (!gtk_css_number_value_can_parse (parser)) break; values[i] = _gtk_css_number_value_parse (parser, flags); if (values[i] == NULL) return FALSE; } if (i == 0) { _gtk_css_parser_error (parser, "Expected a length"); return FALSE; } for (; i < 4; i++) { values[i] = _gtk_css_value_ref (values[(i - 1) >> 1]); } return TRUE; }
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; }
/** * _gtk_css_style_parse_value: * @value: the value to parse into. Must be a valid initialized #GValue * @parser: the parser to parse from * * This is the generic parsing function used for CSS values. If the * function fails to parse a value, it will emit an error on @parser, * return %FALSE and not touch @value. * * Returns: %TRUE if parsing succeeded. **/ gboolean _gtk_css_style_funcs_parse_value (GValue *value, GtkCssParser *parser) { GtkStyleParseFunc func; g_return_val_if_fail (value != NULL, FALSE); g_return_val_if_fail (parser != NULL, FALSE); gtk_css_style_funcs_init (); func = g_hash_table_lookup (parse_funcs, GSIZE_TO_POINTER (G_VALUE_TYPE (value))); if (func == NULL) func = g_hash_table_lookup (parse_funcs, GSIZE_TO_POINTER (g_type_fundamental (G_VALUE_TYPE (value)))); if (func == NULL) { _gtk_css_parser_error (parser, "Cannot convert to type '%s'", g_type_name (G_VALUE_TYPE (value))); return FALSE; } return (*func) (parser, value); }
static gboolean int_value_parse (GtkCssParser *parser, GValue *value) { gint i; if (_gtk_css_parser_has_prefix (parser, "-gtk")) { GtkCssValue *cssvalue = gtk_css_win32_size_value_parse (parser, GTK_CSS_PARSE_NUMBER | GTK_CSS_NUMBER_AS_PIXELS); if (cssvalue) { g_value_set_int (value, _gtk_css_number_value_get (cssvalue, 100)); _gtk_css_value_unref (cssvalue); return TRUE; } return FALSE; } if (!_gtk_css_parser_try_int (parser, &i)) { _gtk_css_parser_error (parser, "Expected a valid integer value"); return FALSE; } g_value_set_int (value, i); return TRUE; }
GtkCssImage * _gtk_css_image_new_parse (GtkCssParser *parser) { GtkCssImageClass *klass; GtkCssImage *image; GType image_type; g_return_val_if_fail (parser != NULL, NULL); image_type = gtk_css_image_get_parser_type (parser); if (image_type == G_TYPE_INVALID) { _gtk_css_parser_error (parser, "Not a valid image"); return NULL; } image = g_object_new (image_type, NULL); klass = GTK_CSS_IMAGE_GET_CLASS (image); if (!klass->parse (image, parser)) { g_object_unref (image); return NULL; } return image; }
static gboolean flags_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GFlagsClass *flags_class; GFlagsValue *flag_value; guint flags = 0; char *str; flags_class = g_type_class_ref (G_VALUE_TYPE (value)); do { str = _gtk_css_parser_try_ident (parser, TRUE); if (str == NULL) { _gtk_css_parser_error (parser, "Expected an identifier"); g_type_class_unref (flags_class); return FALSE; } flag_value = g_flags_get_value_by_nick (flags_class, str); if (!flag_value) { _gtk_css_parser_error (parser, "Unknown flag value '%s' for type '%s'", str, g_type_name (G_VALUE_TYPE (value))); /* XXX Do we want to return FALSE here? We can get * forward-compatibility for new values this way */ g_free (str); g_type_class_unref (flags_class); return FALSE; } g_free (str); } while (_gtk_css_parser_try (parser, ",", FALSE)); g_type_class_unref (flags_class); g_value_set_enum (value, flags); return TRUE; }
static GtkCssValue * gtk_css_ease_value_parse_steps (GtkCssParser *parser) { guint n_steps; gboolean start; if (!_gtk_css_parser_try (parser, "(", TRUE)) { _gtk_css_parser_error (parser, "Expected '('"); return NULL; } if (!_gtk_css_parser_try_uint (parser, &n_steps)) { _gtk_css_parser_error (parser, "Expected number of steps"); return NULL; } if (_gtk_css_parser_try (parser, ",", TRUE)) { if (_gtk_css_parser_try (parser, "start", TRUE)) start = TRUE; else if (_gtk_css_parser_try (parser, "end", TRUE)) start = FALSE; else { _gtk_css_parser_error (parser, "Only allowed values are 'start' and 'end'"); return NULL; } } else start = FALSE; if (!_gtk_css_parser_try (parser, ")", TRUE)) { _gtk_css_parser_error (parser, "Missing closing ')' for steps"); return NULL; } return _gtk_css_ease_value_new_steps (n_steps, start); }
static gboolean border_value_parse (GtkCssParser *parser, GValue *value) { GtkBorder border = { 0, }; guint i; int numbers[4]; for (i = 0; i < G_N_ELEMENTS (numbers); i++) { if (_gtk_css_parser_has_prefix (parser, "-gtk")) { GtkCssValue *cssvalue = gtk_css_win32_size_value_parse (parser, GTK_CSS_PARSE_NUMBER | GTK_CSS_NUMBER_AS_PIXELS); if (cssvalue) { numbers[i] = _gtk_css_number_value_get (cssvalue, 100); _gtk_css_value_unref (cssvalue); return TRUE; } return FALSE; } else { if (!_gtk_css_parser_try_length (parser, &numbers[i])) break; } } if (i == 0) { _gtk_css_parser_error (parser, "Expected valid border"); return FALSE; } border.top = numbers[0]; if (i > 1) border.right = numbers[1]; else border.right = border.top; if (i > 2) border.bottom = numbers[2]; else border.bottom = border.top; if (i > 3) border.left = numbers[3]; else border.left = border.right; g_value_set_boxed (value, &border); return TRUE; }
static gboolean gtk_css_image_builtin_parse (GtkCssImage *image, GtkCssParser *parser) { if (!_gtk_css_parser_try (parser, "builtin", TRUE)) { _gtk_css_parser_error (parser, "Expected 'builtin'"); return FALSE; } return TRUE; }
static GtkCssValue * gtk_css_win32_size_value_parse_size (GtkCssValue *value, GtkCssParser *parser) { if (!_gtk_css_parser_try_int (parser, &value->val.size.id)) { _gtk_css_value_unref (value); _gtk_css_parser_error (parser, "Expected an integer ID"); return NULL; } return value; }
static gboolean bindings_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GPtrArray *array; GtkBindingSet *binding_set; char *name; array = g_ptr_array_new (); do { name = _gtk_css_parser_try_ident (parser, TRUE); if (name == NULL) { _gtk_css_parser_error (parser, "Not a valid binding name"); g_ptr_array_free (array, TRUE); return FALSE; } binding_set = gtk_binding_set_find (name); if (!binding_set) { _gtk_css_parser_error (parser, "No binding set named '%s'", name); g_free (name); continue; } g_ptr_array_add (array, binding_set); g_free (name); } while (_gtk_css_parser_try (parser, ",", TRUE)); g_value_take_boxed (value, array); return TRUE; }
static gboolean gtk_css_image_icon_theme_parse (GtkCssImage *image, GtkCssParser *parser) { GtkCssImageIconTheme *icon_theme = GTK_CSS_IMAGE_ICON_THEME (image); if (!_gtk_css_parser_try (parser, "-gtk-icontheme(", TRUE)) { _gtk_css_parser_error (parser, "Expected '-gtk-icontheme('"); return FALSE; } icon_theme->name = _gtk_css_parser_read_string (parser); if (icon_theme->name == NULL) return FALSE; if (!_gtk_css_parser_try (parser, ")", TRUE)) { _gtk_css_parser_error (parser, "Missing closing bracket at end of '-gtk-icontheme'"); return FALSE; } return TRUE; }
static gboolean enum_parse (GtkCssParser *parser, GType type, int *res) { char *str; if (_gtk_css_parser_try_enum (parser, type, res)) return TRUE; str = _gtk_css_parser_try_ident (parser, TRUE); if (str == NULL) { _gtk_css_parser_error (parser, "Expected an identifier"); return FALSE; } _gtk_css_parser_error (parser, "Unknown value '%s' for enum type '%s'", str, g_type_name (type)); g_free (str); return FALSE; }
static gboolean float_value_parse (GtkCssParser *parser, GValue *value) { gdouble d; if (!_gtk_css_parser_try_double (parser, &d)) { _gtk_css_parser_error (parser, "Expected a number"); return FALSE; } g_value_set_float (value, d); return TRUE; }
static gboolean uint_value_parse (GtkCssParser *parser, GValue *value) { guint u; if (!_gtk_css_parser_try_uint (parser, &u)) { _gtk_css_parser_error (parser, "Expected a valid unsigned value"); return FALSE; } g_value_set_uint (value, u); return TRUE; }
static gboolean int_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { gint i; if (!_gtk_css_parser_try_int (parser, &i)) { _gtk_css_parser_error (parser, "Expected a valid integer value"); return FALSE; } g_value_set_int (value, i); return TRUE; }
static gboolean border_value_parse (GtkCssParser *parser, GFile *base, GValue *value) { GtkBorder border = { 0, }; guint i, numbers[4]; for (i = 0; i < G_N_ELEMENTS (numbers); i++) { if (!_gtk_css_parser_try_uint (parser, &numbers[i])) break; /* XXX: shouldn't allow spaces here? */ _gtk_css_parser_try (parser, "px", TRUE); } if (i == 0) { _gtk_css_parser_error (parser, "Expected valid border"); return FALSE; } border.top = numbers[0]; if (i > 1) border.right = numbers[1]; else border.right = border.top; if (i > 2) border.bottom = numbers[2]; else border.bottom = border.top; if (i > 3) border.left = numbers[3]; else border.left = border.right; g_value_set_boxed (value, &border); return TRUE; }