1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
25 #include <gtkstyleprovider.h>
27 #include "gtkcssprovider.h"
31 typedef struct GtkCssProviderPrivate GtkCssProviderPrivate;
32 typedef struct SelectorElement SelectorElement;
33 typedef struct SelectorPath SelectorPath;
34 typedef struct SelectorStyleInfo SelectorStyleInfo;
35 typedef enum SelectorElementType SelectorElementType;
36 typedef enum CombinatorType CombinatorType;
37 typedef enum ParserScope ParserScope;
38 typedef enum ParserSymbol ParserSymbol;
40 enum SelectorElementType {
49 COMBINATOR_DESCENDANT, /* No direct relation needed */
50 COMBINATOR_CHILD /* Direct child */
53 struct SelectorElement
55 SelectorElementType elem_type;
56 CombinatorType combinator;
66 GtkChildClassFlags flags;
78 struct SelectorStyleInfo
84 struct GtkCssProviderPrivate
89 GHashTable *symbolic_colors;
91 GPtrArray *selectors_info;
93 /* Current parser state */
95 GSList *cur_selectors;
96 GHashTable *cur_properties;
107 /* Extend GtkStateType, since these
108 * values are also used as symbols
111 /* Scope: pseudo-class */
112 SYMBOL_NTH_CHILD = GTK_STATE_LAST,
116 /* Scope: nth-child */
117 SYMBOL_NTH_CHILD_EVEN,
118 SYMBOL_NTH_CHILD_ODD,
119 SYMBOL_NTH_CHILD_FIRST,
120 SYMBOL_NTH_CHILD_LAST
123 #define GTK_CSS_PROVIDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_CSS_PROVIDER, GtkCssProviderPrivate))
125 static void gtk_css_provider_finalize (GObject *object);
126 static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
128 static void css_provider_apply_scope (GtkCssProvider *css_provider,
130 static gboolean css_provider_parse_value (const gchar *value_str,
133 G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0,
134 G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
135 gtk_css_style_provider_iface_init));
138 gtk_css_provider_class_init (GtkCssProviderClass *klass)
140 GObjectClass *object_class = G_OBJECT_CLASS (klass);
142 object_class->finalize = gtk_css_provider_finalize;
144 g_type_class_add_private (object_class, sizeof (GtkCssProviderPrivate));
147 static SelectorPath *
148 selector_path_new (void)
152 path = g_slice_new0 (SelectorPath);
153 path->state = GTK_STATE_NORMAL;
159 static SelectorPath *
160 selector_path_ref (SelectorPath *path)
167 selector_path_unref (SelectorPath *path)
171 if (path->ref_count > 0)
174 while (path->elements)
176 g_slice_free (SelectorElement, path->elements->data);
177 path->elements = g_slist_delete_link (path->elements, path->elements);
180 g_slice_free (SelectorPath, path);
184 selector_path_prepend_type (SelectorPath *path,
185 const gchar *type_name)
187 SelectorElement *elem;
190 elem = g_slice_new (SelectorElement);
191 elem->combinator = COMBINATOR_DESCENDANT;
192 type = g_type_from_name (type_name);
194 if (type == G_TYPE_INVALID)
196 elem->elem_type = SELECTOR_TYPE_NAME;
197 elem->name = g_quark_from_string (type_name);
201 elem->elem_type = SELECTOR_GTYPE;
205 path->elements = g_slist_prepend (path->elements, elem);
209 selector_path_prepend_glob (SelectorPath *path)
211 SelectorElement *elem;
213 elem = g_slice_new (SelectorElement);
214 elem->elem_type = SELECTOR_GLOB;
215 elem->combinator = COMBINATOR_DESCENDANT;
217 path->elements = g_slist_prepend (path->elements, elem);
221 selector_path_prepend_region (SelectorPath *path,
223 GtkChildClassFlags flags)
225 SelectorElement *elem;
227 elem = g_slice_new (SelectorElement);
228 elem->combinator = COMBINATOR_DESCENDANT;
229 elem->elem_type = SELECTOR_REGION;
231 elem->region.name = g_quark_from_string (name);
232 elem->region.flags = flags;
234 path->elements = g_slist_prepend (path->elements, elem);
238 selector_path_prepend_combinator (SelectorPath *path,
239 CombinatorType combinator)
241 SelectorElement *elem;
243 g_assert (path->elements != NULL);
245 /* It is actually stored in the last element */
246 elem = path->elements->data;
247 elem->combinator = combinator;
251 selector_path_depth (SelectorPath *path)
253 return g_slist_length (path->elements);
256 static SelectorStyleInfo *
257 selector_style_info_new (SelectorPath *path)
259 SelectorStyleInfo *info;
261 info = g_slice_new0 (SelectorStyleInfo);
262 info->path = selector_path_ref (path);
268 selector_style_info_free (SelectorStyleInfo *info)
271 g_hash_table_unref (info->style);
274 selector_path_unref (info->path);
278 selector_style_info_set_style (SelectorStyleInfo *info,
282 g_hash_table_unref (info->style);
285 info->style = g_hash_table_ref (style);
291 gtk_css_provider_init (GtkCssProvider *css_provider)
293 GtkCssProviderPrivate *priv;
296 priv = GTK_CSS_PROVIDER_GET_PRIVATE (css_provider);
297 priv->selectors_info = g_ptr_array_new_with_free_func ((GDestroyNotify) selector_style_info_free);
299 scanner = g_scanner_new (NULL);
301 g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "active", GUINT_TO_POINTER (GTK_STATE_ACTIVE));
302 g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "prelight", GUINT_TO_POINTER (GTK_STATE_PRELIGHT));
303 g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "hover", GUINT_TO_POINTER (GTK_STATE_PRELIGHT));
304 g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "selected", GUINT_TO_POINTER (GTK_STATE_SELECTED));
305 g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "insensitive", GUINT_TO_POINTER (GTK_STATE_INSENSITIVE));
307 g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "nth-child", GUINT_TO_POINTER (SYMBOL_NTH_CHILD));
308 g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "first-child", GUINT_TO_POINTER (SYMBOL_FIRST_CHILD));
309 g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "last-child", GUINT_TO_POINTER (SYMBOL_LAST_CHILD));
311 g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "even", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_EVEN));
312 g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "odd", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_ODD));
313 g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "first", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_FIRST));
314 g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "last", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_LAST));
316 priv->scanner = scanner;
317 css_provider_apply_scope (css_provider, SCOPE_SELECTOR);
319 priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal,
320 (GDestroyNotify) g_free,
321 (GDestroyNotify) gtk_symbolic_color_unref);
324 typedef struct ComparePathData ComparePathData;
326 struct ComparePathData
334 compare_selector_element (GtkWidgetPath *path,
336 SelectorElement *elem,
341 if (elem->elem_type == SELECTOR_TYPE_NAME)
343 const gchar *type_name;
346 /* Resolve the type name */
347 type_name = g_quark_to_string (elem->name);
348 resolved_type = g_type_from_name (type_name);
350 if (resolved_type == G_TYPE_INVALID)
352 /* Type couldn't be resolved, so the selector
353 * clearly doesn't affect the given widget path
358 elem->elem_type = SELECTOR_GTYPE;
359 elem->type = resolved_type;
362 if (elem->elem_type == SELECTOR_GTYPE)
366 type = gtk_widget_path_get_element_type (path, index);
368 if (!g_type_is_a (type, elem->type))
371 if (type == elem->type)
379 while ((parent = g_type_parent (parent)) != G_TYPE_INVALID)
381 if (parent == elem->type)
388 g_warning ("Hierarchy is higher than expected.");
396 else if (elem->elem_type == SELECTOR_REGION)
398 const gchar *region_name;
399 GtkChildClassFlags flags;
401 /* FIXME: Need GQuark API here */
402 region_name = g_quark_to_string (elem->region.name);
404 if (!gtk_widget_path_iter_has_region (path, index, region_name, &flags))
407 if (elem->region.flags != 0 &&
408 (flags & elem->region.flags) == 0)
414 else if (elem->elem_type == SELECTOR_GLOB)
416 /* Treat as lowest matching type */
425 compare_selector (GtkWidgetPath *path,
426 SelectorPath *selector)
428 GSList *elements = selector->elements;
429 gboolean match = TRUE;
433 while (elements && match &&
434 i < gtk_widget_path_length (path))
436 SelectorElement *elem;
439 elem = elements->data;
441 match = compare_selector_element (path, i, elem, &elem_score);
444 if (!match && elem->combinator == COMBINATOR_DESCENDANT)
446 /* With descendant combinators there may
447 * be intermediate chidren in the hierarchy
452 elements = elements->next;
456 /* Only 4 bits are actually used */
462 /* If there are pending selector
463 * elements to compare, it's not
475 typedef struct StylePriorityInfo StylePriorityInfo;
477 struct StylePriorityInfo
485 css_provider_get_selectors (GtkCssProvider *css_provider,
488 GtkCssProviderPrivate *priv;
489 GArray *priority_info;
492 priv = GTK_CSS_PROVIDER_GET_PRIVATE (css_provider);
493 priority_info = g_array_new (FALSE, FALSE, sizeof (StylePriorityInfo));
495 for (i = 0; i < priv->selectors_info->len; i++)
497 SelectorStyleInfo *info;
498 StylePriorityInfo new;
499 gboolean added = FALSE;
502 info = g_ptr_array_index (priv->selectors_info, i);
503 score = compare_selector (path, info->path);
509 new.style = info->style;
510 new.state = info->path->state;
512 for (j = 0; j < priority_info->len; j++)
514 StylePriorityInfo *cur;
516 cur = &g_array_index (priority_info, StylePriorityInfo, j);
518 if (cur->score > new.score)
520 g_array_insert_val (priority_info, j, new);
527 g_array_append_val (priority_info, new);
530 return priority_info;
534 css_provider_dump_symbolic_colors (GtkCssProvider *css_provider,
537 GtkCssProviderPrivate *priv;
541 priv = GTK_CSS_PROVIDER_GET_PRIVATE (css_provider);
542 g_hash_table_iter_init (&iter, priv->symbolic_colors);
544 while (g_hash_table_iter_next (&iter, &key, &value))
547 GtkSymbolicColor *color;
552 gtk_style_set_map_color (set, name, color);
557 gtk_css_provider_get_style (GtkStyleProvider *provider,
560 GtkCssProviderPrivate *priv;
562 GArray *priority_info;
565 priv = GTK_CSS_PROVIDER_GET_PRIVATE (provider);
566 set = gtk_style_set_new ();
568 css_provider_dump_symbolic_colors ((GtkCssProvider *) provider, set);
569 priority_info = css_provider_get_selectors (GTK_CSS_PROVIDER (provider), path);
571 for (i = 0; i < priority_info->len; i++)
573 StylePriorityInfo *info;
577 info = &g_array_index (priority_info, StylePriorityInfo, i);
578 g_hash_table_iter_init (&iter, info->style);
580 while (g_hash_table_iter_next (&iter, &key, &value))
587 if (info->state == GTK_STATE_NORMAL)
588 gtk_style_set_set_default (set, key, value);
590 gtk_style_set_set_property (set, key, info->state, value);
594 g_array_free (priority_info, TRUE);
600 gtk_css_provider_get_style_property (GtkStyleProvider *provider,
602 const gchar *property_name,
605 GArray *priority_info;
606 gboolean found = FALSE;
611 path_type = gtk_widget_path_get_widget_type (path);
613 prop_name = g_strdup_printf ("-%s-%s",
614 g_type_name (path_type),
617 priority_info = css_provider_get_selectors (GTK_CSS_PROVIDER (provider), path);
619 for (i = priority_info->len - 1; i >= 0; i--)
621 StylePriorityInfo *info;
624 info = &g_array_index (priority_info, StylePriorityInfo, i);
625 val = g_hash_table_lookup (info->style, prop_name);
629 const gchar *val_str;
631 val_str = g_value_get_string (val);
634 css_provider_parse_value (val_str, value);
639 g_array_free (priority_info, TRUE);
646 gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface)
648 iface->get_style = gtk_css_provider_get_style;
649 iface->get_style_property = gtk_css_provider_get_style_property;
653 gtk_css_provider_finalize (GObject *object)
655 GtkCssProviderPrivate *priv;
657 priv = GTK_CSS_PROVIDER_GET_PRIVATE (object);
659 g_scanner_destroy (priv->scanner);
660 g_free (priv->filename);
662 g_ptr_array_free (priv->selectors_info, TRUE);
664 g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL);
665 g_slist_free (priv->cur_selectors);
667 g_hash_table_unref (priv->cur_properties);
668 g_hash_table_destroy (priv->symbolic_colors);
670 G_OBJECT_CLASS (gtk_css_provider_parent_class)->finalize (object);
674 gtk_css_provider_new (void)
676 return g_object_new (GTK_TYPE_CSS_PROVIDER, NULL);
680 property_value_free (GValue *value)
682 if (G_IS_VALUE (value))
683 g_value_unset (value);
685 g_slice_free (GValue, value);
689 css_provider_apply_scope (GtkCssProvider *css_provider,
692 GtkCssProviderPrivate *priv;
694 priv = GTK_CSS_PROVIDER_GET_PRIVATE (css_provider);
696 g_scanner_set_scope (priv->scanner, scope);
698 if (scope == SCOPE_VALUE)
700 priv->scanner->config->cset_identifier_first = G_CSET_a_2_z "@#-_0123456789" G_CSET_A_2_Z;
701 priv->scanner->config->cset_identifier_nth = G_CSET_a_2_z "@#-_ 0123456789(),." G_CSET_A_2_Z;
702 priv->scanner->config->scan_identifier_1char = TRUE;
704 else if (scope == SCOPE_SELECTOR)
706 priv->scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "*@";
707 priv->scanner->config->cset_identifier_nth = G_CSET_a_2_z "-" G_CSET_A_2_Z;
708 priv->scanner->config->scan_identifier_1char = TRUE;
710 else if (scope == SCOPE_PSEUDO_CLASS ||
711 scope == SCOPE_NTH_CHILD ||
712 scope == SCOPE_DECLARATION)
714 priv->scanner->config->cset_identifier_first = G_CSET_a_2_z "-" G_CSET_A_2_Z;
715 priv->scanner->config->cset_identifier_nth = G_CSET_a_2_z "-" G_CSET_A_2_Z;
716 priv->scanner->config->scan_identifier_1char = FALSE;
719 g_assert_not_reached ();
721 priv->scanner->config->scan_float = FALSE;
725 css_provider_push_scope (GtkCssProvider *css_provider,
728 GtkCssProviderPrivate *priv;
730 priv = GTK_CSS_PROVIDER_GET_PRIVATE (css_provider);
731 priv->state = g_slist_prepend (priv->state, GUINT_TO_POINTER (scope));
733 css_provider_apply_scope (css_provider, scope);
737 css_provider_pop_scope (GtkCssProvider *css_provider)
739 GtkCssProviderPrivate *priv;
740 ParserScope scope = SCOPE_SELECTOR;
742 priv = GTK_CSS_PROVIDER_GET_PRIVATE (css_provider);
746 g_warning ("Push/pop calls to parser scope aren't paired");
747 css_provider_apply_scope (css_provider, SCOPE_SELECTOR);
748 return SCOPE_SELECTOR;
751 priv->state = g_slist_delete_link (priv->state, priv->state);
753 /* Fetch new scope */
755 scope = GPOINTER_TO_INT (priv->state->data);
757 css_provider_apply_scope (css_provider, scope);
763 css_provider_reset_parser (GtkCssProvider *css_provider)
765 GtkCssProviderPrivate *priv;
767 priv = GTK_CSS_PROVIDER_GET_PRIVATE (css_provider);
769 g_slist_free (priv->state);
772 css_provider_apply_scope (css_provider, SCOPE_SELECTOR);
774 g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL);
775 g_slist_free (priv->cur_selectors);
776 priv->cur_selectors = NULL;
778 if (priv->cur_properties)
779 g_hash_table_unref (priv->cur_properties);
781 priv->cur_properties = g_hash_table_new_full (g_str_hash,
783 (GDestroyNotify) g_free,
784 (GDestroyNotify) property_value_free);
788 css_provider_commit (GtkCssProvider *css_provider)
790 GtkCssProviderPrivate *priv;
793 priv = GTK_CSS_PROVIDER_GET_PRIVATE (css_provider);
794 l = priv->cur_selectors;
798 SelectorPath *path = l->data;
799 SelectorStyleInfo *info;
801 info = selector_style_info_new (path);
802 selector_style_info_set_style (info, priv->cur_properties);
804 g_ptr_array_add (priv->selectors_info, info);
810 parse_pseudo_class (GtkCssProvider *css_provider,
812 SelectorPath *selector,
813 GtkChildClassFlags *flags)
817 css_provider_push_scope (css_provider, SCOPE_PSEUDO_CLASS);
818 g_scanner_get_next_token (scanner);
820 if (scanner->token != G_TOKEN_SYMBOL)
821 return G_TOKEN_SYMBOL;
823 symbol = GPOINTER_TO_INT (scanner->value.v_symbol);
825 if (symbol == SYMBOL_NTH_CHILD)
827 g_scanner_get_next_token (scanner);
829 if (scanner->token != G_TOKEN_LEFT_PAREN)
830 return G_TOKEN_LEFT_PAREN;
832 css_provider_push_scope (css_provider, SCOPE_NTH_CHILD);
833 g_scanner_get_next_token (scanner);
835 if (scanner->token != G_TOKEN_SYMBOL)
836 return G_TOKEN_SYMBOL;
838 symbol = GPOINTER_TO_INT (scanner->value.v_symbol);
842 case SYMBOL_NTH_CHILD_EVEN:
843 *flags = GTK_CHILD_CLASS_EVEN;
845 case SYMBOL_NTH_CHILD_ODD:
846 *flags = GTK_CHILD_CLASS_ODD;
848 case SYMBOL_NTH_CHILD_FIRST:
849 *flags = GTK_CHILD_CLASS_FIRST;
851 case SYMBOL_NTH_CHILD_LAST:
852 *flags = GTK_CHILD_CLASS_LAST;
858 g_scanner_get_next_token (scanner);
860 if (scanner->token != G_TOKEN_RIGHT_PAREN)
861 return G_TOKEN_RIGHT_PAREN;
863 css_provider_pop_scope (css_provider);
865 else if (symbol == SYMBOL_FIRST_CHILD)
866 *flags = GTK_CHILD_CLASS_FIRST;
867 else if (symbol == SYMBOL_LAST_CHILD)
868 *flags = GTK_CHILD_CLASS_LAST;
873 state = GPOINTER_TO_INT (scanner->value.v_symbol);
874 selector->state = state;
877 css_provider_pop_scope (css_provider);
882 parse_selector (GtkCssProvider *css_provider,
884 SelectorPath **selector_out)
888 path = selector_path_new ();
889 *selector_out = path;
891 if (scanner->token != ':' &&
892 scanner->token != G_TOKEN_IDENTIFIER)
893 return G_TOKEN_IDENTIFIER;
895 while (scanner->token == G_TOKEN_IDENTIFIER)
897 if (g_ascii_isupper (scanner->value.v_identifier[0]))
898 selector_path_prepend_type (path, scanner->value.v_identifier);
899 else if (g_ascii_islower (scanner->value.v_identifier[0]))
901 GtkChildClassFlags flags = 0;
904 region_name = g_strdup (scanner->value.v_identifier);
906 /* Parse nth-child type pseudo-class, and
907 * possibly a state pseudo-class after it.
909 while (path->state == GTK_STATE_NORMAL &&
910 g_scanner_peek_next_token (scanner) == ':')
914 g_scanner_get_next_token (scanner);
916 if ((token = parse_pseudo_class (css_provider, scanner, path, &flags)) != G_TOKEN_NONE)
920 selector_path_prepend_region (path, region_name, flags);
921 g_free (region_name);
923 else if (scanner->value.v_identifier[0] == '*')
924 selector_path_prepend_glob (path);
926 return G_TOKEN_IDENTIFIER;
928 g_scanner_get_next_token (scanner);
930 /* State is the last element in the selector */
931 if (path->state != GTK_STATE_NORMAL)
934 if (scanner->token == '>')
936 selector_path_prepend_combinator (path, COMBINATOR_CHILD);
937 g_scanner_get_next_token (scanner);
941 if (scanner->token == ':' &&
942 path->state == GTK_STATE_NORMAL)
944 GtkChildClassFlags flags = 0;
947 /* Add glob selector if path is empty */
948 if (selector_path_depth (path) == 0)
949 selector_path_prepend_glob (path);
951 if ((token = parse_pseudo_class (css_provider, scanner, path, &flags)) != G_TOKEN_NONE)
956 /* This means either a standalone :nth-child
957 * selector, or on a invalid element type.
959 return G_TOKEN_SYMBOL;
962 g_scanner_get_next_token (scanner);
964 else if (scanner->token == G_TOKEN_SYMBOL)
966 /* A new pseudo-class was starting, but the state was already
967 * parsed, so nothing is supposed to go after that.
969 return G_TOKEN_LEFT_CURLY;
975 #define SKIP_SPACES(s) while (s[0] == ' ') s++;
977 static GtkSymbolicColor *
978 symbolic_color_parse_str (const gchar *string,
981 GtkSymbolicColor *symbolic_color = NULL;
984 str = (gchar *) string;
994 while (g_ascii_isxdigit (*end))
997 color_str = g_strndup (str, end - str);
998 *end_ptr = (gchar *) end;
1000 if (!gdk_color_parse (color_str, &color))
1006 symbolic_color = gtk_symbolic_color_new_literal (&color);
1009 else if (str[0] == '@')
1017 while (*end == '-' || *end == '_' ||
1018 g_ascii_isalpha (*end))
1021 name = g_strndup (str, end - str);
1022 symbolic_color = gtk_symbolic_color_new_name (name);
1025 *end_ptr = (gchar *) end;
1027 else if (g_str_has_prefix (str, "shade"))
1029 GtkSymbolicColor *param_color;
1032 str += strlen ("shade");
1038 *end_ptr = (gchar *) str;
1044 param_color = symbolic_color_parse_str (str, end_ptr);
1054 gtk_symbolic_color_unref (param_color);
1055 *end_ptr = (gchar *) str;
1061 factor = g_ascii_strtod (str, end_ptr);
1065 *end_ptr = (gchar *) str;
1069 gtk_symbolic_color_unref (param_color);
1073 symbolic_color = gtk_symbolic_color_new_shade (param_color, factor);
1076 else if (g_str_has_prefix (str, "mix"))
1078 GtkSymbolicColor *color1, *color2;
1081 str += strlen ("mix");
1086 *end_ptr = (gchar *) str;
1092 color1 = symbolic_color_parse_str (str, end_ptr);
1102 gtk_symbolic_color_unref (color1);
1103 *end_ptr = (gchar *) str;
1109 color2 = symbolic_color_parse_str (str, end_ptr);
1111 if (!color2 || *end_ptr[0] != ',')
1113 gtk_symbolic_color_unref (color1);
1122 gtk_symbolic_color_unref (color1);
1123 gtk_symbolic_color_unref (color2);
1124 *end_ptr = (gchar *) str;
1130 factor = g_ascii_strtod (str, end_ptr);
1134 *end_ptr = (gchar *) str;
1138 gtk_symbolic_color_unref (color1);
1139 gtk_symbolic_color_unref (color2);
1143 symbolic_color = gtk_symbolic_color_new_mix (color1, color2, factor);
1147 return symbolic_color;
1152 static GtkSymbolicColor *
1153 symbolic_color_parse (const gchar *str)
1155 GtkSymbolicColor *color;
1158 color = symbolic_color_parse_str (str, &end);
1162 g_warning ("Error parsing symbolic color \"%s\", stopped at char %ld : '%c'",
1163 str, end - str, *end);
1167 gtk_symbolic_color_unref (color);
1176 css_provider_parse_value (const gchar *value_str,
1180 gboolean parsed = TRUE;
1182 type = G_VALUE_TYPE (value);
1184 if (type == GDK_TYPE_COLOR)
1188 if (gdk_color_parse (value_str, &color) == TRUE)
1189 g_value_set_boxed (value, &color);
1192 GtkSymbolicColor *symbolic_color;
1194 symbolic_color = symbolic_color_parse (value_str);
1196 if (!symbolic_color)
1199 g_value_unset (value);
1200 g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
1201 g_value_take_boxed (value, symbolic_color);
1204 else if (type == PANGO_TYPE_FONT_DESCRIPTION)
1206 PangoFontDescription *font_desc;
1208 font_desc = pango_font_description_from_string (value_str);
1209 g_value_take_boxed (value, font_desc);
1211 else if (type == G_TYPE_BOOLEAN)
1213 if (value_str[0] == '1' ||
1214 g_ascii_strcasecmp (value_str, "true") == 0)
1215 g_value_set_boolean (value, TRUE);
1217 g_value_set_boolean (value, FALSE);
1219 else if (type == G_TYPE_INT)
1220 g_value_set_int (value, atoi (value_str));
1221 else if (type == G_TYPE_DOUBLE)
1222 g_value_set_double (value, g_ascii_strtod (value_str, NULL));
1223 else if (type == GTK_TYPE_THEMING_ENGINE)
1225 GtkThemingEngine *engine;
1227 engine = gtk_theming_engine_load (value_str);
1228 g_value_set_object (value, engine);
1232 g_warning ("Cannot parse string '%s' for type %s", value_str, g_type_name (type));
1240 parse_rule (GtkCssProvider *css_provider,
1243 GtkCssProviderPrivate *priv;
1244 GTokenType expected_token;
1245 SelectorPath *selector;
1247 priv = GTK_CSS_PROVIDER_GET_PRIVATE (css_provider);
1249 css_provider_push_scope (css_provider, SCOPE_SELECTOR);
1251 if (scanner->token == G_TOKEN_IDENTIFIER &&
1252 scanner->value.v_identifier[0] == '@')
1254 GtkSymbolicColor *color;
1257 /* Rule is a color mapping */
1258 color_name = g_strdup (&scanner->value.v_identifier[1]);
1259 g_scanner_get_next_token (scanner);
1261 if (scanner->token != ':')
1264 css_provider_push_scope (css_provider, SCOPE_VALUE);
1265 g_scanner_get_next_token (scanner);
1267 if (scanner->token != G_TOKEN_IDENTIFIER)
1268 return G_TOKEN_IDENTIFIER;
1270 color = symbolic_color_parse (scanner->value.v_identifier);
1273 return G_TOKEN_IDENTIFIER;
1275 g_hash_table_insert (priv->symbolic_colors, color_name, color);
1277 css_provider_pop_scope (css_provider);
1278 g_scanner_get_next_token (scanner);
1280 if (scanner->token != ';')
1283 return G_TOKEN_NONE;
1286 expected_token = parse_selector (css_provider, scanner, &selector);
1288 if (expected_token != G_TOKEN_NONE)
1290 selector_path_unref (selector);
1291 return expected_token;
1294 priv->cur_selectors = g_slist_prepend (priv->cur_selectors, selector);
1296 while (scanner->token == ',')
1298 g_scanner_get_next_token (scanner);
1300 expected_token = parse_selector (css_provider, scanner, &selector);
1302 if (expected_token != G_TOKEN_NONE)
1304 selector_path_unref (selector);
1305 return expected_token;
1308 priv->cur_selectors = g_slist_prepend (priv->cur_selectors, selector);
1311 css_provider_pop_scope (css_provider);
1313 if (scanner->token != G_TOKEN_LEFT_CURLY)
1314 return G_TOKEN_LEFT_CURLY;
1316 /* Declarations parsing */
1317 css_provider_push_scope (css_provider, SCOPE_DECLARATION);
1318 g_scanner_get_next_token (scanner);
1320 while (scanner->token == G_TOKEN_IDENTIFIER)
1322 const gchar *value_str = NULL;
1326 prop = g_strdup (scanner->value.v_identifier);
1327 g_scanner_get_next_token (scanner);
1329 if (scanner->token != ':')
1335 css_provider_push_scope (css_provider, SCOPE_VALUE);
1336 g_scanner_get_next_token (scanner);
1338 if (scanner->token != G_TOKEN_IDENTIFIER)
1341 return G_TOKEN_IDENTIFIER;
1344 value_str = g_strstrip (scanner->value.v_identifier);
1346 if (prop[0] == '-' &&
1347 g_ascii_isupper (prop[1]))
1351 val = g_slice_new0 (GValue);
1352 g_value_init (val, G_TYPE_STRING);
1353 g_value_set_string (val, value_str);
1355 g_hash_table_insert (priv->cur_properties, prop, val);
1357 else if (gtk_style_set_lookup_property (prop, &prop_type))
1361 val = g_slice_new0 (GValue);
1362 g_value_init (val, prop_type);
1364 if (css_provider_parse_value (value_str, val))
1365 g_hash_table_insert (priv->cur_properties, prop, val);
1368 g_value_unset (val);
1369 g_slice_free (GValue, val);
1372 return G_TOKEN_IDENTIFIER;
1378 css_provider_pop_scope (css_provider);
1379 g_scanner_get_next_token (scanner);
1381 if (scanner->token != ';')
1384 g_scanner_get_next_token (scanner);
1387 if (scanner->token != G_TOKEN_RIGHT_CURLY)
1388 return G_TOKEN_RIGHT_CURLY;
1390 css_provider_pop_scope (css_provider);
1392 return G_TOKEN_NONE;
1396 parse_stylesheet (GtkCssProvider *css_provider)
1398 GtkCssProviderPrivate *priv;
1400 priv = GTK_CSS_PROVIDER_GET_PRIVATE (css_provider);
1401 g_scanner_get_next_token (priv->scanner);
1403 while (!g_scanner_eof (priv->scanner))
1405 GTokenType expected_token;
1407 css_provider_reset_parser (css_provider);
1408 expected_token = parse_rule (css_provider, priv->scanner);
1410 if (expected_token != G_TOKEN_NONE)
1412 g_scanner_unexp_token (priv->scanner, expected_token,
1414 "Error parsing style resource", FALSE);
1416 while (!g_scanner_eof (priv->scanner) &&
1417 priv->scanner->token != G_TOKEN_RIGHT_CURLY)
1418 g_scanner_get_next_token (priv->scanner);
1421 css_provider_commit (css_provider);
1423 g_scanner_get_next_token (priv->scanner);
1430 gtk_css_provider_load_from_data (GtkCssProvider *css_provider,
1435 GtkCssProviderPrivate *priv;
1437 g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
1438 g_return_val_if_fail (data != NULL, FALSE);
1440 priv = GTK_CSS_PROVIDER_GET_PRIVATE (css_provider);
1443 length = strlen (data);
1445 if (priv->selectors_info->len > 0)
1446 g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
1448 css_provider_reset_parser (css_provider);
1449 priv->scanner->input_name = "-";
1450 g_scanner_input_text (priv->scanner, data, (guint) length);
1452 parse_stylesheet (css_provider);
1458 gtk_css_provider_load_from_file (GtkCssProvider *css_provider,
1462 GtkCssProviderPrivate *priv;
1463 GError *internal_error = NULL;
1467 g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
1468 g_return_val_if_fail (G_IS_FILE (file), FALSE);
1470 priv = GTK_CSS_PROVIDER_GET_PRIVATE (css_provider);
1472 if (!g_file_load_contents (file, NULL,
1474 NULL, &internal_error))
1476 g_propagate_error (error, internal_error);
1480 if (priv->selectors_info->len > 0)
1481 g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
1483 g_free (priv->filename);
1484 priv->filename = g_file_get_path (file);
1486 css_provider_reset_parser (css_provider);
1487 priv->scanner->input_name = priv->filename;
1488 g_scanner_input_text (priv->scanner, data, (guint) length);
1490 parse_stylesheet (css_provider);
1497 #define __GTK_CSS_PROVIDER_C__
1498 #include "gtkaliasdef.c"