X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkcssprovider.c;h=751266847464ff2e9d495af992c5b203d60c7e8a;hb=d97861bd8b338c3d25d7ffb5496edee9eee9bfbb;hp=7ed86dc9d40f64e38c7051aaaf50aa842b219f5b;hpb=a96c122fa478dd3b341a8c4684bfab830d1b1310;p=~andy%2Fgtk diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c index 7ed86dc9d..751266847 100644 --- a/gtk/gtkcssprovider.c +++ b/gtk/gtkcssprovider.c @@ -12,9 +12,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ #include "config.h" @@ -28,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" @@ -917,7 +917,7 @@ * * text-shadow * shadow list (see above) - * #GtkTextShadow + * internal use only * text-shadow: 1 1 0 blue, -4 -4 red; * * @@ -964,15 +964,35 @@ typedef struct GtkCssRuleset GtkCssRuleset; typedef struct _GtkCssScanner GtkCssScanner; +typedef struct _PropertyValue PropertyValue; +typedef struct _WidgetPropertyValue WidgetPropertyValue; typedef enum ParserScope ParserScope; typedef enum ParserSymbol ParserSymbol; +struct _PropertyValue { + GtkCssStyleProperty *property; + GtkCssValue *value; + GtkCssSection *section; +}; + +struct _WidgetPropertyValue { + WidgetPropertyValue *next; + char *name; + char *value; + + GtkCssSection *section; +}; + struct GtkCssRuleset { GtkCssSelector *selector; - GHashTable *widget_style; - GHashTable *style; + GtkCssSelectorTree *selector_match; + WidgetPropertyValue *widget_style; + PropertyValue *styles; GtkBitmask *set_styles; + guint n_styles; + guint owns_styles : 1; + guint owns_widget_style : 1; }; struct _GtkCssScanner @@ -981,8 +1001,6 @@ struct _GtkCssScanner GtkCssParser *parser; GtkCssSection *section; GtkCssScanner *parent; - GFile *file; - GFile *base; GSList *state; }; @@ -991,8 +1009,10 @@ struct _GtkCssProviderPrivate GScanner *scanner; GHashTable *symbolic_colors; + GHashTable *keyframes; GArray *rulesets; + GtkCssSelectorTree *tree; GResource *resource; }; @@ -1001,11 +1021,14 @@ 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); static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface); static void gtk_css_style_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface); +static void widget_property_value_list_free (WidgetPropertyValue *head); static gboolean gtk_css_provider_load_internal (GtkCssProvider *css_provider, @@ -1042,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); } } @@ -1078,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 @@ -1114,16 +1119,17 @@ gtk_css_provider_class_init (GtkCssProviderClass *klass) static void gtk_css_ruleset_init_copy (GtkCssRuleset *new, - const GtkCssRuleset *ruleset, + GtkCssRuleset *ruleset, GtkCssSelector *selector) { memcpy (new, ruleset, sizeof (GtkCssRuleset)); new->selector = selector; - if (new->widget_style) - g_hash_table_ref (new->widget_style); - if (new->style) - g_hash_table_ref (new->style); + /* First copy takes over ownership */ + if (ruleset->owns_styles) + ruleset->owns_styles = FALSE; + if (ruleset->owns_widget_style) + ruleset->owns_widget_style = FALSE; if (new->set_styles) new->set_styles = _gtk_bitmask_copy (new->set_styles); } @@ -1131,116 +1137,140 @@ gtk_css_ruleset_init_copy (GtkCssRuleset *new, static void gtk_css_ruleset_clear (GtkCssRuleset *ruleset) { - if (ruleset->style) - g_hash_table_unref (ruleset->style); + if (ruleset->owns_styles) + { + guint i; + + for (i = 0; i < ruleset->n_styles; i++) + { + _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); + } if (ruleset->set_styles) _gtk_bitmask_free (ruleset->set_styles); - if (ruleset->widget_style) - g_hash_table_unref (ruleset->widget_style); + if (ruleset->owns_widget_style) + widget_property_value_list_free (ruleset->widget_style); if (ruleset->selector) _gtk_css_selector_free (ruleset->selector); memset (ruleset, 0, sizeof (GtkCssRuleset)); } -typedef struct _PropertyValue PropertyValue; -struct _PropertyValue { - GtkCssSection *section; - GValue value; -}; - -static PropertyValue * -property_value_new (GtkCssSection *section) +static WidgetPropertyValue * +widget_property_value_new (char *name, GtkCssSection *section) { - PropertyValue *value; + WidgetPropertyValue *value; - value = g_slice_new0 (PropertyValue); + value = g_slice_new0 (WidgetPropertyValue); - value->section = gtk_css_section_ref (section); + value->name = name; + if (gtk_keep_css_sections) + value->section = gtk_css_section_ref (section); return value; } static void -property_value_free (PropertyValue *value) +widget_property_value_free (WidgetPropertyValue *value) +{ + g_free (value->value); + g_free (value->name); + if (value->section) + gtk_css_section_unref (value->section); + + g_slice_free (WidgetPropertyValue, value); +} + +static void +widget_property_value_list_free (WidgetPropertyValue *head) +{ + WidgetPropertyValue *l, *next; + for (l = head; l != NULL; l = next) + { + next = l->next; + widget_property_value_free (l); + } +} + +static WidgetPropertyValue * +widget_property_value_list_remove_name (WidgetPropertyValue *head, const char *name) { - if (G_IS_VALUE (&value->value)) - g_value_unset (&value->value); + WidgetPropertyValue *l, **last; + + last = &head; + + for (l = head; l != NULL; l = l->next) + { + if (strcmp (l->name, name) == 0) + { + *last = l->next; + widget_property_value_free (l); + break; + } - gtk_css_section_unref (value->section); + last = &l->next; + } - g_slice_free (PropertyValue, value); + return head; } static void gtk_css_ruleset_add_style (GtkCssRuleset *ruleset, char *name, - PropertyValue *value) + WidgetPropertyValue *value) { - if (ruleset->widget_style == NULL) - ruleset->widget_style = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) property_value_free); - - g_hash_table_insert (ruleset->widget_style, name, value); + value->next = widget_property_value_list_remove_name (ruleset->widget_style, name); + ruleset->widget_style = value; + ruleset->owns_widget_style = TRUE; } static void -gtk_css_ruleset_add (GtkCssRuleset *ruleset, - GtkStyleProperty *prop, - PropertyValue *value) +gtk_css_ruleset_add (GtkCssRuleset *ruleset, + GtkCssStyleProperty *property, + GtkCssValue *value, + GtkCssSection *section) { - if (ruleset->style == NULL) - { - ruleset->style = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) property_value_free); - ruleset->set_styles = _gtk_bitmask_new (); - } + guint i; - if (GTK_IS_CSS_SHORTHAND_PROPERTY (prop)) - { - GtkCssShorthandProperty *shorthand = GTK_CSS_SHORTHAND_PROPERTY (prop); - GArray *array = g_value_get_boxed (&value->value); - guint i; + g_return_if_fail (ruleset->owns_styles || ruleset->n_styles == 0); + + if (ruleset->set_styles == NULL) + ruleset->set_styles = _gtk_bitmask_new (); + + ruleset->set_styles = _gtk_bitmask_set (ruleset->set_styles, + _gtk_css_style_property_get_id (property), + TRUE); - for (i = 0; i < _gtk_css_shorthand_property_get_n_subproperties (shorthand); i++) + ruleset->owns_styles = TRUE; + + for (i = 0; i < ruleset->n_styles; i++) + { + if (ruleset->styles[i].property == property) { - GtkCssStyleProperty *child = _gtk_css_shorthand_property_get_subproperty (shorthand, i); - const GValue *sub = &g_array_index (array, GValue, i); - PropertyValue *val; - - val = property_value_new (value->section); - g_value_init (&val->value, G_VALUE_TYPE (sub)); - g_value_copy (sub, &val->value); - gtk_css_ruleset_add (ruleset, GTK_STYLE_PROPERTY (child), val); + _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; } - property_value_free (value); - } - else if (GTK_IS_CSS_STYLE_PROPERTY (prop)) - { - g_return_if_fail (_gtk_css_style_property_is_specified_type (GTK_CSS_STYLE_PROPERTY (prop), - G_VALUE_TYPE (&value->value))); - - _gtk_bitmask_set (ruleset->set_styles, - _gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (prop)), - TRUE); - g_hash_table_insert (ruleset->style, prop, value); } - else + if (i == ruleset->n_styles) { - g_assert_not_reached (); + ruleset->n_styles++; + ruleset->styles = g_realloc (ruleset->styles, ruleset->n_styles * sizeof (PropertyValue)); + ruleset->styles[i].value = NULL; + ruleset->styles[i].property = property; } -} -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 @@ -1249,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); @@ -1295,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; @@ -1343,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); @@ -1382,78 +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) +static void +verify_tree_get_change_results (GtkCssProvider *provider, + const GtkCssMatcher *matcher, + GtkCssChange change) { - GtkCssProvider *css_provider; - GtkCssProviderPrivate *priv; - GtkStyleProperties *props; - guint i; +#ifdef VERIFY_TREE + { + GtkCssChange verify_change = 0; + GPtrArray *tree_rules; + int i; - css_provider = GTK_CSS_PROVIDER (provider); - priv = css_provider->priv; - props = gtk_style_properties_new (); - - css_provider_dump_symbolic_colors (css_provider, props); - - for (i = 0; i < priv->rulesets->len; i++) - { - GtkCssRuleset *ruleset; - GHashTableIter iter; - gpointer key, val; - - ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i); + tree_rules = _gtk_css_selector_tree_match_all (provider->priv->tree, matcher); + verify_tree_match_results (provider, matcher, tree_rules); - if (ruleset->style == NULL) - continue; + for (i = tree_rules->len - 1; i >= 0; i--) + { + GtkCssRuleset *ruleset; - if (!gtk_css_ruleset_matches (ruleset, path, 0)) - continue; + ruleset = tree_rules->pdata[i]; - g_hash_table_iter_init (&iter, ruleset->style); + verify_change |= _gtk_css_selector_tree_match_get_change (ruleset->selector_match); + } - while (g_hash_table_iter_next (&iter, &key, &val)) - { - GtkCssStyleProperty *prop = key; - PropertyValue *value = val; + if (change != verify_change) + { + GString *s; - _gtk_style_properties_set_property_by_property (props, - prop, - _gtk_css_selector_get_state_flags (ruleset->selector), - &value->value); - } - } + 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); + } - return props; + g_ptr_array_free (tree_rules, TRUE); + } +#endif } + static gboolean gtk_css_provider_get_style_property (GtkStyleProvider *provider, GtkWidgetPath *path, @@ -1463,51 +1487,57 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider, { GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider); GtkCssProviderPrivate *priv = css_provider->priv; - PropertyValue *val; + 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; - - val = g_hash_table_lookup (ruleset->widget_style, prop_name); + for (val = ruleset->widget_style; val != NULL; val = val->next) + { + if (strcmp (val->name, prop_name) == 0) + { + GtkCssScanner *scanner; - if (val) - { - GtkCssScanner *scanner; + scanner = gtk_css_scanner_new (css_provider, + NULL, + val->section, + val->section != NULL ? gtk_css_section_get_file (val->section) : NULL, + val->value); - scanner = gtk_css_scanner_new (css_provider, - NULL, - val->section, - gtk_css_section_get_file (val->section), - g_value_get_string (&val->value)); + found = _gtk_css_style_parse_value (value, + scanner->parser); - found = _gtk_css_style_parse_value (value, - scanner->parser, - NULL); + gtk_css_scanner_destroy (scanner); - gtk_css_scanner_destroy (scanner); + break; + } + } - if (found) - break; - } + if (found) + break; } g_free (prop_name); + g_ptr_array_free (tree_rules, TRUE); return found; } @@ -1515,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) { @@ -1528,58 +1557,90 @@ 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; + 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; - GHashTableIter iter; - gpointer key, val; + 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->style == NULL) + if (ruleset->styles == NULL) continue; if (!_gtk_bitmask_intersects (_gtk_css_lookup_get_missing (lookup), ruleset->set_styles)) continue; - if (!gtk_css_ruleset_matches (ruleset, path, state)) - continue; - - g_hash_table_iter_init (&iter, ruleset->style); - - while (g_hash_table_iter_next (&iter, &key, &val)) + for (j = 0; j < ruleset->n_styles; j++) { - GtkCssStyleProperty *prop = key; - PropertyValue *value = val; + GtkCssStyleProperty *prop = ruleset->styles[j].property; guint id = _gtk_css_style_property_get_id (prop); if (!_gtk_css_lookup_is_missing (lookup, id)) continue; - _gtk_css_lookup_set (lookup, id, value->section, &value->value); + _gtk_css_lookup_set (lookup, + id, + 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 +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 @@ -1596,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) { @@ -1694,7 +1756,7 @@ css_provider_commit (GtkCssProvider *css_provider, priv = css_provider->priv; - if (ruleset->style == NULL && ruleset->widget_style == NULL) + if (ruleset->styles == NULL && ruleset->widget_style == NULL) { g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free); return; @@ -1728,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 @@ -1741,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; } @@ -1775,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 @@ -1801,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) @@ -1853,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); @@ -1877,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); @@ -1889,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, @@ -1901,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; @@ -2015,302 +2062,92 @@ skip_semicolon: return TRUE; } -static void -parse_at_keyword (GtkCssScanner *scanner) +static gboolean +parse_keyframes (GtkCssScanner *scanner) { - if (parse_import (scanner)) - return; - if (parse_color_definition (scanner)) - return; - if (parse_binding_set (scanner)) - return; + GtkCssKeyframes *keyframes; + char *name; - else + gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_KEYFRAMES); + + if (!_gtk_css_parser_try (scanner->parser, "@keyframes", TRUE)) { - gtk_css_provider_error_literal (scanner->provider, - scanner, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_SYNTAX, - "unknown @ rule"); - _gtk_css_parser_resync (scanner->parser, TRUE, 0); + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_KEYFRAMES); + return FALSE; } -} - -static gboolean -parse_selector_class (GtkCssScanner *scanner, GArray *classes) -{ - GQuark qname; - char *name; - - name = _gtk_css_parser_try_name (scanner->parser, 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 a valid name for class"); - return FALSE; + "Expected name for keyframes"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + goto exit; } - qname = g_quark_from_string (name); - g_array_append_val (classes, qname); - g_free (name); - return TRUE; -} - -static gboolean -parse_selector_name (GtkCssScanner *scanner, GArray *names) -{ - GQuark qname; - char *name; - - name = _gtk_css_parser_try_name (scanner->parser, FALSE); - - if (name == NULL) + 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 a valid name for id"); - return FALSE; + "Expected '{' for keyframes"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + g_free (name); + goto exit; } - qname = g_quark_from_string (name); - g_array_append_val (names, qname); - g_free (name); - return TRUE; -} + keyframes = _gtk_css_keyframes_parse (scanner->parser); + if (keyframes == NULL) + { + _gtk_css_parser_resync (scanner->parser, TRUE, '}'); + g_free (name); + goto exit; + } -static gboolean -parse_selector_pseudo_class (GtkCssScanner *scanner, - GtkRegionFlags *region_to_modify, - GtkStateFlags *state_to_modify) -{ - struct { - const char *name; - GtkRegionFlags region_flag; - GtkStateFlags state_flag; - } pseudo_classes[] = { - { "first-child", GTK_REGION_FIRST, 0 }, - { "last-child", GTK_REGION_LAST, 0 }, - { "only-child", GTK_REGION_ONLY, 0 }, - { "sorted", GTK_REGION_SORTED, 0 }, - { "active", 0, GTK_STATE_FLAG_ACTIVE }, - { "prelight", 0, GTK_STATE_FLAG_PRELIGHT }, - { "hover", 0, GTK_STATE_FLAG_PRELIGHT }, - { "selected", 0, GTK_STATE_FLAG_SELECTED }, - { "insensitive", 0, GTK_STATE_FLAG_INSENSITIVE }, - { "inconsistent", 0, GTK_STATE_FLAG_INCONSISTENT }, - { "focused", 0, GTK_STATE_FLAG_FOCUSED }, - { "focus", 0, GTK_STATE_FLAG_FOCUSED }, - { "backdrop", 0, GTK_STATE_FLAG_BACKDROP }, - { NULL, } - }, nth_child_classes[] = { - { "first", GTK_REGION_FIRST, 0 }, - { "last", GTK_REGION_LAST, 0 }, - { "even", GTK_REGION_EVEN, 0 }, - { "odd", GTK_REGION_ODD, 0 }, - { NULL, } - }, *classes; - guint i; - char *name; + g_hash_table_insert (scanner->provider->priv->keyframes, name, keyframes); - name = _gtk_css_parser_try_ident (scanner->parser, FALSE); - if (name == NULL) + 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, - "Missing name of pseudo-class"); - return FALSE; - } - - if (_gtk_css_parser_try (scanner->parser, "(", TRUE)) - { - char *function = name; - - name = _gtk_css_parser_try_ident (scanner->parser, TRUE); - if (!_gtk_css_parser_try (scanner->parser, ")", FALSE)) - { - gtk_css_provider_error_literal (scanner->provider, - scanner, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_SYNTAX, - "Missing closing bracket for pseudo-class"); - return FALSE; - } - - if (g_ascii_strcasecmp (function, "nth-child") != 0) - { - gtk_css_provider_error (scanner->provider, - scanner, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE, - "Unknown pseudo-class '%s(%s)'", function, name ? name : ""); - g_free (function); - g_free (name); - return FALSE; - } - - g_free (function); - - if (name == NULL) - { - gtk_css_provider_error (scanner->provider, - scanner, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE, - "nth-child() requires an argument"); - return FALSE; - } - - classes = nth_child_classes; + "expected '}' after declarations"); + if (!_gtk_css_parser_is_eof (scanner->parser)) + _gtk_css_parser_resync (scanner->parser, FALSE, 0); } - else - classes = pseudo_classes; - for (i = 0; classes[i].name != NULL; i++) - { - if (g_ascii_strcasecmp (name, classes[i].name) == 0) - { - if ((*region_to_modify & classes[i].region_flag) || - (*state_to_modify & classes[i].state_flag)) - { - if (classes == nth_child_classes) - gtk_css_provider_error (scanner->provider, - scanner, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_SYNTAX, - "Duplicate pseudo-class 'nth-child(%s)'", name); - else - gtk_css_provider_error (scanner->provider, - scanner, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_SYNTAX, - "Duplicate pseudo-class '%s'", name); - } - *region_to_modify |= classes[i].region_flag; - *state_to_modify |= classes[i].state_flag; +exit: + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_KEYFRAMES); - g_free (name); - return TRUE; - } - } - - if (classes == nth_child_classes) - gtk_css_provider_error (scanner->provider, - scanner, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE, - "Unknown pseudo-class 'nth-child(%s)'", name); - else - gtk_css_provider_error (scanner->provider, - scanner, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE, - "Unknown pseudo-class '%s'", name); - g_free (name); - return FALSE; -} - -static gboolean -parse_simple_selector (GtkCssScanner *scanner, - char **name, - GArray *ids, - GArray *classes, - GtkRegionFlags *pseudo_classes, - GtkStateFlags *state) -{ - gboolean parsed_something; - - *name = _gtk_css_parser_try_ident (scanner->parser, FALSE); - if (*name) - parsed_something = TRUE; - else - parsed_something = _gtk_css_parser_try (scanner->parser, "*", FALSE); - - do { - if (_gtk_css_parser_try (scanner->parser, "#", FALSE)) - { - if (!parse_selector_name (scanner, ids)) - return FALSE; - } - else if (_gtk_css_parser_try (scanner->parser, ".", FALSE)) - { - if (!parse_selector_class (scanner, classes)) - return FALSE; - } - else if (_gtk_css_parser_try (scanner->parser, ":", FALSE)) - { - if (!parse_selector_pseudo_class (scanner, pseudo_classes, state)) - return FALSE; - } - else if (!parsed_something) - { - gtk_css_provider_error_literal (scanner->provider, - scanner, - GTK_CSS_PROVIDER_ERROR, - GTK_CSS_PROVIDER_ERROR_SYNTAX, - "Expected a valid selector"); - return FALSE; - } - else - break; - - parsed_something = TRUE; - } - while (!_gtk_css_parser_is_eof (scanner->parser)); - - _gtk_css_parser_skip_whitespace (scanner->parser); return TRUE; } -static GtkCssSelector * -parse_selector (GtkCssScanner *scanner) +static void +parse_at_keyword (GtkCssScanner *scanner) { - GtkCssSelector *selector = NULL; - - do { - char *name = NULL; - GArray *ids = g_array_new (TRUE, FALSE, sizeof (GQuark)); - GArray *classes = g_array_new (TRUE, FALSE, sizeof (GQuark)); - GtkRegionFlags pseudo_classes = 0; - GtkStateFlags state = 0; - GtkCssCombinator combine = GTK_CSS_COMBINE_DESCANDANT; - - if (selector) - { - if (_gtk_css_parser_try (scanner->parser, ">", TRUE)) - combine = GTK_CSS_COMBINE_CHILD; - } - - if (!parse_simple_selector (scanner, &name, ids, classes, &pseudo_classes, &state)) - { - g_array_free (ids, TRUE); - g_array_free (classes, TRUE); - if (selector) - _gtk_css_selector_free (selector); - return NULL; - } + if (parse_import (scanner)) + return; + if (parse_color_definition (scanner)) + return; + if (parse_binding_set (scanner)) + return; + if (parse_keyframes (scanner)) + return; - selector = _gtk_css_selector_new (selector, - combine, - name, - (GQuark *) g_array_free (ids, ids->len == 0), - (GQuark *) g_array_free (classes, classes->len == 0), - pseudo_classes, - state); - g_free (name); + else + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "unknown @ rule"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); } - while (!_gtk_css_parser_is_eof (scanner->parser) && - !_gtk_css_parser_begins_with (scanner->parser, ',') && - !_gtk_css_parser_begins_with (scanner->parser, '{')); - - return selector; } static GSList * @@ -2321,7 +2158,7 @@ parse_selector_list (GtkCssScanner *scanner) gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_SELECTOR); do { - GtkCssSelector *select = parse_selector (scanner); + GtkCssSelector *select = _gtk_css_selector_parse (scanner->parser); if (select == NULL) { @@ -2379,47 +2216,64 @@ parse_declaration (GtkCssScanner *scanner, if (property) { - PropertyValue *val; + GtkCssValue *value; g_free (name); gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_VALUE); - val = property_value_new (scanner->section); + value = _gtk_style_property_parse_value (property, + scanner->parser); - if (_gtk_style_property_parse_value (property, - &val->value, - scanner->parser, - gtk_css_scanner_get_base_url (scanner))) + 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)) - { - gtk_css_ruleset_add (ruleset, property, val); - } - 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, '}'); - property_value_free (val); - 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)) { - property_value_free (val); + 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] == '-') @@ -2431,11 +2285,10 @@ parse_declaration (GtkCssScanner *scanner, value_str = _gtk_css_parser_read_value (scanner->parser); if (value_str) { - PropertyValue *val; + WidgetPropertyValue *val; - val = property_value_new (scanner->section); - g_value_init (&val->value, G_TYPE_STRING); - g_value_take_string (&val->value, value_str); + val = widget_property_value_new (name, scanner->section); + val->value = value_str; gtk_css_ruleset_add_style (ruleset, name, val); } @@ -2572,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; } @@ -2586,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 @@ -2684,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, @@ -2715,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; } @@ -2727,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; } /** @@ -2751,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, @@ -2773,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); @@ -2792,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; } /** @@ -2838,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); @@ -2925,107 +2802,164 @@ 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); - if (resource != NULL) - g_resources_register (resource); + if (resource != NULL) + g_resources_register (resource); - 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); - } + gtk_css_provider_load_from_path (provider, path, NULL); - g_free (path); - g_free (dir); + /* Only set this after load, as load_from_path will clear it */ + provider->priv->resource = resource; + + 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; } static int -compare_properties (gconstpointer a, gconstpointer b) +compare_properties (gconstpointer a, gconstpointer b, gpointer style) { - return strcmp (_gtk_style_property_get_name ((GtkStyleProperty *) a), - _gtk_style_property_get_name ((GtkStyleProperty *) b)); + const guint *ua = a; + const guint *ub = b; + PropertyValue *styles = style; + + return strcmp (_gtk_style_property_get_name (GTK_STYLE_PROPERTY (styles[*ua].property)), + _gtk_style_property_get_name (GTK_STYLE_PROPERTY (styles[*ub].property))); +} + +static int +compare_names (gconstpointer a, gconstpointer b) +{ + const WidgetPropertyValue *aa = a; + const WidgetPropertyValue *bb = b; + return strcmp (aa->name, bb->name); } static void gtk_css_ruleset_print (const GtkCssRuleset *ruleset, GString *str) { - GList *keys, *walk; + GList *values, *walk; + 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"); - if (ruleset->style) + if (ruleset->styles) { - keys = g_hash_table_get_keys (ruleset->style); + guint *sorted = g_new (guint, ruleset->n_styles); + + for (i = 0; i < ruleset->n_styles; i++) + sorted[i] = i; + /* so the output is identical for identical selector styles */ - keys = g_list_sort (keys, compare_properties); + g_qsort_with_data (sorted, ruleset->n_styles, sizeof (guint), compare_properties, ruleset->styles); - for (walk = keys; walk; walk = walk->next) + for (i = 0; i < ruleset->n_styles; i++) { - GtkCssStyleProperty *prop = walk->data; - const PropertyValue *value = g_hash_table_lookup (ruleset->style, prop); - + PropertyValue *prop = &ruleset->styles[sorted[i]]; g_string_append (str, " "); - g_string_append (str, _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop))); + 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, &value->value, str); + _gtk_css_value_print (prop->value, str); g_string_append (str, ";\n"); } - g_list_free (keys); + g_free (sorted); } if (ruleset->widget_style) { - keys = g_hash_table_get_keys (ruleset->widget_style); + values = NULL; + for (widget_value = ruleset->widget_style; widget_value != NULL; widget_value = widget_value->next) + values = g_list_prepend (values, widget_value); + /* so the output is identical for identical selector styles */ - keys = g_list_sort (keys, (GCompareFunc) strcmp); + values = g_list_sort (values, compare_names); - for (walk = keys; walk; walk = walk->next) + for (walk = values; walk; walk = walk->next) { - const char *name = walk->data; - const PropertyValue *value = g_hash_table_lookup (ruleset->widget_style, (gpointer) name); + widget_value = walk->data; g_string_append (str, " "); - g_string_append (str, name); + g_string_append (str, widget_value->name); g_string_append (str, ": "); - g_string_append (str, g_value_get_string (&value->value)); + g_string_append (str, widget_value->value); g_string_append (str, ";\n"); } - g_list_free (keys); + g_list_free (values); } g_string_append (str, "}\n"); @@ -3036,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 */ @@ -3045,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 @@ -3089,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); }