X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkcssprovider.c;h=751266847464ff2e9d495af992c5b203d60c7e8a;hb=feb64f40b0f50735104da0a7fdafbe480763c180;hp=137f3aeca07e682f987a6593df28620cf6f07746;hpb=f70fc49ebc999bc4e37c0807e818aeff2bca3459;p=~andy%2Fgtk diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c index 137f3aeca..751266847 100644 --- a/gtk/gtkcssprovider.c +++ b/gtk/gtkcssprovider.c @@ -27,12 +27,13 @@ #include "gtkbitmaskprivate.h" #include "gtkcssarrayvalueprivate.h" -#include "gtkcssstylefuncsprivate.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" @@ -985,6 +986,7 @@ struct _WidgetPropertyValue { struct GtkCssRuleset { GtkCssSelector *selector; + GtkCssSelectorTree *selector_match; WidgetPropertyValue *widget_style; PropertyValue *styles; GtkBitmask *set_styles; @@ -1007,8 +1009,10 @@ struct _GtkCssProviderPrivate GScanner *scanner; GHashTable *symbolic_colors; + GHashTable *keyframes; GArray *rulesets; + GtkCssSelectorTree *tree; GResource *resource; }; @@ -1061,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); - - if (info) - path = g_file_info_get_display_name (info); - else - path = ""; - } - else - { - info = NULL; - path = ""; - } + char *s = _gtk_css_section_to_string (section); - 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); } } @@ -1290,19 +1273,6 @@ gtk_css_ruleset_add (GtkCssRuleset *ruleset, ruleset->styles[i].section = NULL; } -static gboolean -gtk_css_ruleset_matches (GtkCssRuleset *ruleset, - const GtkCssMatcher *matcher) -{ - return _gtk_css_selector_matches (ruleset->selector, matcher); -} - -static GtkCssChange -gtk_css_ruleset_get_change (GtkCssRuleset *ruleset) -{ - return _gtk_css_selector_get_change (ruleset->selector); -} - static void gtk_css_scanner_destroy (GtkCssScanner *scanner) { @@ -1422,72 +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); +#ifdef VERIFY_TREE + GtkCssProviderPrivate *priv = provider->priv; + GtkCssRuleset *ruleset; + gboolean should_match; + int i, j; - while (g_hash_table_iter_next (&iter, &key, &value)) + for (i = 0; i < priv->rulesets->len; i++) { - const gchar *name; - GtkSymbolicColor *color; + gboolean found = FALSE; - name = key; - color = value; + ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i); - gtk_style_properties_map_color (props, name, color); + 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 } -static GtkStyleProperties * -gtk_css_provider_get_style (GtkStyleProvider *provider, - GtkWidgetPath *path) -{ - GtkCssMatcher matcher; - 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); - if (_gtk_css_matcher_init (&matcher, path, 0)) - { - for (i = 0; i < priv->rulesets->len; i++) - { - GtkCssRuleset *ruleset; - - ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i); - - if (ruleset->styles == NULL) - continue; - - if (!gtk_css_ruleset_matches (ruleset, &matcher)) - 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); - } - } - - 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, @@ -1498,6 +1488,7 @@ 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; @@ -1506,22 +1497,20 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider, 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, &matcher)) - continue; - for (val = ruleset->widget_style; val != NULL; val = val->next) { if (strcmp (val->name, prop_name) == 0) @@ -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,6 +1557,15 @@ 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, const GtkCssMatcher *matcher, @@ -1575,17 +1573,20 @@ gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider, { 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; @@ -1594,9 +1595,6 @@ gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider, ruleset->set_styles)) continue; - if (!gtk_css_ruleset_matches (ruleset, matcher)) - continue; - for (j = 0; j < ruleset->n_styles; j++) { GtkCssStyleProperty *prop = ruleset->styles[j].property; @@ -1610,7 +1608,12 @@ gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider, ruleset->styles[j].section, 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 @@ -1619,26 +1622,14 @@ gtk_css_style_provider_get_change (GtkStyleProviderPrivate *provider, { GtkCssProvider *css_provider; GtkCssProviderPrivate *priv; - GtkCssChange change = 0; - int i; + GtkCssChange change; css_provider = GTK_CSS_PROVIDER (provider); priv = css_provider->priv; - for (i = priv->rulesets->len - 1; i >= 0; i--) - { - GtkCssRuleset *ruleset; + change = _gtk_css_selector_tree_get_change_all (priv->tree, matcher); - ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i); - - if (ruleset->styles == NULL) - continue; - - if (!gtk_css_ruleset_matches (ruleset, matcher)) - continue; - - change |= gtk_css_ruleset_get_change (ruleset); - } + verify_tree_get_change_results (css_provider, matcher, change); return change; } @@ -1647,6 +1638,7 @@ 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; } @@ -1665,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) { @@ -1797,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 @@ -1810,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; } @@ -1844,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 @@ -1921,7 +1900,7 @@ parse_import (GtkCssScanner *scanner) static gboolean parse_color_definition (GtkCssScanner *scanner) { - GtkCssValue *symbolic; + GtkCssValue *color; char *name; gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION); @@ -1945,8 +1924,8 @@ parse_color_definition (GtkCssScanner *scanner) return TRUE; } - symbolic = _gtk_css_symbolic_value_new (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); @@ -1957,7 +1936,7 @@ parse_color_definition (GtkCssScanner *scanner) if (!_gtk_css_parser_try (scanner->parser, ";", TRUE)) { g_free (name); - _gtk_css_value_unref (symbolic); + _gtk_css_value_unref (color); gtk_css_provider_error_literal (scanner->provider, scanner, GTK_CSS_PROVIDER_ERROR, @@ -1969,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; @@ -2083,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) { @@ -2092,6 +2136,8 @@ parse_at_keyword (GtkCssScanner *scanner) return; if (parse_binding_set (scanner)) return; + if (parse_keyframes (scanner)) + return; else { @@ -2386,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 @@ -2484,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, @@ -2529,7 +2608,10 @@ 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, @@ -2559,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, @@ -2581,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); @@ -2600,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; } /** @@ -2646,96 +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) - { - 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"); - /* 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); - path = NULL; - } + /* 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 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 */ - if (!path) - { - const gchar *home_dir; - - 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); @@ -2746,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; } @@ -2819,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"); @@ -2839,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"); } @@ -2877,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 */ @@ -2886,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 @@ -2930,6 +3047,7 @@ 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++) {