From f9788eb173e59df9671551b2ac727f5a4729fc17 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 8 Oct 2010 18:36:46 +0200 Subject: [PATCH] GtkCssProvider: Add support for @import rules Now other CSS files can be referenced from the currently parsed file: @import url (other-file.css); @import url (/some/file.css); --- gtk/gtkcssprovider.c | 173 +++++++++++++++++++++++++++++++------------ 1 file changed, 127 insertions(+), 46 deletions(-) diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c index 471a8b3ef1..598b1ffbee 100644 --- a/gtk/gtkcssprovider.c +++ b/gtk/gtkcssprovider.c @@ -125,11 +125,16 @@ enum ParserSymbol { static void gtk_css_provider_finalize (GObject *object); static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface); -static void css_provider_apply_scope (GtkCssProvider *css_provider, - ParserScope scope); +static void scanner_apply_scope (GScanner *scanner, + ParserScope scope); static gboolean css_provider_parse_value (GtkCssProvider *css_provider, const gchar *value_str, GValue *value); +static gboolean gtk_css_provider_load_from_path_internal (GtkCssProvider *css_provider, + const gchar *path, + gboolean reset, + GError **error); + G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER, @@ -317,18 +322,11 @@ selector_style_info_set_style (SelectorStyleInfo *info, info->style = NULL; } -static void -gtk_css_provider_init (GtkCssProvider *css_provider) +static GScanner * +create_scanner (void) { - GtkCssProviderPrivate *priv; GScanner *scanner; - priv = css_provider->priv = G_TYPE_INSTANCE_GET_PRIVATE (css_provider, - GTK_TYPE_CSS_PROVIDER, - GtkCssProviderPrivate); - - priv->selectors_info = g_ptr_array_new_with_free_func ((GDestroyNotify) selector_style_info_free); - scanner = g_scanner_new (NULL); g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "active", GUINT_TO_POINTER (GTK_STATE_ACTIVE)); @@ -346,8 +344,22 @@ gtk_css_provider_init (GtkCssProvider *css_provider) g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "first", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_FIRST)); g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "last", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_LAST)); - priv->scanner = scanner; - css_provider_apply_scope (css_provider, SCOPE_SELECTOR); + scanner_apply_scope (scanner, SCOPE_SELECTOR); + + return scanner; +} + +static void +gtk_css_provider_init (GtkCssProvider *css_provider) +{ + GtkCssProviderPrivate *priv; + + priv = css_provider->priv = G_TYPE_INSTANCE_GET_PRIVATE (css_provider, + GTK_TYPE_CSS_PROVIDER, + GtkCssProviderPrivate); + + priv->selectors_info = g_ptr_array_new_with_free_func ((GDestroyNotify) selector_style_info_free); + priv->scanner = create_scanner (); priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, @@ -756,40 +768,36 @@ property_value_free (GValue *value) } static void -css_provider_apply_scope (GtkCssProvider *css_provider, - ParserScope scope) +scanner_apply_scope (GScanner *scanner, + ParserScope scope) { - GtkCssProviderPrivate *priv; - - priv = css_provider->priv; - - g_scanner_set_scope (priv->scanner, scope); + g_scanner_set_scope (scanner, scope); if (scope == SCOPE_VALUE) { - priv->scanner->config->cset_identifier_first = G_CSET_a_2_z "@#-_0123456789" G_CSET_A_2_Z; - priv->scanner->config->cset_identifier_nth = G_CSET_a_2_z "@#-_ 0123456789(),.\n" G_CSET_A_2_Z; - priv->scanner->config->scan_identifier_1char = TRUE; + scanner->config->cset_identifier_first = G_CSET_a_2_z "@#-_0123456789" G_CSET_A_2_Z; + scanner->config->cset_identifier_nth = G_CSET_a_2_z "@#-_ 0123456789(),.\n" G_CSET_A_2_Z; + scanner->config->scan_identifier_1char = TRUE; } else if (scope == SCOPE_SELECTOR) { - priv->scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "*@"; - priv->scanner->config->cset_identifier_nth = G_CSET_a_2_z "-_#" G_CSET_A_2_Z; - priv->scanner->config->scan_identifier_1char = TRUE; + scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "*@"; + scanner->config->cset_identifier_nth = G_CSET_a_2_z "-_#" G_CSET_A_2_Z; + scanner->config->scan_identifier_1char = TRUE; } else if (scope == SCOPE_PSEUDO_CLASS || scope == SCOPE_NTH_CHILD || scope == SCOPE_DECLARATION) { - priv->scanner->config->cset_identifier_first = G_CSET_a_2_z "-" G_CSET_A_2_Z; - priv->scanner->config->cset_identifier_nth = G_CSET_a_2_z "-" G_CSET_A_2_Z; - priv->scanner->config->scan_identifier_1char = FALSE; + scanner->config->cset_identifier_first = G_CSET_a_2_z "-" G_CSET_A_2_Z; + scanner->config->cset_identifier_nth = G_CSET_a_2_z "-" G_CSET_A_2_Z; + scanner->config->scan_identifier_1char = FALSE; } else g_assert_not_reached (); - priv->scanner->config->scan_float = FALSE; - priv->scanner->config->cpair_comment_single = NULL; + scanner->config->scan_float = FALSE; + scanner->config->cpair_comment_single = NULL; } static void @@ -801,7 +809,7 @@ css_provider_push_scope (GtkCssProvider *css_provider, priv = css_provider->priv; priv->state = g_slist_prepend (priv->state, GUINT_TO_POINTER (scope)); - css_provider_apply_scope (css_provider, scope); + scanner_apply_scope (priv->scanner, scope); } static ParserScope @@ -815,7 +823,7 @@ css_provider_pop_scope (GtkCssProvider *css_provider) if (!priv->state) { g_warning ("Push/pop calls to parser scope aren't paired"); - css_provider_apply_scope (css_provider, SCOPE_SELECTOR); + scanner_apply_scope (priv->scanner, SCOPE_SELECTOR); return SCOPE_SELECTOR; } @@ -825,7 +833,7 @@ css_provider_pop_scope (GtkCssProvider *css_provider) if (priv->state) scope = GPOINTER_TO_INT (priv->state->data); - css_provider_apply_scope (css_provider, scope); + scanner_apply_scope (priv->scanner, scope); return scope; } @@ -840,7 +848,7 @@ css_provider_reset_parser (GtkCssProvider *css_provider) g_slist_free (priv->state); priv->state = NULL; - css_provider_apply_scope (css_provider, SCOPE_SELECTOR); + scanner_apply_scope (priv->scanner, SCOPE_SELECTOR); g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL); g_slist_free (priv->cur_selectors); @@ -1979,6 +1987,64 @@ parse_rule (GtkCssProvider *css_provider, return G_TOKEN_NONE; } + else if (strcmp (directive, "import") == 0) + { + GScanner *scanner_backup; + GSList *state_backup; + GError *error = NULL; + gboolean loaded; + gchar *path; + + css_provider_push_scope (css_provider, SCOPE_VALUE); + g_scanner_get_next_token (scanner); + + if (scanner->token != G_TOKEN_IDENTIFIER) + return G_TOKEN_IDENTIFIER; + + path = path_parse (css_provider, + g_strstrip (scanner->value.v_identifier)); + + if (!path) + return G_TOKEN_IDENTIFIER; + + css_provider_pop_scope (css_provider); + g_scanner_get_next_token (scanner); + + if (scanner->token != ';') + { + g_free (path); + return ';'; + } + + /* Snapshot current parser state and scanner in order to restore after importing */ + state_backup = priv->state; + scanner_backup = priv->scanner; + + priv->state = NULL; + priv->scanner = create_scanner (); + + /* FIXME: Avoid recursive importing */ + loaded = gtk_css_provider_load_from_path_internal (css_provider, path, + FALSE, &error); + + /* Restore previous state */ + css_provider_reset_parser (css_provider); + priv->state = state_backup; + g_scanner_destroy (priv->scanner); + priv->scanner = scanner_backup; + + g_free (path); + + if (!loaded) + { + g_warning ("Error loading imported file \"%s\": %s", + path, (error) ? error->message : ""); + g_error_free (error); + return G_TOKEN_IDENTIFIER; + } + else + return G_TOKEN_NONE; + } else return G_TOKEN_IDENTIFIER; } @@ -2209,10 +2275,11 @@ gtk_css_provider_load_from_file (GtkCssProvider *css_provider, return TRUE; } -gboolean -gtk_css_provider_load_from_path (GtkCssProvider *css_provider, - const gchar *path, - GError **error) +static gboolean +gtk_css_provider_load_from_path_internal (GtkCssProvider *css_provider, + const gchar *path, + gboolean reset, + GError **error) { GtkCssProviderPrivate *priv; GError *internal_error = NULL; @@ -2220,9 +2287,6 @@ gtk_css_provider_load_from_path (GtkCssProvider *css_provider, const gchar *data; gsize length; - g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE); - g_return_val_if_fail (path != NULL, FALSE); - priv = css_provider->priv; mapped_file = g_mapped_file_new (path, FALSE, &internal_error); @@ -2240,11 +2304,14 @@ gtk_css_provider_load_from_path (GtkCssProvider *css_provider, if (!data) return FALSE; - if (priv->selectors_info->len > 0) - g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len); + if (reset) + { + if (priv->selectors_info->len > 0) + g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len); - g_free (priv->filename); - priv->filename = g_strdup (path); + g_free (priv->filename); + priv->filename = g_strdup (path); + } priv->scanner->input_name = priv->filename; g_scanner_input_text (priv->scanner, data, (guint) length); @@ -2256,6 +2323,20 @@ gtk_css_provider_load_from_path (GtkCssProvider *css_provider, return TRUE; } +gboolean +gtk_css_provider_load_from_path (GtkCssProvider *css_provider, + const gchar *path, + GError **error) +{ + GtkCssProviderPrivate *priv; + + g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + return gtk_css_provider_load_from_path_internal (css_provider, path, + TRUE, error); +} + GtkCssProvider * gtk_css_provider_get_default (void) {