#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"
* <row>
* <entry>text-shadow</entry>
* <entry>shadow list (see above)</entry>
- * <entry>#GtkTextShadow</entry>
+ * <entry>internal use only</entry>
* <entry><literallayout>text-shadow: 1 1 0 blue, -4 -4 red;</literallayout></entry>
* </row>
* <row>
struct GtkCssRuleset
{
GtkCssSelector *selector;
+ GtkCssSelectorTree *selector_match;
WidgetPropertyValue *widget_style;
PropertyValue *styles;
GtkBitmask *set_styles;
GtkCssParser *parser;
GtkCssSection *section;
GtkCssScanner *parent;
- GFile *file;
- GFile *base;
GSList *state;
};
GScanner *scanner;
GHashTable *symbolic_colors;
+ GHashTable *keyframes;
GArray *rulesets;
+ GtkCssSelectorTree *tree;
GResource *resource;
};
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 = "<broken file>";
- }
- else
- {
- info = NULL;
- path = "<data>";
- }
+ 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);
}
}
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)
{
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);
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,
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;
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;
+#ifdef VERIFY_TREE
+ GtkCssProviderPrivate *priv = provider->priv;
+ GtkCssRuleset *ruleset;
+ gboolean should_match;
+ int i, j;
- priv = css_provider->priv;
- g_hash_table_iter_init (&iter, priv->symbolic_colors);
-
- 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)
{
- GtkCssMatcher matcher;
- GtkCssProvider *css_provider;
- GtkCssProviderPrivate *priv;
- GtkStyleProperties *props;
- guint i, j;
+#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 ();
+ tree_rules = _gtk_css_selector_tree_match_all (provider->priv->tree, matcher);
+ verify_tree_match_results (provider, matcher, tree_rules);
- 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;
+ for (i = tree_rules->len - 1; i >= 0; i--)
+ {
+ GtkCssRuleset *ruleset;
- ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);
+ ruleset = tree_rules->pdata[i];
- if (ruleset->styles == NULL)
- continue;
+ verify_change |= _gtk_css_selector_tree_match_get_change (ruleset->selector_match);
+ }
- if (!gtk_css_ruleset_matches (ruleset, &matcher))
- continue;
+ if (change != verify_change)
+ {
+ GString *s;
- 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);
- }
- }
+ 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,
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;
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)
}
g_free (prop_name);
+ g_ptr_array_free (tree_rules, TRUE);
return found;
}
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)
{
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,
{
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;
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;
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
{
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;
-
- ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);
-
- if (ruleset->styles == NULL)
- continue;
-
- if (!gtk_css_ruleset_matches (ruleset, matcher))
- continue;
+ change = _gtk_css_selector_tree_get_change_all (priv->tree, matcher);
- change |= gtk_css_ruleset_get_change (ruleset);
- }
+ verify_tree_get_change_results (css_provider, matcher, change);
return change;
}
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;
}
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)
{
}
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
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 = "<broken file>";
- }
- else
- {
- info = NULL;
- path = "<unknown>";
- }
+ 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;
}
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
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
static gboolean
parse_color_definition (GtkCssScanner *scanner)
{
- GtkCssValue *symbolic;
+ GtkCssValue *color;
char *name;
gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
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);
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,
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;
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)
{
return;
if (parse_binding_set (scanner))
return;
+ if (parse_keyframes (scanner))
+ return;
else
{
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
* 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,
* 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,
* 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,
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);
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;
}
/**
}
/**
- * 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);
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);
}
}
+}
+
+/**
+ * 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 (key != name)
- g_free (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;
}
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");
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");
}
GString *str)
{
GList *keys, *walk;
- char *s;
keys = g_hash_table_get_keys (colors);
/* so the output is identical for identical styles */
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
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++)
{