X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkcssprovider.c;h=751266847464ff2e9d495af992c5b203d60c7e8a;hb=bbf915118bd3ff3a871a8c8014514352037f6d10;hp=250bcb46fa0743c22003044630b164ea0c1dcc72;hpb=8adf311acd96c9019daa512dd5577ee8704ecf63;p=~andy%2Fgtk diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c index 250bcb46f..751266847 100644 --- a/gtk/gtkcssprovider.c +++ b/gtk/gtkcssprovider.c @@ -26,12 +26,14 @@ #include "gtkcssproviderprivate.h" #include "gtkbitmaskprivate.h" -#include "gtkcssstylefuncsprivate.h" +#include "gtkcssarrayvalueprivate.h" +#include "gtkcsscolorvalueprivate.h" +#include "gtkcsskeyframesprivate.h" #include "gtkcssparserprivate.h" #include "gtkcsssectionprivate.h" #include "gtkcssselectorprivate.h" #include "gtkcssshorthandpropertyprivate.h" -#include "gtksymboliccolor.h" +#include "gtkcssstylefuncsprivate.h" #include "gtkstyleprovider.h" #include "gtkstylecontextprivate.h" #include "gtkstylepropertiesprivate.h" @@ -915,7 +917,7 @@ * * text-shadow * shadow list (see above) - * #GtkTextShadow + * internal use only * text-shadow: 1 1 0 blue, -4 -4 red; * * @@ -969,14 +971,14 @@ typedef enum ParserSymbol ParserSymbol; struct _PropertyValue { GtkCssStyleProperty *property; - GValue value; + GtkCssValue *value; GtkCssSection *section; }; struct _WidgetPropertyValue { - char *name; WidgetPropertyValue *next; - GValue value; + char *name; + char *value; GtkCssSection *section; }; @@ -984,6 +986,7 @@ struct _WidgetPropertyValue { struct GtkCssRuleset { GtkCssSelector *selector; + GtkCssSelectorTree *selector_match; WidgetPropertyValue *widget_style; PropertyValue *styles; GtkBitmask *set_styles; @@ -998,8 +1001,6 @@ struct _GtkCssScanner GtkCssParser *parser; GtkCssSection *section; GtkCssScanner *parent; - GFile *file; - GFile *base; GSList *state; }; @@ -1008,8 +1009,10 @@ struct _GtkCssProviderPrivate GScanner *scanner; GHashTable *symbolic_colors; + GHashTable *keyframes; GArray *rulesets; + GtkCssSelectorTree *tree; GResource *resource; }; @@ -1018,6 +1021,8 @@ enum { LAST_SIGNAL }; +static gboolean gtk_keep_css_sections = FALSE; + static guint css_provider_signals[LAST_SIGNAL] = { 0 }; static void gtk_css_provider_finalize (GObject *object); @@ -1060,34 +1065,13 @@ gtk_css_provider_parsing_error (GtkCssProvider *provider, 0, TRUE)) { - GFileInfo *info; - GFile *file; - const char *path; - - file = gtk_css_section_get_file (section); - if (file) - { - info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, 0, NULL, NULL); + char *s = _gtk_css_section_to_string (section); - if (info) - path = g_file_info_get_display_name (info); - else - path = ""; - } - else - { - info = NULL; - path = ""; - } - - g_warning ("Theme parsing error: %s:%u:%u: %s", - path, - gtk_css_section_get_end_line (section) + 1, - gtk_css_section_get_end_position (section), + g_warning ("Theme parsing error: %s: %s", + s, error->message); - if (info) - g_object_unref (info); + g_free (s); } } @@ -1096,6 +1080,9 @@ gtk_css_provider_class_init (GtkCssProviderClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + if (g_getenv ("GTK_CSS_DEBUG")) + gtk_keep_css_sections = TRUE; + /** * GtkCssProvider::parsing-error: * @provider: the provider that had a parsing error @@ -1156,8 +1143,10 @@ gtk_css_ruleset_clear (GtkCssRuleset *ruleset) for (i = 0; i < ruleset->n_styles; i++) { - g_value_unset (&ruleset->styles[i].value); - gtk_css_section_unref (ruleset->styles[i].section); + _gtk_css_value_unref (ruleset->styles[i].value); + ruleset->styles[i].value = NULL; + if (ruleset->styles[i].section) + gtk_css_section_unref (ruleset->styles[i].section); } g_free (ruleset->styles); } @@ -1179,7 +1168,8 @@ widget_property_value_new (char *name, GtkCssSection *section) value = g_slice_new0 (WidgetPropertyValue); value->name = name; - value->section = gtk_css_section_ref (section); + if (gtk_keep_css_sections) + value->section = gtk_css_section_ref (section); return value; } @@ -1187,11 +1177,10 @@ widget_property_value_new (char *name, GtkCssSection *section) static void widget_property_value_free (WidgetPropertyValue *value) { - if (G_IS_VALUE (&value->value)) - g_value_unset (&value->value); - + g_free (value->value); g_free (value->name); - gtk_css_section_unref (value->section); + if (value->section) + gtk_css_section_unref (value->section); g_slice_free (WidgetPropertyValue, value); } @@ -1242,12 +1231,11 @@ gtk_css_ruleset_add_style (GtkCssRuleset *ruleset, static void gtk_css_ruleset_add (GtkCssRuleset *ruleset, GtkCssStyleProperty *property, - const GValue *value, + GtkCssValue *value, GtkCssSection *section) { guint i; - g_return_if_fail (_gtk_css_style_property_is_specified_type (property, G_VALUE_TYPE (value))); g_return_if_fail (ruleset->owns_styles || ruleset->n_styles == 0); if (ruleset->set_styles == NULL) @@ -1263,8 +1251,10 @@ gtk_css_ruleset_add (GtkCssRuleset *ruleset, { if (ruleset->styles[i].property == property) { - g_value_unset (&ruleset->styles[i].value); - gtk_css_section_unref (ruleset->styles[i].section); + _gtk_css_value_unref (ruleset->styles[i].value); + ruleset->styles[i].value = NULL; + if (ruleset->styles[i].section) + gtk_css_section_unref (ruleset->styles[i].section); break; } } @@ -1272,21 +1262,15 @@ gtk_css_ruleset_add (GtkCssRuleset *ruleset, { ruleset->n_styles++; ruleset->styles = g_realloc (ruleset->styles, ruleset->n_styles * sizeof (PropertyValue)); - memset (&ruleset->styles[i].value, 0, sizeof (GValue)); + ruleset->styles[i].value = NULL; ruleset->styles[i].property = property; } - g_value_init (&ruleset->styles[i].value, G_VALUE_TYPE (value)); - g_value_copy (value, &ruleset->styles[i].value); - ruleset->styles[i].section = gtk_css_section_ref (section); -} - -static gboolean -gtk_css_ruleset_matches (GtkCssRuleset *ruleset, - GtkWidgetPath *path, - GtkStateFlags state) -{ - return _gtk_css_selector_matches (ruleset->selector, path, state); + ruleset->styles[i].value = value; + if (gtk_keep_css_sections) + ruleset->styles[i].section = gtk_css_section_ref (section); + else + ruleset->styles[i].section = NULL; } static void @@ -1295,9 +1279,6 @@ gtk_css_scanner_destroy (GtkCssScanner *scanner) if (scanner->section) gtk_css_section_unref (scanner->section); g_object_unref (scanner->provider); - if (scanner->file) - g_object_unref (scanner->file); - g_object_unref (scanner->base); _gtk_css_parser_free (scanner->parser); g_slice_free (GtkCssScanner, scanner); @@ -1341,38 +1322,22 @@ gtk_css_scanner_new (GtkCssProvider *provider, if (section) scanner->section = gtk_css_section_ref (section); - if (file) - { - scanner->file = g_object_ref (file); - scanner->base = g_file_get_parent (file); - } - else - { - char *dir = g_get_current_dir (); - scanner->base = g_file_new_for_path (dir); - g_free (dir); - } - scanner->parser = _gtk_css_parser_new (text, + file, gtk_css_scanner_parser_error, scanner); return scanner; } -static GFile * -gtk_css_scanner_get_base_url (GtkCssScanner *scanner) -{ - return scanner->base; -} - static gboolean gtk_css_scanner_would_recurse (GtkCssScanner *scanner, GFile *file) { while (scanner) { - if (scanner->file && g_file_equal (scanner->file, file)) + GFile *parser_file = _gtk_css_parser_get_file (scanner->parser); + if (parser_file && g_file_equal (parser_file, file)) return TRUE; scanner = scanner->parent; @@ -1389,8 +1354,7 @@ gtk_css_scanner_push_section (GtkCssScanner *scanner, section = _gtk_css_section_new (scanner->section, section_type, - scanner->parser, - scanner->file); + scanner->parser); if (scanner->section) gtk_css_section_unref (scanner->section); @@ -1428,69 +1392,92 @@ gtk_css_provider_init (GtkCssProvider *css_provider) priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, - (GDestroyNotify) gtk_symbolic_color_unref); + (GDestroyNotify) _gtk_css_value_unref); + priv->keyframes = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) _gtk_css_value_unref); } static void -css_provider_dump_symbolic_colors (GtkCssProvider *css_provider, - GtkStyleProperties *props) +verify_tree_match_results (GtkCssProvider *provider, + const GtkCssMatcher *matcher, + GPtrArray *tree_rules) { - GtkCssProviderPrivate *priv; - GHashTableIter iter; - gpointer key, value; - - priv = css_provider->priv; - g_hash_table_iter_init (&iter, priv->symbolic_colors); - - while (g_hash_table_iter_next (&iter, &key, &value)) - { - const gchar *name; - GtkSymbolicColor *color; - - name = key; - color = value; - - gtk_style_properties_map_color (props, name, color); - } -} - -static GtkStyleProperties * -gtk_css_provider_get_style (GtkStyleProvider *provider, - GtkWidgetPath *path) -{ - GtkCssProvider *css_provider; - GtkCssProviderPrivate *priv; - GtkStyleProperties *props; - guint i, j; - - css_provider = GTK_CSS_PROVIDER (provider); - priv = css_provider->priv; - props = gtk_style_properties_new (); - - css_provider_dump_symbolic_colors (css_provider, props); +#ifdef VERIFY_TREE + GtkCssProviderPrivate *priv = provider->priv; + GtkCssRuleset *ruleset; + gboolean should_match; + int i, j; for (i = 0; i < priv->rulesets->len; i++) { - GtkCssRuleset *ruleset; + gboolean found = FALSE; ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i); - if (ruleset->styles == NULL) - continue; - - if (!gtk_css_ruleset_matches (ruleset, path, 0)) - continue; - - for (j = 0; j < ruleset->n_styles; j++) - _gtk_style_properties_set_property_by_property (props, - GTK_CSS_STYLE_PROPERTY (ruleset->styles[i].property), - _gtk_css_selector_get_state_flags (ruleset->selector), - &ruleset->styles[i].value); + for (j = 0; j < tree_rules->len; j++) + { + if (ruleset == tree_rules->pdata[j]) + { + found = TRUE; + break; + } + } + should_match = _gtk_css_selector_matches (ruleset->selector, matcher); + if (found != !!should_match) + { + g_error ("expected rule '%s' to %s, but it %s\n", + _gtk_css_selector_to_string (ruleset->selector), + should_match ? "match" : "not match", + found ? "matched" : "didn't match"); + } } +#endif +} - return props; +static void +verify_tree_get_change_results (GtkCssProvider *provider, + const GtkCssMatcher *matcher, + GtkCssChange change) +{ +#ifdef VERIFY_TREE + { + GtkCssChange verify_change = 0; + GPtrArray *tree_rules; + int i; + + tree_rules = _gtk_css_selector_tree_match_all (provider->priv->tree, matcher); + verify_tree_match_results (provider, matcher, tree_rules); + + for (i = tree_rules->len - 1; i >= 0; i--) + { + GtkCssRuleset *ruleset; + + ruleset = tree_rules->pdata[i]; + + verify_change |= _gtk_css_selector_tree_match_get_change (ruleset->selector_match); + } + + if (change != verify_change) + { + GString *s; + + s = g_string_new (""); + g_string_append_printf (s, "expected change 0x%x, but it was 0x%x", verify_change, change); + if ((change & ~verify_change) != 0) + g_string_append_printf (s, ", unexpectedly set: 0x%x", change & ~verify_change); + if ((~change & verify_change) != 0) + g_string_append_printf (s, ", unexpectedly no set: 0x%x", ~change & verify_change); + g_warning (s->str); + g_string_free (s, TRUE); + } + + g_ptr_array_free (tree_rules, TRUE); + } +#endif } + static gboolean gtk_css_provider_get_style_property (GtkStyleProvider *provider, GtkWidgetPath *path, @@ -1501,26 +1488,29 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider, GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider); GtkCssProviderPrivate *priv = css_provider->priv; WidgetPropertyValue *val; + GPtrArray *tree_rules; + GtkCssMatcher matcher; gboolean found = FALSE; gchar *prop_name; gint i; + if (!_gtk_css_matcher_init (&matcher, path, state)) + return FALSE; + + tree_rules = _gtk_css_selector_tree_match_all (priv->tree, &matcher); + verify_tree_match_results (css_provider, &matcher, tree_rules); + prop_name = g_strdup_printf ("-%s-%s", g_type_name (pspec->owner_type), pspec->name); - for (i = priv->rulesets->len - 1; i >= 0; i--) + for (i = tree_rules->len - 1; i >= 0; i--) { - GtkCssRuleset *ruleset; - - ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i); + GtkCssRuleset *ruleset = tree_rules->pdata[i]; if (ruleset->widget_style == NULL) continue; - if (!gtk_css_ruleset_matches (ruleset, path, state)) - continue; - for (val = ruleset->widget_style; val != NULL; val = val->next) { if (strcmp (val->name, prop_name) == 0) @@ -1530,12 +1520,11 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider, scanner = gtk_css_scanner_new (css_provider, NULL, val->section, - gtk_css_section_get_file (val->section), - g_value_get_string (&val->value)); + val->section != NULL ? gtk_css_section_get_file (val->section) : NULL, + val->value); found = _gtk_css_style_parse_value (value, - scanner->parser, - NULL); + scanner->parser); gtk_css_scanner_destroy (scanner); @@ -1548,6 +1537,7 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider, } g_free (prop_name); + g_ptr_array_free (tree_rules, TRUE); return found; } @@ -1555,11 +1545,10 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider, static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface) { - iface->get_style = gtk_css_provider_get_style; iface->get_style_property = gtk_css_provider_get_style_property; } -static GtkSymbolicColor * +static GtkCssValue * gtk_css_style_provider_get_color (GtkStyleProviderPrivate *provider, const char *name) { @@ -1568,25 +1557,36 @@ gtk_css_style_provider_get_color (GtkStyleProviderPrivate *provider, return g_hash_table_lookup (css_provider->priv->symbolic_colors, name); } +static GtkCssKeyframes * +gtk_css_style_provider_get_keyframes (GtkStyleProviderPrivate *provider, + const char *name) +{ + GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider); + + return g_hash_table_lookup (css_provider->priv->keyframes, name); +} + static void gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider, - GtkWidgetPath *path, - GtkStateFlags state, + const GtkCssMatcher *matcher, GtkCssLookup *lookup) { GtkCssProvider *css_provider; GtkCssProviderPrivate *priv; - int i; + GtkCssRuleset *ruleset; guint j; + int i; + GPtrArray *tree_rules; css_provider = GTK_CSS_PROVIDER (provider); priv = css_provider->priv; - for (i = priv->rulesets->len - 1; i >= 0; i--) - { - GtkCssRuleset *ruleset; + tree_rules = _gtk_css_selector_tree_match_all (priv->tree, matcher); + verify_tree_match_results (css_provider, matcher, tree_rules); - ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i); + for (i = tree_rules->len - 1; i >= 0; i--) + { + ruleset = tree_rules->pdata[i]; if (ruleset->styles == NULL) continue; @@ -1595,9 +1595,6 @@ gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider, ruleset->set_styles)) continue; - if (!gtk_css_ruleset_matches (ruleset, path, state)) - continue; - for (j = 0; j < ruleset->n_styles; j++) { GtkCssStyleProperty *prop = ruleset->styles[j].property; @@ -1609,16 +1606,41 @@ gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider, _gtk_css_lookup_set (lookup, id, ruleset->styles[j].section, - &ruleset->styles[j].value); + ruleset->styles[j].value); } + + if (_gtk_bitmask_is_empty (_gtk_css_lookup_get_missing (lookup))) + break; } + + g_ptr_array_free (tree_rules, TRUE); +} + +static GtkCssChange +gtk_css_style_provider_get_change (GtkStyleProviderPrivate *provider, + const GtkCssMatcher *matcher) +{ + GtkCssProvider *css_provider; + GtkCssProviderPrivate *priv; + GtkCssChange change; + + css_provider = GTK_CSS_PROVIDER (provider); + priv = css_provider->priv; + + change = _gtk_css_selector_tree_get_change_all (priv->tree, matcher); + + verify_tree_get_change_results (css_provider, matcher, change); + + return change; } static void gtk_css_style_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface) { iface->get_color = gtk_css_style_provider_get_color; + iface->get_keyframes = gtk_css_style_provider_get_keyframes; iface->lookup = gtk_css_style_provider_lookup; + iface->get_change = gtk_css_style_provider_get_change; } static void @@ -1635,9 +1657,10 @@ gtk_css_provider_finalize (GObject *object) gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i)); g_array_free (priv->rulesets, TRUE); + _gtk_css_selector_tree_free (priv->tree); - if (priv->symbolic_colors) - g_hash_table_destroy (priv->symbolic_colors); + g_hash_table_destroy (priv->symbolic_colors); + g_hash_table_destroy (priv->keyframes); if (priv->resource) { @@ -1767,10 +1790,14 @@ gtk_css_provider_reset (GtkCssProvider *css_provider) } g_hash_table_remove_all (priv->symbolic_colors); + g_hash_table_remove_all (priv->keyframes); for (i = 0; i < priv->rulesets->len; i++) gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i)); g_array_set_size (priv->rulesets, 0); + _gtk_css_selector_tree_free (priv->tree); + priv->tree = NULL; + } static void @@ -1780,32 +1807,14 @@ gtk_css_provider_propagate_error (GtkCssProvider *provider, GError **propagate_to) { - GFileInfo *info; - GFile *file; - const char *path; - - file = gtk_css_section_get_file (section); - if (file) - { - info = g_file_query_info (file,G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, 0, NULL, NULL); - - if (info) - path = g_file_info_get_display_name (info); - else - path = ""; - } - else - { - info = NULL; - path = ""; - } + char *s; /* don't fail for deprecations */ if (g_error_matches (error, GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_DEPRECATED)) { - g_warning ("Theme parsing error: %s:%u:%u: %s", path, - gtk_css_section_get_end_line (section) + 1, - gtk_css_section_get_end_position (section), error->message); + s = _gtk_css_section_to_string (section); + g_warning ("Theme parsing error: %s: %s", s, error->message); + g_free (s); return; } @@ -1814,12 +1823,12 @@ gtk_css_provider_propagate_error (GtkCssProvider *provider, return; *propagate_to = g_error_copy (error); - g_prefix_error (propagate_to, "%s:%u:%u: ", path, - gtk_css_section_get_end_line (section) + 1, - gtk_css_section_get_end_position (section)); - - if (info) - g_object_unref (info); + if (section) + { + s = _gtk_css_section_to_string (section); + g_prefix_error (propagate_to, "%s", s); + g_free (s); + } } static gboolean @@ -1840,13 +1849,12 @@ parse_import (GtkCssScanner *scanner) char *uri; uri = _gtk_css_parser_read_string (scanner->parser); - file = g_file_resolve_relative_path (gtk_css_scanner_get_base_url (scanner), uri); + file = _gtk_css_parser_get_file_for_path (scanner->parser, uri); g_free (uri); } else { - file = _gtk_css_parser_read_url (scanner->parser, - gtk_css_scanner_get_base_url (scanner)); + file = _gtk_css_parser_read_url (scanner->parser); } if (file == NULL) @@ -1892,7 +1900,7 @@ parse_import (GtkCssScanner *scanner) static gboolean parse_color_definition (GtkCssScanner *scanner) { - GtkSymbolicColor *symbolic; + GtkCssValue *color; char *name; gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION); @@ -1916,8 +1924,8 @@ parse_color_definition (GtkCssScanner *scanner) return TRUE; } - symbolic = _gtk_css_parser_read_symbolic_color (scanner->parser); - if (symbolic == NULL) + color = _gtk_css_color_value_parse (scanner->parser); + if (color == NULL) { g_free (name); _gtk_css_parser_resync (scanner->parser, TRUE, 0); @@ -1928,7 +1936,7 @@ parse_color_definition (GtkCssScanner *scanner) if (!_gtk_css_parser_try (scanner->parser, ";", TRUE)) { g_free (name); - gtk_symbolic_color_unref (symbolic); + _gtk_css_value_unref (color); gtk_css_provider_error_literal (scanner->provider, scanner, GTK_CSS_PROVIDER_ERROR, @@ -1940,7 +1948,7 @@ parse_color_definition (GtkCssScanner *scanner) return TRUE; } - g_hash_table_insert (scanner->provider->priv->symbolic_colors, name, symbolic); + g_hash_table_insert (scanner->provider->priv->symbolic_colors, name, color); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION); return TRUE; @@ -2054,6 +2062,71 @@ skip_semicolon: return TRUE; } +static gboolean +parse_keyframes (GtkCssScanner *scanner) +{ + GtkCssKeyframes *keyframes; + char *name; + + gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_KEYFRAMES); + + if (!_gtk_css_parser_try (scanner->parser, "@keyframes", TRUE)) + { + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_KEYFRAMES); + return FALSE; + } + + name = _gtk_css_parser_try_ident (scanner->parser, TRUE); + if (name == NULL) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Expected name for keyframes"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + goto exit; + } + + if (!_gtk_css_parser_try (scanner->parser, "{", TRUE)) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Expected '{' for keyframes"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + g_free (name); + goto exit; + } + + keyframes = _gtk_css_keyframes_parse (scanner->parser); + if (keyframes == NULL) + { + _gtk_css_parser_resync (scanner->parser, TRUE, '}'); + g_free (name); + goto exit; + } + + g_hash_table_insert (scanner->provider->priv->keyframes, name, keyframes); + + if (!_gtk_css_parser_try (scanner->parser, "}", TRUE)) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "expected '}' after declarations"); + if (!_gtk_css_parser_is_eof (scanner->parser)) + _gtk_css_parser_resync (scanner->parser, FALSE, 0); + } + +exit: + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_KEYFRAMES); + + return TRUE; +} + static void parse_at_keyword (GtkCssScanner *scanner) { @@ -2063,6 +2136,8 @@ parse_at_keyword (GtkCssScanner *scanner) return; if (parse_binding_set (scanner)) return; + if (parse_keyframes (scanner)) + return; else { @@ -2141,66 +2216,64 @@ parse_declaration (GtkCssScanner *scanner, if (property) { - GValue value = { 0, }; + GtkCssValue *value; g_free (name); gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_VALUE); - if (_gtk_style_property_parse_value (property, - &value, - scanner->parser, - gtk_css_scanner_get_base_url (scanner))) + value = _gtk_style_property_parse_value (property, + scanner->parser); + + if (value == NULL) { - if (_gtk_css_parser_begins_with (scanner->parser, ';') || - _gtk_css_parser_begins_with (scanner->parser, '}') || - _gtk_css_parser_is_eof (scanner->parser)) - { - if (GTK_IS_CSS_SHORTHAND_PROPERTY (property)) - { - GtkCssShorthandProperty *shorthand = GTK_CSS_SHORTHAND_PROPERTY (property); - GArray *array = g_value_get_boxed (&value); - guint i; - - for (i = 0; i < _gtk_css_shorthand_property_get_n_subproperties (shorthand); i++) - { - GtkCssStyleProperty *child = _gtk_css_shorthand_property_get_subproperty (shorthand, i); - const GValue *sub = &g_array_index (array, GValue, i); - - gtk_css_ruleset_add (ruleset, child, sub, scanner->section); - } - } - else if (GTK_IS_CSS_STYLE_PROPERTY (property)) - { - gtk_css_ruleset_add (ruleset, GTK_CSS_STYLE_PROPERTY (property), &value, scanner->section); - } - else - { - g_assert_not_reached (); - } - - g_value_unset (&value); - } - else - { - gtk_css_provider_error_literal (scanner->provider, - scanner, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_SYNTAX, - "Junk at end of value"); - _gtk_css_parser_resync (scanner->parser, TRUE, '}'); - gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE); - gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION); - return; - } + _gtk_css_parser_resync (scanner->parser, TRUE, '}'); + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE); + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION); + return; } - else + + if (!_gtk_css_parser_begins_with (scanner->parser, ';') && + !_gtk_css_parser_begins_with (scanner->parser, '}') && + !_gtk_css_parser_is_eof (scanner->parser)) { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Junk at end of value"); _gtk_css_parser_resync (scanner->parser, TRUE, '}'); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE); gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION); return; } + + if (GTK_IS_CSS_SHORTHAND_PROPERTY (property)) + { + GtkCssShorthandProperty *shorthand = GTK_CSS_SHORTHAND_PROPERTY (property); + guint i; + + for (i = 0; i < _gtk_css_shorthand_property_get_n_subproperties (shorthand); i++) + { + GtkCssStyleProperty *child = _gtk_css_shorthand_property_get_subproperty (shorthand, i); + GtkCssValue *sub = _gtk_css_array_value_get_nth (value, i); + + gtk_css_ruleset_add (ruleset, child, _gtk_css_value_ref (sub), scanner->section); + } + + _gtk_css_value_unref (value); + } + else if (GTK_IS_CSS_STYLE_PROPERTY (property)) + { + gtk_css_ruleset_add (ruleset, GTK_CSS_STYLE_PROPERTY (property), value, scanner->section); + } + else + { + g_assert_not_reached (); + _gtk_css_value_unref (value); + } + + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE); } else if (name[0] == '-') @@ -2215,8 +2288,7 @@ parse_declaration (GtkCssScanner *scanner, WidgetPropertyValue *val; val = widget_property_value_new (name, scanner->section); - g_value_init (&val->value, G_TYPE_STRING); - g_value_take_string (&val->value, value_str); + val->value = value_str; gtk_css_ruleset_add_style (ruleset, name, val); } @@ -2353,13 +2425,6 @@ gtk_css_provider_compare_rule (gconstpointer a_, if (compare != 0) return compare; - /* compare pointers in array to ensure a stable sort */ - if (a_ < b_) - return -1; - - if (a_ > b_) - return 1; - return 0; } @@ -2367,8 +2432,38 @@ static void gtk_css_provider_postprocess (GtkCssProvider *css_provider) { GtkCssProviderPrivate *priv = css_provider->priv; + GtkCssSelectorTreeBuilder *builder; + guint i; g_array_sort (priv->rulesets, gtk_css_provider_compare_rule); + + builder = _gtk_css_selector_tree_builder_new (); + for (i = 0; i < priv->rulesets->len; i++) + { + GtkCssRuleset *ruleset; + + ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i); + + _gtk_css_selector_tree_builder_add (builder, + ruleset->selector, + &ruleset->selector_match, + ruleset); + } + + priv->tree = _gtk_css_selector_tree_builder_build (builder); + _gtk_css_selector_tree_builder_free (builder); + +#ifndef VERIFY_TREE + for (i = 0; i < priv->rulesets->len; i++) + { + GtkCssRuleset *ruleset; + + ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i); + + _gtk_css_selector_free (ruleset->selector); + ruleset->selector = NULL; + } +#endif } static gboolean @@ -2465,7 +2560,10 @@ gtk_css_provider_load_internal (GtkCssProvider *css_provider, * Loads @data into @css_provider, making it clear any previously loaded * information. * - * Returns: %TRUE if the data could be loaded. + * Returns: %TRUE. The return value is deprecated and %FALSE will only be + * returned for backwards compatibility reasons if an @error is not + * %NULL and a loading error occured. To track errors while loading + * CSS, connect to the GtkCssProvider::parsing-error signal. **/ gboolean gtk_css_provider_load_from_data (GtkCssProvider *css_provider, @@ -2496,6 +2594,8 @@ gtk_css_provider_load_from_data (GtkCssProvider *css_provider, g_free (free_data); + _gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (css_provider)); + return ret; } @@ -2508,19 +2608,28 @@ gtk_css_provider_load_from_data (GtkCssProvider *css_provider, * Loads the data contained in @file into @css_provider, making it * clear any previously loaded information. * - * Returns: %TRUE if the data could be loaded. + * Returns: %TRUE. The return value is deprecated and %FALSE will only be + * returned for backwards compatibility reasons if an @error is not + * %NULL and a loading error occured. To track errors while loading + * CSS, connect to the GtkCssProvider::parsing-error signal. **/ gboolean gtk_css_provider_load_from_file (GtkCssProvider *css_provider, GFile *file, GError **error) { + gboolean success; + g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE); g_return_val_if_fail (G_IS_FILE (file), FALSE); gtk_css_provider_reset (css_provider); - return gtk_css_provider_load_internal (css_provider, NULL, file, NULL, error); + success = gtk_css_provider_load_internal (css_provider, NULL, file, NULL, error); + + _gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (css_provider)); + + return success; } /** @@ -2532,7 +2641,10 @@ gtk_css_provider_load_from_file (GtkCssProvider *css_provider, * Loads the data contained in @path into @css_provider, making it clear * any previously loaded information. * - * Returns: %TRUE if the data could be loaded. + * Returns: %TRUE. The return value is deprecated and %FALSE will only be + * returned for backwards compatibility reasons if an @error is not + * %NULL and a loading error occured. To track errors while loading + * CSS, connect to the GtkCssProvider::parsing-error signal. **/ gboolean gtk_css_provider_load_from_path (GtkCssProvider *css_provider, @@ -2554,16 +2666,15 @@ gtk_css_provider_load_from_path (GtkCssProvider *css_provider, return result; } -static gboolean -_gtk_css_provider_load_from_resource (GtkCssProvider *css_provider, - const gchar *resource_path) +static void +gtk_css_provider_load_from_resource (GtkCssProvider *css_provider, + const gchar *resource_path) { GFile *file; char *uri, *escaped; - gboolean result; - g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE); - g_return_val_if_fail (resource_path != NULL, FALSE); + g_return_if_fail (GTK_IS_CSS_PROVIDER (css_provider)); + g_return_if_fail (resource_path != NULL); escaped = g_uri_escape_string (resource_path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE); @@ -2573,11 +2684,9 @@ _gtk_css_provider_load_from_resource (GtkCssProvider *css_provider, file = g_file_new_for_uri (uri); g_free (uri); - result = gtk_css_provider_load_from_file (css_provider, file, NULL); + gtk_css_provider_load_from_file (css_provider, file, NULL); g_object_unref (file); - - return result; } /** @@ -2619,83 +2728,70 @@ _gtk_css_provider_get_theme_dir (void) } /** - * gtk_css_provider_get_named: + * _gtk_css_provider_load_named: + * @provider: a #GtkCssProvider * @name: A theme name * @variant: (allow-none): variant to load, for example, "dark", or * %NULL for the default * - * Loads a theme from the usual theme paths - * - * Returns: (transfer none): a #GtkCssProvider with the theme loaded. - * This memory is owned by GTK+, and you must not free it. - */ -GtkCssProvider * -gtk_css_provider_get_named (const gchar *name, - const gchar *variant) + * Loads a theme from the usual theme paths. The actual process of + * finding the theme might change between releases, but it is + * guaranteed that this function uses the same mechanism to load the + * theme than GTK uses for loading its own theme. + **/ +void +_gtk_css_provider_load_named (GtkCssProvider *provider, + const gchar *name, + const gchar *variant) { - static GHashTable *themes = NULL; - GtkCssProvider *provider; - gchar *key; + gchar *subpath, *path; + gchar *resource_path; - if (variant == NULL) - key = (gchar *)name; - else - key = g_strconcat (name, "-", variant, NULL); + g_return_if_fail (GTK_IS_CSS_PROVIDER (provider)); + g_return_if_fail (name != NULL); - if (G_UNLIKELY (!themes)) - themes = g_hash_table_new (g_str_hash, g_str_equal); + gtk_css_provider_reset (provider); - provider = g_hash_table_lookup (themes, key); + /* try loading the resource for the theme. This is mostly meant for built-in + * themes. + */ + if (variant) + resource_path = g_strdup_printf ("/org/gtk/libgtk/%s-%s.css", name, variant); + else + resource_path = g_strdup_printf ("/org/gtk/libgtk/%s.css", name); - if (!provider) + if (g_resources_get_info (resource_path, 0, NULL, NULL, NULL)) { - gchar *resource_path = NULL; - - if (variant) - resource_path = g_strdup_printf ("/org/gtk/libgtk/%s-%s.css", name, variant); - else - resource_path = g_strdup_printf ("/org/gtk/libgtk/%s.css", name); - - if (g_resources_get_info (resource_path, 0, NULL, NULL, NULL)) - { - provider = gtk_css_provider_new (); - if (!_gtk_css_provider_load_from_resource (provider, resource_path)) - { - g_object_unref (provider); - provider = NULL; - } - } + gtk_css_provider_load_from_resource (provider, resource_path); g_free (resource_path); + return; } + g_free (resource_path); - if (!provider) - { - const gchar *home_dir; - gchar *subpath, *path = NULL; - if (variant) - subpath = g_strdup_printf ("gtk-3.0" G_DIR_SEPARATOR_S "gtk-%s.css", variant); - else - subpath = g_strdup ("gtk-3.0" G_DIR_SEPARATOR_S "gtk.css"); + /* Next try looking for files in the various theme directories. + */ + if (variant) + subpath = g_strdup_printf ("gtk-3.0" G_DIR_SEPARATOR_S "gtk-%s.css", variant); + else + subpath = g_strdup ("gtk-3.0" G_DIR_SEPARATOR_S "gtk.css"); - /* First look in the users home directory + /* First look in the user's config directory + */ + path = g_build_filename (g_get_user_data_dir (), "themes", name, subpath, NULL); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) + { + g_free (path); + /* Next look in the user's home directory */ - home_dir = g_get_home_dir (); - if (home_dir) - { - path = g_build_filename (home_dir, ".themes", name, subpath, NULL); - - if (!g_file_test (path, G_FILE_TEST_EXISTS)) - { - g_free (path); - path = NULL; - } - } - - if (!path) + path = g_build_filename (g_get_home_dir (), ".themes", name, subpath, NULL); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { gchar *theme_dir; + g_free (path); + + /* Finally, try in the default theme directory */ theme_dir = _gtk_css_provider_get_theme_dir (); path = g_build_filename (theme_dir, name, subpath, NULL); g_free (theme_dir); @@ -2706,48 +2802,85 @@ gtk_css_provider_get_named (const gchar *name, path = NULL; } } + } - g_free (subpath); + g_free (subpath); - if (path) - { - char *dir, *resource_file; - GResource *resource; + if (path) + { + char *dir, *resource_file; + GResource *resource; - provider = gtk_css_provider_new (); + dir = g_path_get_dirname (path); + resource_file = g_build_filename (dir, "gtk.gresource", NULL); + resource = g_resource_load (resource_file, NULL); + g_free (resource_file); - dir = g_path_get_dirname (path); - resource_file = g_build_filename (dir, "gtk.gresource", NULL); - resource = g_resource_load (resource_file, NULL); - g_free (resource_file); + if (resource != NULL) + g_resources_register (resource); - if (resource != NULL) - g_resources_register (resource); + gtk_css_provider_load_from_path (provider, path, NULL); - if (!gtk_css_provider_load_from_path (provider, path, NULL)) - { - if (resource != NULL) - { - g_resources_unregister (resource); - g_resource_unref (resource); - } - g_object_unref (provider); - provider = NULL; - } - else - { - /* Only set this after load success, as load_from_path will clear it */ - provider->priv->resource = resource; - g_hash_table_insert (themes, g_strdup (key), provider); - } + /* Only set this after load, as load_from_path will clear it */ + provider->priv->resource = resource; - g_free (path); - g_free (dir); + g_free (path); + g_free (dir); + } + else + { + /* Things failed! Fall back! Fall back! */ + + if (variant) + { + /* If there was a variant, try without */ + _gtk_css_provider_load_named (provider, name, NULL); + } + else + { + /* Worst case, fall back to Raleigh */ + g_return_if_fail (!g_str_equal (name, "Raleigh")); /* infloop protection */ + _gtk_css_provider_load_named (provider, "Raleigh", NULL); } } +} - if (key != name) - g_free (key); +/** + * gtk_css_provider_get_named: + * @name: A theme name + * @variant: (allow-none): variant to load, for example, "dark", or + * %NULL for the default + * + * Loads a theme from the usual theme paths + * + * Returns: (transfer none): a #GtkCssProvider with the theme loaded. + * This memory is owned by GTK+, and you must not free it. + */ +GtkCssProvider * +gtk_css_provider_get_named (const gchar *name, + const gchar *variant) +{ + static GHashTable *themes = NULL; + GtkCssProvider *provider; + gchar *key; + + if (variant == NULL) + key = g_strdup (name); + else + key = g_strconcat (name, "-", variant, NULL); + if (G_UNLIKELY (!themes)) + themes = g_hash_table_new (g_str_hash, g_str_equal); + + provider = g_hash_table_lookup (themes, key); + + if (!provider) + { + provider = gtk_css_provider_new (); + _gtk_css_provider_load_named (provider, name, variant); + g_hash_table_insert (themes, g_strdup (key), provider); + } + + g_free (key); return provider; } @@ -2779,7 +2912,7 @@ gtk_css_ruleset_print (const GtkCssRuleset *ruleset, WidgetPropertyValue *widget_value; guint i; - _gtk_css_selector_print (ruleset->selector, str); + _gtk_css_selector_tree_match_print (ruleset->selector_match, str); g_string_append (str, " {\n"); @@ -2799,7 +2932,7 @@ gtk_css_ruleset_print (const GtkCssRuleset *ruleset, g_string_append (str, " "); g_string_append (str, _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop->property))); g_string_append (str, ": "); - _gtk_css_style_property_print_value (prop->property, &prop->value, str); + _gtk_css_value_print (prop->value, str); g_string_append (str, ";\n"); } @@ -2822,7 +2955,7 @@ gtk_css_ruleset_print (const GtkCssRuleset *ruleset, g_string_append (str, " "); g_string_append (str, widget_value->name); g_string_append (str, ": "); - g_string_append (str, g_value_get_string (&widget_value->value)); + g_string_append (str, widget_value->value); g_string_append (str, ";\n"); } @@ -2837,7 +2970,6 @@ gtk_css_provider_print_colors (GHashTable *colors, GString *str) { GList *keys, *walk; - char *s; keys = g_hash_table_get_keys (colors); /* so the output is identical for identical styles */ @@ -2846,20 +2978,45 @@ gtk_css_provider_print_colors (GHashTable *colors, for (walk = keys; walk; walk = walk->next) { const char *name = walk->data; - GtkSymbolicColor *symbolic = g_hash_table_lookup (colors, (gpointer) name); + GtkCssValue *color = g_hash_table_lookup (colors, (gpointer) name); g_string_append (str, "@define-color "); g_string_append (str, name); g_string_append (str, " "); - s = gtk_symbolic_color_to_string (symbolic); - g_string_append (str, s); - g_free (s); + _gtk_css_value_print (color, str); g_string_append (str, ";\n"); } g_list_free (keys); } +static void +gtk_css_provider_print_keyframes (GHashTable *keyframes, + GString *str) +{ + GList *keys, *walk; + + keys = g_hash_table_get_keys (keyframes); + /* so the output is identical for identical styles */ + keys = g_list_sort (keys, (GCompareFunc) strcmp); + + for (walk = keys; walk; walk = walk->next) + { + const char *name = walk->data; + GtkCssKeyframes *keyframe = g_hash_table_lookup (keyframes, (gpointer) name); + + if (str->len > 0) + g_string_append (str, "\n"); + g_string_append (str, "@keyframes "); + g_string_append (str, name); + g_string_append (str, " {\n"); + _gtk_css_keyframes_print (keyframe, str); + g_string_append (str, "}\n"); + } + + g_list_free (keys); +} + /** * gtk_css_provider_to_string: * @provider: the provider to write to a string @@ -2890,10 +3047,11 @@ gtk_css_provider_to_string (GtkCssProvider *provider) str = g_string_new (""); gtk_css_provider_print_colors (priv->symbolic_colors, str); + gtk_css_provider_print_keyframes (priv->keyframes, str); for (i = 0; i < priv->rulesets->len; i++) { - if (i > 0) + if (str->len != 0) g_string_append (str, "\n"); gtk_css_ruleset_print (&g_array_index (priv->rulesets, GtkCssRuleset, i), str); }