]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssprovider.c
GtkCssProvider: Add lighter/darker color functions.
[~andy/gtk] / gtk / gtkcssprovider.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #include "config.h"
21
22 #include <string.h>
23 #include <stdlib.h>
24 #include <gtk/gtk.h>
25 #include <gtkstyleprovider.h>
26 #include <gdk-pixbuf/gdk-pixbuf.h>
27 #include <cairo-gobject.h>
28
29 #include "gtkanimationdescription.h"
30 #include "gtk9slice.h"
31 #include "gtkcssprovider.h"
32
33 /**
34  * SECTION:gtkcssprovider
35  * @Short_description: CSS-like styling for widgets
36  * @Title: GtkCssProvider
37  * @See_also: #GtkStyleContext, #GtkStyleProvider
38  *
39  * #GtkCssProvider is an object implementing #GtkStyleProvider, it is able
40  * to parse CSS-like input in order to style widgets.
41  *
42  * <refsect2 id="gtkcssprovider-selectors">
43  * <title>Widget selectors</title>
44  * <para>
45  * Selectors work in a really similar way than in common CSS, widget object
46  * names act as HTML tags:
47  * </para>
48  * <example>
49  * <title>Widgets in selectors</title>
50  * <programlisting>
51  * /&ast; Theme labels that are descendants of a window &ast;/
52  * GtkWindow GtkLabel {
53  *     background-color: &num;898989;
54  * }
55  *
56  * /&ast; Theme notebooks, and anything that's within these &ast;/
57  * GtkNotebook {
58  *     background-color: &num;a939f0;
59  * }
60  *
61  * /&ast; Theme combo boxes, and entries that
62  *  are direct children of a notebook &ast;/
63  * GtkComboBox,
64  * GtkNotebook > GtkEntry {
65  *     background-color: &num;1209a2;
66  * }
67  *
68  * /&ast; Theme any widget within a GtkBin &ast;/
69  * GtkBin * {
70  *     font-name: Sans 20;
71  * }
72  * </programlisting>
73  * </example>
74  * <para>
75  * Widget names may be matched in CSS as well:
76  * </para>
77  * <example>
78  * <title>Widget names in selectors</title>
79  * <programlisting>
80  * /&ast; Theme a label named title-label &ast;/
81  * GtkLabel&num;title-label {
82  *     font-name: Sans 15;
83  * }
84  *
85  * /&ast; Theme any widget named main-entry &ast;/
86  * &num;main-entry {
87  *     background-color: &num;f0a810;
88  * }
89  * </programlisting>
90  * </example>
91  * <para>
92  * Widgets may also define different classes, so these can be matched
93  * in CSS:
94  * </para>
95  * <example>
96  * <title>Widget names in selectors</title>
97  * <programlisting>
98  * /&ast; Theme all widgets defining the class entry &ast;/
99  * .entry {
100  *     color: &num;39f1f9;
101  * }
102  *
103  * /&ast; Theme spinbuttons' entry &ast;/
104  * GtkSpinButton.entry {
105  *     color: &num;900185;
106  * }
107  * </programlisting>
108  * </example>
109  * <para>
110  * Container widgets may define regions, these region names may be
111  * referenced in CSS, it's also possible to apply :nth-child
112  * pseudo-class information if the widget implementation provides
113  * such data.
114  * </para>
115  * <example>
116  * <title>Region names in containers</title>
117  * <programlisting>
118  * /&ast; Theme any label within a notebook &ast;/
119  * GtkNotebook GtkLabel {
120  *     color: &num;f90192;
121  * }
122  *
123  * /&ast; Theme labels within notebook tabs &ast;/
124  * GtkNotebook tab:nth-child GtkLabel {
125  *     color: &num;703910;
126  * }
127  *
128  * /&ast; Theme labels in the any first notebook
129  *  tab, both selectors are equivalent &ast;/
130  * GtkNotebook tab:nth-child(first) GtkLabel,
131  * GtkNotebook tab:first-child GtkLabel {
132  *     color: &num;89d012;
133  * }
134  * </programlisting>
135  * </example>
136  * <para>
137  * Widget states may be matched as pseudoclasses.
138  * Given the needed widget states differ from the
139  * offered pseudoclasses in CSS, some are unsupported,
140  * and some custom ones have been added.
141  * </para>
142  * <example>
143  * <title>Styling specific widget states</title>
144  * <programlisting>
145  * /&ast; Theme active (pressed) buttons &ast;/
146  * GtkButton:active {
147  *     background-color: &num;0274d9;
148  * }
149  *
150  * /&ast; Theme buttons with the mouse pointer on it &ast;/
151  * GtkButton:hover,
152  * GtkButton:prelight {
153  *     background-color: &num;3085a9;
154  * }
155  *
156  * /&ast; Theme insensitive widgets, both are equivalent &ast;/
157  * :insensitive,
158  * *:insensitive {
159  *     background-color: &num;320a91;
160  * }
161  *
162  * /&ast; Theme selection colors in entries &ast;/
163  * GtkEntry:selected {
164  *     background-color: &num;56f9a0;
165  * }
166  *
167  * /&ast; Theme focused labels &ast;/
168  * GtkLabel:focused {
169  *     background-color: &num;b4940f;
170  * }
171  *
172  * /&ast; Theme inconsistent checkbuttons &ast;/
173  * GtkCheckButton:inconsistent {
174  *     background-color: &num;20395a;
175  * }
176  * </programlisting>
177  * </example>
178  * <para>
179  * Widget state pseudoclasses may only apply to the
180  * last element in a selector.
181  * </para>
182  * <para>
183  * All the mentioned elements may be combined to create
184  * complex selectors that match specific widget paths.
185  * As in CSS, rules apply by specificity, so the selectors
186  * describing the best a widget path will take precedence
187  * over the others.
188  * </para>
189  * </refsect2>
190  * <refsect2 id="gtkcssprovider-rules">
191  * <title>&commat; rules</title>
192  * <para>
193  * GTK+'s CSS supports the &commat;import rule, in order
194  * to load another CSS file in addition to the currently
195  * parsed one.
196  * </para>
197  * <example>
198  * <title>Using the &commat;import rule</title>
199  * <programlisting>
200  * &commat;import url (path/to/common.css)
201  * </programlisting>
202  * </example>
203  * <para>
204  * GTK+ also supports an additional &commat;define-color
205  * rule, in order to define a color name which may be used
206  * instead of color numeric representations.
207  * </para>
208  * <example>
209  * <title>Defining colors</title>
210  * <programlisting>
211  * &commat;define-color bg_color &num;f9a039;
212  *
213  * &ast; {
214  *     background-color: &commat;bg_color;
215  * }
216  * </programlisting>
217  * </example>
218  * </refsect2>
219  * <refsect2 id="gtkcssprovider-symbolic-colors">
220  * <title>Symbolic colors</title>
221  * <para>
222  * Besides being able to define color names, the CSS
223  * parser is also able to read different color modifiers,
224  * which can also be nested, providing a rich language
225  * to define colors starting from other colors.
226  * </para>
227  * <example>
228  * <title>Using symbolic colors</title>
229  * <programlisting>
230  * &commat;define-color entry-color shade (&commat;bg_color, 0.7);
231  *
232  * GtkEntry {
233  *     background-color: @entry-color;
234  * }
235  *
236  * GtkEntry:focused {
237  *     background-color: mix (&commat;entry-color,
238  *                            shade (&num;fff, 0.5),
239  *                            0.8);
240  * }
241  * </programlisting>
242  * </example>
243  * </refsect2>
244  * <refsect2 id="gtkcssprovider-properties">
245  * <title>Supported properties</title>
246  * <para>
247  * Properties are the part that differ the most to common CSS,
248  * not all properties are supported (some are planned to be
249  * supported eventually, some others are meaningless or don't
250  * map intuitively in a widget based environment).
251  * </para>
252  * <para>
253  * There is also a difference in shorthand properties, for
254  * example in common CSS it is fine to define a font through
255  * the different @font-family, @font-style, @font-size
256  * properties, meanwhile in GTK+'s CSS only the canonical
257  * @font property would be supported.
258  * </para>
259  * <para>
260  * The currently supported properties are:
261  * </para>
262  * <informaltable>
263  *   <tgroup cols="4">
264  *     <thead>
265  *       <row>
266  *         <entry>Property name</entry>
267  *         <entry>Syntax</entry>
268  *         <entry>Maps to</entry>
269  *         <entry>Examples</entry>
270  *       </row>
271  *     </thead>
272  *     <tbody>
273  *       <row>
274  *         <entry>engine</entry>
275  *         <entry><programlisting>engine-name</programlisting></entry>
276  *         <entry>#GtkThemingEngine</entry>
277  *         <entry><programlisting>engine: clearlooks;</programlisting></entry>
278  *       </row>
279  *       <row>
280  *         <entry>background-color</entry>
281  *         <entry morerows="1"><programlisting>color</programlisting></entry>
282  *         <entry morerows="1">#GdkRGBA</entry>
283  *         <entry morerows="1">
284  *           <programlisting>
285  * background-color: &num;fff;
286  * color: @color-name;
287  * background-color: shade (@color-name, 0.5);
288  * color: mix (@color-name, &num;f0f, 0.8);</programlisting>
289  *         </entry>
290  *       </row>
291  *       <row>
292  *         <entry>color</entry>
293  *       </row>
294  *       <row>
295  *         <entry>font</entry>
296  *         <entry><programlisting>family [style] [size]</programlisting></entry>
297  *         <entry>#PangoFontDescription</entry>
298  *         <entry><programlisting>font: Sans 15;</programlisting></entry>
299  *       </row>
300  *       <row>
301  *         <entry>margin</entry>
302  *         <entry morerows="1">
303  *           <programlisting>
304  * width
305  * vertical-width horizontal-width
306  * top-width horizontal-width bottom-width
307  * top-width right-width bottom-width left-width
308  *           </programlisting>
309  *         </entry>
310  *         <entry morerows="1">#GtkBorder</entry>
311  *         <entry morerows="1">
312  *           <programlisting>
313  * margin: 5;
314  * margin: 5 10;
315  * margin: 5 10 3;
316  * margin: 5 10 3 5;</programlisting>
317  *         </entry>
318  *       </row>
319  *       <row>
320  *         <entry>padding</entry>
321  *       </row>
322  *       <row>
323  *         <entry>background-image</entry>
324  *         <entry>
325  *           <programlisting>
326  * -gtk-gradient (linear,
327  *                starting-x-position starting-y-position,
328  *                ending-x-position ending-y-position,
329  *                [ [from|to] (color) |
330  *                  color-stop (percentage, color) ] )
331  *
332  * -gtk-gradient (radial,
333  *                starting-x-position starting-y-position, starting-radius,
334  *                ending-x-position ending-y-position, ending-radius,
335  *                [ [from|to] (color) |
336  *                  color-stop (percentage, color) ]* )</programlisting>
337  *         </entry>
338  *         <entry>#cairo_pattern_t</entry>
339  *         <entry>
340  *           <programlisting>
341  * -gtk-gradient (linear,
342  *                top left, top right,
343  *                from (&num;fff), to (&num;000));
344  * -gtk-gradient (linear, 0.0 0.5, 0.5 1.0,
345  *                from (&num;fff),
346  *                color-stop (0.5, &num;f00),
347  *                to (&num;000));
348  * -gtk-gradient (radial,
349  *                center center, 0.2,
350  *                center center, 0.8,
351  *                color-stop (0.0, &num;fff),
352  *                color-stop (1.0, &num;000));</programlisting>
353  *         </entry>
354  *       </row>
355  *       <row>
356  *         <entry>border-image</entry>
357  *         <entry><programlisting>url([path]) top-distance right-distance bottom-distance left-distance horizontal-option vertical-option</programlisting></entry>
358  *         <entry></entry>
359  *         <entry>
360  *           <programlisting>
361  * border-image: url (/path/to/image.png) 3 4 3 4 stretch;
362  * border-image: url (/path/to/image.png) 3 4 4 3 repeat stretch;</programlisting>
363  *         </entry>
364  *       </row>
365  *       <row>
366  *         <entry>transition</entry>
367  *         <entry><programlisting>duration [s|ms] [linear|ease|ease-in|ease-out|ease-in-out]</programlisting></entry>
368  *         <entry></entry>
369  *         <entry>
370  *           <programlisting>
371  * transition: 150ms ease-in-out;
372  * transition: 1s linear;</programlisting>
373  *         </entry>
374  *       </row>
375  *     </tbody>
376  *   </tgroup>
377  * </informaltable>
378  * </refsect2>
379  */
380
381 typedef struct GtkCssProviderPrivate GtkCssProviderPrivate;
382 typedef struct SelectorElement SelectorElement;
383 typedef struct SelectorPath SelectorPath;
384 typedef struct SelectorStyleInfo SelectorStyleInfo;
385 typedef enum SelectorElementType SelectorElementType;
386 typedef enum CombinatorType CombinatorType;
387 typedef enum ParserScope ParserScope;
388 typedef enum ParserSymbol ParserSymbol;
389
390 enum SelectorElementType {
391   SELECTOR_TYPE_NAME,
392   SELECTOR_NAME,
393   SELECTOR_GTYPE,
394   SELECTOR_REGION,
395   SELECTOR_CLASS,
396   SELECTOR_GLOB
397 };
398
399 enum CombinatorType {
400   COMBINATOR_DESCENDANT, /* No direct relation needed */
401   COMBINATOR_CHILD       /* Direct child */
402 };
403
404 struct SelectorElement
405 {
406   SelectorElementType elem_type;
407   CombinatorType combinator;
408
409   union
410   {
411     GQuark name;
412     GType type;
413
414     struct
415     {
416       GQuark name;
417       GtkRegionFlags flags;
418     } region;
419   };
420 };
421
422 struct SelectorPath
423 {
424   GSList *elements;
425   GtkStateFlags state;
426   guint ref_count;
427 };
428
429 struct SelectorStyleInfo
430 {
431   SelectorPath *path;
432   GHashTable *style;
433 };
434
435 struct GtkCssProviderPrivate
436 {
437   GScanner *scanner;
438   gchar *filename;
439
440   GHashTable *symbolic_colors;
441
442   GPtrArray *selectors_info;
443
444   /* Current parser state */
445   GSList *state;
446   GSList *cur_selectors;
447   GHashTable *cur_properties;
448 };
449
450 enum ParserScope {
451   SCOPE_SELECTOR,
452   SCOPE_PSEUDO_CLASS,
453   SCOPE_NTH_CHILD,
454   SCOPE_DECLARATION,
455   SCOPE_VALUE
456 };
457
458 /* Extend GtkStateType, since these
459  * values are also used as symbols
460  */
461 enum ParserSymbol {
462   /* Scope: pseudo-class */
463   SYMBOL_NTH_CHILD = GTK_STATE_FOCUSED + 1,
464   SYMBOL_FIRST_CHILD,
465   SYMBOL_LAST_CHILD,
466   SYMBOL_SORTED_CHILD,
467
468   /* Scope: nth-child */
469   SYMBOL_NTH_CHILD_EVEN,
470   SYMBOL_NTH_CHILD_ODD,
471   SYMBOL_NTH_CHILD_FIRST,
472   SYMBOL_NTH_CHILD_LAST
473 };
474
475 static void gtk_css_provider_finalize (GObject *object);
476 static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
477
478 static void scanner_apply_scope (GScanner    *scanner,
479                                  ParserScope  scope);
480 static gboolean css_provider_parse_value (GtkCssProvider *css_provider,
481                                           const gchar    *value_str,
482                                           GValue         *value);
483 static gboolean gtk_css_provider_load_from_path_internal (GtkCssProvider  *css_provider,
484                                                           const gchar     *path,
485                                                           gboolean         reset,
486                                                           GError         **error);
487
488
489 G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0,
490                         G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
491                                                gtk_css_style_provider_iface_init));
492
493 static void
494 gtk_css_provider_class_init (GtkCssProviderClass *klass)
495 {
496   GObjectClass *object_class = G_OBJECT_CLASS (klass);
497
498   object_class->finalize = gtk_css_provider_finalize;
499
500   g_type_class_add_private (object_class, sizeof (GtkCssProviderPrivate));
501 }
502
503 static SelectorPath *
504 selector_path_new (void)
505 {
506   SelectorPath *path;
507
508   path = g_slice_new0 (SelectorPath);
509   path->ref_count = 1;
510
511   return path;
512 }
513
514 static SelectorPath *
515 selector_path_ref (SelectorPath *path)
516 {
517   path->ref_count++;
518   return path;
519 }
520
521 static void
522 selector_path_unref (SelectorPath *path)
523 {
524   path->ref_count--;
525
526   if (path->ref_count > 0)
527     return;
528
529   while (path->elements)
530     {
531       g_slice_free (SelectorElement, path->elements->data);
532       path->elements = g_slist_delete_link (path->elements, path->elements);
533     }
534
535   g_slice_free (SelectorPath, path);
536 }
537
538 static void
539 selector_path_prepend_type (SelectorPath *path,
540                             const gchar  *type_name)
541 {
542   SelectorElement *elem;
543   GType type;
544
545   elem = g_slice_new (SelectorElement);
546   elem->combinator = COMBINATOR_DESCENDANT;
547   type = g_type_from_name (type_name);
548
549   if (type == G_TYPE_INVALID)
550     {
551       elem->elem_type = SELECTOR_TYPE_NAME;
552       elem->name = g_quark_from_string (type_name);
553     }
554   else
555     {
556       elem->elem_type = SELECTOR_GTYPE;
557       elem->type = type;
558     }
559
560   path->elements = g_slist_prepend (path->elements, elem);
561 }
562
563 static void
564 selector_path_prepend_glob (SelectorPath *path)
565 {
566   SelectorElement *elem;
567
568   elem = g_slice_new (SelectorElement);
569   elem->elem_type = SELECTOR_GLOB;
570   elem->combinator = COMBINATOR_DESCENDANT;
571
572   path->elements = g_slist_prepend (path->elements, elem);
573 }
574
575 static void
576 selector_path_prepend_region (SelectorPath   *path,
577                               const gchar    *name,
578                               GtkRegionFlags  flags)
579 {
580   SelectorElement *elem;
581
582   elem = g_slice_new (SelectorElement);
583   elem->combinator = COMBINATOR_DESCENDANT;
584   elem->elem_type = SELECTOR_REGION;
585
586   elem->region.name = g_quark_from_string (name);
587   elem->region.flags = flags;
588
589   path->elements = g_slist_prepend (path->elements, elem);
590 }
591
592 static void
593 selector_path_prepend_name (SelectorPath *path,
594                             const gchar  *name)
595 {
596   SelectorElement *elem;
597
598   elem = g_slice_new (SelectorElement);
599   elem->combinator = COMBINATOR_DESCENDANT;
600   elem->elem_type = SELECTOR_NAME;
601
602   elem->name = g_quark_from_string (name);
603
604   path->elements = g_slist_prepend (path->elements, elem);
605 }
606
607 static void
608 selector_path_prepend_class (SelectorPath *path,
609                              const gchar  *name)
610 {
611   SelectorElement *elem;
612
613   elem = g_slice_new (SelectorElement);
614   elem->combinator = COMBINATOR_DESCENDANT;
615   elem->elem_type = SELECTOR_CLASS;
616
617   elem->name = g_quark_from_string (name);
618
619   path->elements = g_slist_prepend (path->elements, elem);
620 }
621
622 static void
623 selector_path_prepend_combinator (SelectorPath   *path,
624                                   CombinatorType  combinator)
625 {
626   SelectorElement *elem;
627
628   g_assert (path->elements != NULL);
629
630   /* It is actually stored in the last element */
631   elem = path->elements->data;
632   elem->combinator = combinator;
633 }
634
635 static gint
636 selector_path_depth (SelectorPath *path)
637 {
638   return g_slist_length (path->elements);
639 }
640
641 static SelectorStyleInfo *
642 selector_style_info_new (SelectorPath *path)
643 {
644   SelectorStyleInfo *info;
645
646   info = g_slice_new0 (SelectorStyleInfo);
647   info->path = selector_path_ref (path);
648
649   return info;
650 }
651
652 static void
653 selector_style_info_free (SelectorStyleInfo *info)
654 {
655   if (info->style)
656     g_hash_table_unref (info->style);
657
658   if (info->path)
659     selector_path_unref (info->path);
660 }
661
662 static void
663 selector_style_info_set_style (SelectorStyleInfo *info,
664                                GHashTable        *style)
665 {
666   if (info->style)
667     g_hash_table_unref (info->style);
668
669   if (style)
670     info->style = g_hash_table_ref (style);
671   else
672     info->style = NULL;
673 }
674
675 static GScanner *
676 create_scanner (void)
677 {
678   GScanner *scanner;
679
680   scanner = g_scanner_new (NULL);
681
682   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "active", GUINT_TO_POINTER (GTK_STATE_ACTIVE));
683   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "prelight", GUINT_TO_POINTER (GTK_STATE_PRELIGHT));
684   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "hover", GUINT_TO_POINTER (GTK_STATE_PRELIGHT));
685   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "selected", GUINT_TO_POINTER (GTK_STATE_SELECTED));
686   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "insensitive", GUINT_TO_POINTER (GTK_STATE_INSENSITIVE));
687   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "inconsistent", GUINT_TO_POINTER (GTK_STATE_INCONSISTENT));
688   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "focused", GUINT_TO_POINTER (GTK_STATE_FOCUSED));
689
690   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "nth-child", GUINT_TO_POINTER (SYMBOL_NTH_CHILD));
691   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "first-child", GUINT_TO_POINTER (SYMBOL_FIRST_CHILD));
692   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "last-child", GUINT_TO_POINTER (SYMBOL_LAST_CHILD));
693   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "sorted", GUINT_TO_POINTER (SYMBOL_SORTED_CHILD));
694
695   g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "even", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_EVEN));
696   g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "odd", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_ODD));
697   g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "first", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_FIRST));
698   g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "last", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_LAST));
699
700   scanner_apply_scope (scanner, SCOPE_SELECTOR);
701
702   return scanner;
703 }
704
705 static void
706 gtk_css_provider_init (GtkCssProvider *css_provider)
707 {
708   GtkCssProviderPrivate *priv;
709
710   priv = css_provider->priv = G_TYPE_INSTANCE_GET_PRIVATE (css_provider,
711                                                            GTK_TYPE_CSS_PROVIDER,
712                                                            GtkCssProviderPrivate);
713
714   priv->selectors_info = g_ptr_array_new_with_free_func ((GDestroyNotify) selector_style_info_free);
715   priv->scanner = create_scanner ();
716
717   priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal,
718                                                  (GDestroyNotify) g_free,
719                                                  (GDestroyNotify) gtk_symbolic_color_unref);
720 }
721
722 typedef struct ComparePathData ComparePathData;
723
724 struct ComparePathData
725 {
726   guint64 score;
727   SelectorPath *path;
728   GSList *iter;
729 };
730
731 static gboolean
732 compare_selector_element (GtkWidgetPath   *path,
733                           guint            index,
734                           SelectorElement *elem,
735                           guint8          *score)
736 {
737   *score = 0;
738
739   if (elem->elem_type == SELECTOR_TYPE_NAME)
740     {
741       const gchar *type_name;
742       GType resolved_type;
743
744       /* Resolve the type name */
745       type_name = g_quark_to_string (elem->name);
746       resolved_type = g_type_from_name (type_name);
747
748       if (resolved_type == G_TYPE_INVALID)
749         {
750           /* Type couldn't be resolved, so the selector
751            * clearly doesn't affect the given widget path
752            */
753           return FALSE;
754         }
755
756       elem->elem_type = SELECTOR_GTYPE;
757       elem->type = resolved_type;
758     }
759
760   if (elem->elem_type == SELECTOR_GTYPE)
761     {
762       GType type;
763
764       type = gtk_widget_path_iter_get_widget_type (path, index);
765
766       if (!g_type_is_a (type, elem->type))
767         return FALSE;
768
769       if (type == elem->type)
770         *score |= 0xF;
771       else
772         {
773           GType parent = type;
774
775           *score = 0xE;
776
777           while ((parent = g_type_parent (parent)) != G_TYPE_INVALID)
778             {
779               if (parent == elem->type)
780                 break;
781
782               *score -= 1;
783
784               if (*score == 1)
785                 {
786                   g_warning ("Hierarchy is higher than expected.");
787                   break;
788                 }
789             }
790         }
791
792       return TRUE;
793     }
794   else if (elem->elem_type == SELECTOR_REGION)
795     {
796       GtkRegionFlags flags;
797
798       if (!gtk_widget_path_iter_has_qregion (path, index,
799                                              elem->region.name,
800                                              &flags))
801         return FALSE;
802
803       if (elem->region.flags != 0 &&
804           (flags & elem->region.flags) == 0)
805         return FALSE;
806
807       *score = 0xF;
808       return TRUE;
809     }
810   else if (elem->elem_type == SELECTOR_GLOB)
811     {
812       /* Treat as lowest matching type */
813       *score = 1;
814       return TRUE;
815     }
816   else if (elem->elem_type == SELECTOR_NAME)
817     {
818       if (!gtk_widget_path_iter_has_qname (path, index, elem->name))
819         return FALSE;
820
821       *score = 0xF;
822       return TRUE;
823     }
824   else if (elem->elem_type == SELECTOR_CLASS)
825     {
826       if (!gtk_widget_path_iter_has_qclass (path, index, elem->name))
827         return FALSE;
828
829       *score = 0xF;
830       return TRUE;
831     }
832
833   return FALSE;
834 }
835
836 static guint64
837 compare_selector (GtkWidgetPath *path,
838                   SelectorPath  *selector)
839 {
840   GSList *elements = selector->elements;
841   gboolean match = TRUE;
842   guint64 score = 0;
843   gint i;
844
845   i = gtk_widget_path_length (path) - 1;
846
847   while (elements && match && i >= 0)
848     {
849       SelectorElement *elem;
850       guint8 elem_score;
851
852       elem = elements->data;
853
854       match = compare_selector_element (path, i, elem, &elem_score);
855
856       /* Only move on to the next index if there is no match
857        * with the current element (whether to continue or not
858        * handled right after in the combinator check), or a
859        * GType or glob has just been matched.
860        *
861        * Region and widget names do not trigger this because
862        * the next element in the selector path could also be
863        * related to the same index.
864        */
865       if (!match ||
866           (elem->elem_type == SELECTOR_GTYPE ||
867            elem->elem_type == SELECTOR_GLOB))
868         i--;
869
870       if (!match &&
871           elem->elem_type != SELECTOR_TYPE_NAME &&
872           elem->combinator == COMBINATOR_DESCENDANT)
873         {
874           /* With descendant combinators there may
875            * be intermediate chidren in the hierarchy
876            */
877           match = TRUE;
878         }
879       else if (match)
880         elements = elements->next;
881
882       if (match)
883         {
884           /* Only 4 bits are actually used */
885           score <<= 4;
886           score |= elem_score;
887         }
888     }
889
890   /* If there are pending selector
891    * elements to compare, it's not
892    * a match.
893    */
894   if (elements)
895     match = FALSE;
896
897   if (!match)
898     score = 0;
899
900   return score;
901 }
902
903 typedef struct StylePriorityInfo StylePriorityInfo;
904
905 struct StylePriorityInfo
906 {
907   guint64 score;
908   GHashTable *style;
909   GtkStateFlags state;
910 };
911
912 static GArray *
913 css_provider_get_selectors (GtkCssProvider *css_provider,
914                             GtkWidgetPath  *path)
915 {
916   GtkCssProviderPrivate *priv;
917   GArray *priority_info;
918   guint i, j;
919
920   priv = css_provider->priv;
921   priority_info = g_array_new (FALSE, FALSE, sizeof (StylePriorityInfo));
922
923   for (i = 0; i < priv->selectors_info->len; i++)
924     {
925       SelectorStyleInfo *info;
926       StylePriorityInfo new;
927       gboolean added = FALSE;
928       guint64 score;
929
930       info = g_ptr_array_index (priv->selectors_info, i);
931       score = compare_selector (path, info->path);
932
933       if (score <= 0)
934         continue;
935
936       new.score = score;
937       new.style = info->style;
938       new.state = info->path->state;
939
940       for (j = 0; j < priority_info->len; j++)
941         {
942           StylePriorityInfo *cur;
943
944           cur = &g_array_index (priority_info, StylePriorityInfo, j);
945
946           if (cur->score > new.score)
947             {
948               g_array_insert_val (priority_info, j, new);
949               added = TRUE;
950               break;
951             }
952         }
953
954       if (!added)
955         g_array_append_val (priority_info, new);
956     }
957
958   return priority_info;
959 }
960
961 static void
962 css_provider_dump_symbolic_colors (GtkCssProvider     *css_provider,
963                                    GtkStyleProperties *props)
964 {
965   GtkCssProviderPrivate *priv;
966   GHashTableIter iter;
967   gpointer key, value;
968
969   priv = css_provider->priv;
970   g_hash_table_iter_init (&iter, priv->symbolic_colors);
971
972   while (g_hash_table_iter_next (&iter, &key, &value))
973     {
974       const gchar *name;
975       GtkSymbolicColor *color;
976
977       name = key;
978       color = value;
979
980       gtk_style_properties_map_color (props, name, color);
981     }
982 }
983
984 static GtkStyleProperties *
985 gtk_css_provider_get_style (GtkStyleProvider *provider,
986                             GtkWidgetPath    *path)
987 {
988   GtkCssProvider *css_provider;
989   GtkCssProviderPrivate *priv;
990   GtkStyleProperties *props;
991   GArray *priority_info;
992   guint i;
993
994   css_provider = GTK_CSS_PROVIDER (provider);
995   props = gtk_style_properties_new ();
996   priv = css_provider->priv;
997
998   css_provider_dump_symbolic_colors (css_provider, props);
999   priority_info = css_provider_get_selectors (css_provider, path);
1000
1001   for (i = 0; i < priority_info->len; i++)
1002     {
1003       StylePriorityInfo *info;
1004       GHashTableIter iter;
1005       gpointer key, value;
1006
1007       info = &g_array_index (priority_info, StylePriorityInfo, i);
1008       g_hash_table_iter_init (&iter, info->style);
1009
1010       while (g_hash_table_iter_next (&iter, &key, &value))
1011         {
1012           gchar *prop = key;
1013
1014           /* Properties starting with '-' may be both widget style properties
1015            * or custom properties from the theming engine, so check whether
1016            * the type is registered or not.
1017            */
1018           if (prop[0] == '-' &&
1019               !gtk_style_properties_lookup_property (prop, NULL, NULL))
1020             continue;
1021
1022           gtk_style_properties_set_property (props, key, info->state, value);
1023         }
1024     }
1025
1026   g_array_free (priority_info, TRUE);
1027
1028   return props;
1029 }
1030
1031 static gboolean
1032 gtk_css_provider_get_style_property (GtkStyleProvider *provider,
1033                                      GtkWidgetPath    *path,
1034                                      const gchar      *property_name,
1035                                      GValue           *value)
1036 {
1037   GArray *priority_info;
1038   gboolean found = FALSE;
1039   gchar *prop_name;
1040   GType path_type;
1041   gint i;
1042
1043   path_type = gtk_widget_path_get_widget_type (path);
1044
1045   prop_name = g_strdup_printf ("-%s-%s",
1046                                g_type_name (path_type),
1047                                property_name);
1048
1049   priority_info = css_provider_get_selectors (GTK_CSS_PROVIDER (provider), path);
1050
1051   for (i = priority_info->len - 1; i >= 0; i--)
1052     {
1053       StylePriorityInfo *info;
1054       GValue *val;
1055
1056       info = &g_array_index (priority_info, StylePriorityInfo, i);
1057       val = g_hash_table_lookup (info->style, prop_name);
1058
1059       if (val)
1060         {
1061           const gchar *val_str;
1062
1063           val_str = g_value_get_string (val);
1064           found = TRUE;
1065
1066           css_provider_parse_value (GTK_CSS_PROVIDER (provider), val_str, value);
1067           break;
1068         }
1069     }
1070
1071   g_array_free (priority_info, TRUE);
1072   g_free (prop_name);
1073
1074   return found;
1075 }
1076
1077 static void
1078 gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface)
1079 {
1080   iface->get_style = gtk_css_provider_get_style;
1081   iface->get_style_property = gtk_css_provider_get_style_property;
1082 }
1083
1084 static void
1085 gtk_css_provider_finalize (GObject *object)
1086 {
1087   GtkCssProvider *css_provider;
1088   GtkCssProviderPrivate *priv;
1089
1090   css_provider = GTK_CSS_PROVIDER (object);
1091   priv = css_provider->priv;
1092
1093   g_scanner_destroy (priv->scanner);
1094   g_free (priv->filename);
1095
1096   g_ptr_array_free (priv->selectors_info, TRUE);
1097
1098   g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL);
1099   g_slist_free (priv->cur_selectors);
1100
1101   g_hash_table_unref (priv->cur_properties);
1102   g_hash_table_destroy (priv->symbolic_colors);
1103
1104   G_OBJECT_CLASS (gtk_css_provider_parent_class)->finalize (object);
1105 }
1106
1107 /**
1108  * gtk_css_provider_new:
1109  *
1110  * Returns a newly created #GtkCssProvider.
1111  *
1112  * Returns: A new #GtkCssProvider
1113  **/
1114 GtkCssProvider *
1115 gtk_css_provider_new (void)
1116 {
1117   return g_object_new (GTK_TYPE_CSS_PROVIDER, NULL);
1118 }
1119
1120 static void
1121 property_value_free (GValue *value)
1122 {
1123   if (G_IS_VALUE (value))
1124     g_value_unset (value);
1125
1126   g_slice_free (GValue, value);
1127 }
1128
1129 static void
1130 scanner_apply_scope (GScanner    *scanner,
1131                      ParserScope  scope)
1132 {
1133   g_scanner_set_scope (scanner, scope);
1134
1135   if (scope == SCOPE_VALUE)
1136     {
1137       scanner->config->cset_identifier_first = G_CSET_a_2_z "@#-_0123456789" G_CSET_A_2_Z;
1138       scanner->config->cset_identifier_nth = G_CSET_a_2_z "@#-_ 0123456789(),.\t\n" G_CSET_A_2_Z;
1139       scanner->config->scan_identifier_1char = TRUE;
1140     }
1141   else if (scope == SCOPE_SELECTOR)
1142     {
1143       scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "*@";
1144       scanner->config->cset_identifier_nth = G_CSET_a_2_z "-_#." G_CSET_A_2_Z;
1145       scanner->config->scan_identifier_1char = TRUE;
1146     }
1147   else if (scope == SCOPE_PSEUDO_CLASS ||
1148            scope == SCOPE_NTH_CHILD ||
1149            scope == SCOPE_DECLARATION)
1150     {
1151       scanner->config->cset_identifier_first = G_CSET_a_2_z "-" G_CSET_A_2_Z;
1152       scanner->config->cset_identifier_nth = G_CSET_a_2_z "-" G_CSET_A_2_Z;
1153       scanner->config->scan_identifier_1char = FALSE;
1154     }
1155   else
1156     g_assert_not_reached ();
1157
1158   scanner->config->scan_float = FALSE;
1159   scanner->config->cpair_comment_single = NULL;
1160 }
1161
1162 static void
1163 css_provider_push_scope (GtkCssProvider *css_provider,
1164                          ParserScope     scope)
1165 {
1166   GtkCssProviderPrivate *priv;
1167
1168   priv = css_provider->priv;
1169   priv->state = g_slist_prepend (priv->state, GUINT_TO_POINTER (scope));
1170
1171   scanner_apply_scope (priv->scanner, scope);
1172 }
1173
1174 static ParserScope
1175 css_provider_pop_scope (GtkCssProvider *css_provider)
1176 {
1177   GtkCssProviderPrivate *priv;
1178   ParserScope scope = SCOPE_SELECTOR;
1179
1180   priv = css_provider->priv;
1181
1182   if (!priv->state)
1183     {
1184       g_warning ("Push/pop calls to parser scope aren't paired");
1185       scanner_apply_scope (priv->scanner, SCOPE_SELECTOR);
1186       return SCOPE_SELECTOR;
1187     }
1188
1189   priv->state = g_slist_delete_link (priv->state, priv->state);
1190
1191   /* Fetch new scope */
1192   if (priv->state)
1193     scope = GPOINTER_TO_INT (priv->state->data);
1194
1195   scanner_apply_scope (priv->scanner, scope);
1196
1197   return scope;
1198 }
1199
1200 static void
1201 css_provider_reset_parser (GtkCssProvider *css_provider)
1202 {
1203   GtkCssProviderPrivate *priv;
1204
1205   priv = css_provider->priv;
1206
1207   g_slist_free (priv->state);
1208   priv->state = NULL;
1209
1210   scanner_apply_scope (priv->scanner, SCOPE_SELECTOR);
1211
1212   g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL);
1213   g_slist_free (priv->cur_selectors);
1214   priv->cur_selectors = NULL;
1215
1216   if (priv->cur_properties)
1217     g_hash_table_unref (priv->cur_properties);
1218
1219   priv->cur_properties = g_hash_table_new_full (g_str_hash,
1220                                                 g_str_equal,
1221                                                 (GDestroyNotify) g_free,
1222                                                 (GDestroyNotify) property_value_free);
1223 }
1224
1225 static void
1226 css_provider_commit (GtkCssProvider *css_provider)
1227 {
1228   GtkCssProviderPrivate *priv;
1229   GSList *l;
1230
1231   priv = css_provider->priv;
1232   l = priv->cur_selectors;
1233
1234   while (l)
1235     {
1236       SelectorPath *path = l->data;
1237       SelectorStyleInfo *info;
1238
1239       info = selector_style_info_new (path);
1240       selector_style_info_set_style (info, priv->cur_properties);
1241
1242       g_ptr_array_add (priv->selectors_info, info);
1243       l = l->next;
1244     }
1245 }
1246
1247 static GTokenType
1248 parse_nth_child (GtkCssProvider *css_provider,
1249                  GScanner       *scanner,
1250                  GtkRegionFlags *flags)
1251 {
1252   ParserSymbol symbol;
1253
1254   g_scanner_get_next_token (scanner);
1255
1256   if (scanner->token != G_TOKEN_SYMBOL)
1257     return G_TOKEN_SYMBOL;
1258
1259   symbol = GPOINTER_TO_INT (scanner->value.v_symbol);
1260
1261   if (symbol == SYMBOL_NTH_CHILD)
1262     {
1263       g_scanner_get_next_token (scanner);
1264
1265       if (scanner->token != G_TOKEN_LEFT_PAREN)
1266         return G_TOKEN_LEFT_PAREN;
1267
1268       css_provider_push_scope (css_provider, SCOPE_NTH_CHILD);
1269       g_scanner_get_next_token (scanner);
1270
1271       if (scanner->token != G_TOKEN_SYMBOL)
1272         return G_TOKEN_SYMBOL;
1273
1274       symbol = GPOINTER_TO_INT (scanner->value.v_symbol);
1275
1276       switch (symbol)
1277         {
1278         case SYMBOL_NTH_CHILD_EVEN:
1279           *flags = GTK_REGION_EVEN;
1280           break;
1281         case SYMBOL_NTH_CHILD_ODD:
1282           *flags = GTK_REGION_ODD;
1283           break;
1284         case SYMBOL_NTH_CHILD_FIRST:
1285           *flags = GTK_REGION_FIRST;
1286           break;
1287         case SYMBOL_NTH_CHILD_LAST:
1288           *flags = GTK_REGION_LAST;
1289           break;
1290         default:
1291           break;
1292         }
1293
1294       g_scanner_get_next_token (scanner);
1295
1296       if (scanner->token != G_TOKEN_RIGHT_PAREN)
1297         return G_TOKEN_RIGHT_PAREN;
1298
1299       css_provider_pop_scope (css_provider);
1300     }
1301   else if (symbol == SYMBOL_FIRST_CHILD)
1302     *flags = GTK_REGION_FIRST;
1303   else if (symbol == SYMBOL_LAST_CHILD)
1304     *flags = GTK_REGION_LAST;
1305   else if (symbol == SYMBOL_SORTED_CHILD)
1306     *flags = GTK_REGION_SORTED;
1307   else
1308     {
1309       *flags = 0;
1310       return G_TOKEN_SYMBOL;
1311     }
1312
1313   return G_TOKEN_NONE;
1314 }
1315
1316 static GTokenType
1317 parse_pseudo_class (GtkCssProvider *css_provider,
1318                     GScanner       *scanner,
1319                     SelectorPath   *selector)
1320 {
1321   GtkStateType state;
1322
1323   g_scanner_get_next_token (scanner);
1324
1325   if (scanner->token != G_TOKEN_SYMBOL)
1326     return G_TOKEN_SYMBOL;
1327
1328   state = GPOINTER_TO_INT (scanner->value.v_symbol);
1329
1330   switch (state)
1331     {
1332     case GTK_STATE_ACTIVE:
1333       selector->state |= GTK_STATE_FLAG_ACTIVE;
1334       break;
1335     case GTK_STATE_PRELIGHT:
1336       selector->state |= GTK_STATE_FLAG_PRELIGHT;
1337       break;
1338     case GTK_STATE_SELECTED:
1339       selector->state |= GTK_STATE_FLAG_SELECTED;
1340       break;
1341     case GTK_STATE_INSENSITIVE:
1342       selector->state |= GTK_STATE_FLAG_INSENSITIVE;
1343       break;
1344     case GTK_STATE_INCONSISTENT:
1345       selector->state |= GTK_STATE_FLAG_INCONSISTENT;
1346       break;
1347     case GTK_STATE_FOCUSED:
1348       selector->state |= GTK_STATE_FLAG_FOCUSED;
1349       break;
1350     default:
1351       return G_TOKEN_SYMBOL;
1352     }
1353
1354   return G_TOKEN_NONE;
1355 }
1356
1357 /* Parses a number of concatenated classes */
1358 static void
1359 parse_classes (SelectorPath   *path,
1360                const gchar    *str)
1361 {
1362   gchar *pos;
1363
1364   if ((pos = strchr (str, '.')) != NULL)
1365     {
1366       /* Leave the last class to the call after the loop */
1367       while (pos)
1368         {
1369           *pos = '\0';
1370           selector_path_prepend_class (path, str);
1371
1372           str = pos + 1;
1373           pos = strchr (str, '.');
1374         }
1375     }
1376
1377   selector_path_prepend_class (path, str);
1378 }
1379
1380 static GTokenType
1381 parse_selector (GtkCssProvider  *css_provider,
1382                 GScanner        *scanner,
1383                 SelectorPath   **selector_out)
1384 {
1385   SelectorPath *path;
1386
1387   path = selector_path_new ();
1388   *selector_out = path;
1389
1390   if (scanner->token != ':' &&
1391       scanner->token != '#' &&
1392       scanner->token != '.' &&
1393       scanner->token != G_TOKEN_IDENTIFIER)
1394     return G_TOKEN_IDENTIFIER;
1395
1396   while (scanner->token == '#' ||
1397          scanner->token == '.' ||
1398          scanner->token == G_TOKEN_IDENTIFIER)
1399     {
1400       if (scanner->token == '#' ||
1401           scanner->token == '.')
1402         {
1403           gboolean is_class;
1404           gchar *pos;
1405
1406           is_class = (scanner->token == '.');
1407
1408           g_scanner_get_next_token (scanner);
1409
1410           if (scanner->token != G_TOKEN_IDENTIFIER)
1411             return G_TOKEN_IDENTIFIER;
1412
1413           selector_path_prepend_glob (path);
1414           selector_path_prepend_combinator (path, COMBINATOR_CHILD);
1415
1416           if (is_class)
1417             parse_classes (path, scanner->value.v_identifier);
1418           else
1419             {
1420               if ((pos = strchr (scanner->value.v_identifier, '.')) != NULL)
1421                 *pos = '\0';
1422
1423               selector_path_prepend_name (path, scanner->value.v_identifier);
1424
1425               /* Parse any remaining classes */
1426               if (pos)
1427                 parse_classes (path, pos + 1);
1428             }
1429         }
1430       else if (g_ascii_isupper (scanner->value.v_identifier[0]))
1431         {
1432           gchar *pos;
1433
1434           if ((pos = strchr (scanner->value.v_identifier, '#')) != NULL ||
1435               (pos = strchr (scanner->value.v_identifier, '.')) != NULL)
1436             {
1437               gchar *type_name, *name;
1438               gboolean is_class;
1439
1440               is_class = (*pos == '.');
1441
1442               /* Widget type and name/class put together */
1443               name = pos + 1;
1444               *pos = '\0';
1445               type_name = scanner->value.v_identifier;
1446
1447               selector_path_prepend_type (path, type_name);
1448
1449               /* This is only so there is a direct relationship
1450                * between widget type and its name.
1451                */
1452               selector_path_prepend_combinator (path, COMBINATOR_CHILD);
1453
1454               if (is_class)
1455                 parse_classes (path, name);
1456               else
1457                 {
1458                   if ((pos = strchr (name, '.')) != NULL)
1459                     *pos = '\0';
1460
1461                   selector_path_prepend_name (path, name);
1462
1463                   /* Parse any remaining classes */
1464                   if (pos)
1465                     parse_classes (path, pos + 1);
1466                 }
1467             }
1468           else
1469             selector_path_prepend_type (path, scanner->value.v_identifier);
1470         }
1471       else if (g_ascii_islower (scanner->value.v_identifier[0]))
1472         {
1473           GtkRegionFlags flags = 0;
1474           gchar *region_name;
1475
1476           region_name = g_strdup (scanner->value.v_identifier);
1477
1478           if (g_scanner_peek_next_token (scanner) == ':')
1479             {
1480               ParserSymbol symbol;
1481
1482               g_scanner_get_next_token (scanner);
1483               css_provider_push_scope (css_provider, SCOPE_PSEUDO_CLASS);
1484
1485               /* Check for the next token being nth-child, parse in that
1486                * case, and fallback into common state parsing if not.
1487                */
1488               if (g_scanner_peek_next_token (scanner) != G_TOKEN_SYMBOL)
1489                 return G_TOKEN_SYMBOL;
1490
1491               symbol = GPOINTER_TO_INT (scanner->next_value.v_symbol);
1492
1493               if (symbol == SYMBOL_FIRST_CHILD ||
1494                   symbol == SYMBOL_LAST_CHILD ||
1495                   symbol == SYMBOL_NTH_CHILD ||
1496                   symbol == SYMBOL_SORTED_CHILD)
1497                 {
1498                   GTokenType token;
1499
1500                   if ((token = parse_nth_child (css_provider, scanner, &flags)) != G_TOKEN_NONE)
1501                     return token;
1502
1503                   css_provider_pop_scope (css_provider);
1504                 }
1505               else
1506                 {
1507                   css_provider_pop_scope (css_provider);
1508                   selector_path_prepend_region (path, region_name, 0);
1509                   g_free (region_name);
1510                   break;
1511                 }
1512             }
1513
1514           selector_path_prepend_region (path, region_name, flags);
1515           g_free (region_name);
1516         }
1517       else if (scanner->value.v_identifier[0] == '*')
1518         selector_path_prepend_glob (path);
1519       else
1520         return G_TOKEN_IDENTIFIER;
1521
1522       g_scanner_get_next_token (scanner);
1523
1524       if (scanner->token == '>')
1525         {
1526           selector_path_prepend_combinator (path, COMBINATOR_CHILD);
1527           g_scanner_get_next_token (scanner);
1528         }
1529     }
1530
1531   if (scanner->token == ':')
1532     {
1533       /* Add glob selector if path is empty */
1534       if (selector_path_depth (path) == 0)
1535         selector_path_prepend_glob (path);
1536
1537       css_provider_push_scope (css_provider, SCOPE_PSEUDO_CLASS);
1538
1539       while (scanner->token == ':')
1540         {
1541           GTokenType token;
1542
1543           if ((token = parse_pseudo_class (css_provider, scanner, path)) != G_TOKEN_NONE)
1544             return token;
1545
1546           g_scanner_get_next_token (scanner);
1547         }
1548
1549       css_provider_pop_scope (css_provider);
1550     }
1551
1552   return G_TOKEN_NONE;
1553 }
1554
1555 #define SKIP_SPACES(s) while (s[0] == ' ' || s[0] == '\t' || s[0] == '\n') s++;
1556
1557 static GtkSymbolicColor *
1558 symbolic_color_parse_str (const gchar  *string,
1559                           gchar       **end_ptr)
1560 {
1561   GtkSymbolicColor *symbolic_color = NULL;
1562   gchar *str;
1563
1564   str = (gchar *) string;
1565   *end_ptr = str;
1566
1567   if (str[0] == '@')
1568     {
1569       const gchar *end;
1570       gchar *name;
1571
1572       str++;
1573       end = str;
1574
1575       while (*end == '-' || *end == '_' ||
1576              g_ascii_isalpha (*end))
1577         end++;
1578
1579       name = g_strndup (str, end - str);
1580       symbolic_color = gtk_symbolic_color_new_name (name);
1581       g_free (name);
1582
1583       *end_ptr = (gchar *) end;
1584     }
1585   else if (g_str_has_prefix (str, "lighter") ||
1586            g_str_has_prefix (str, "darker"))
1587     {
1588       GtkSymbolicColor *param_color;
1589       gboolean is_lighter = FALSE;
1590
1591       is_lighter = g_str_has_prefix (str, "lighter");
1592
1593       if (is_lighter)
1594         str += strlen ("lighter");
1595       else
1596         str += strlen ("darker");
1597
1598       SKIP_SPACES (str);
1599
1600       if (*str != '(')
1601         {
1602           *end_ptr = (gchar *) str;
1603           return NULL;
1604         }
1605
1606       str++;
1607       SKIP_SPACES (str);
1608       param_color = symbolic_color_parse_str (str, end_ptr);
1609
1610       if (!param_color)
1611         return NULL;
1612
1613       str = *end_ptr;
1614       SKIP_SPACES (str);
1615       *end_ptr = (gchar *) str;
1616
1617       if (*str != ')')
1618         {
1619           gtk_symbolic_color_unref (param_color);
1620           return NULL;
1621         }
1622
1623       if (is_lighter)
1624         symbolic_color = gtk_symbolic_color_new_shade (param_color, 1.3);
1625       else
1626         symbolic_color = gtk_symbolic_color_new_shade (param_color, 0.7);
1627
1628       gtk_symbolic_color_unref (param_color);
1629       (*end_ptr)++;
1630     }
1631   else if (g_str_has_prefix (str, "shade") ||
1632            g_str_has_prefix (str, "alpha"))
1633     {
1634       GtkSymbolicColor *param_color;
1635       gboolean is_shade = FALSE;
1636       gdouble factor;
1637
1638       is_shade = g_str_has_prefix (str, "shade");
1639
1640       if (is_shade)
1641         str += strlen ("shade");
1642       else
1643         str += strlen ("alpha");
1644
1645       SKIP_SPACES (str);
1646
1647       if (*str != '(')
1648         {
1649           *end_ptr = (gchar *) str;
1650           return NULL;
1651         }
1652
1653       str++;
1654       SKIP_SPACES (str);
1655       param_color = symbolic_color_parse_str (str, end_ptr);
1656
1657       if (!param_color)
1658         return NULL;
1659
1660       str = *end_ptr;
1661       SKIP_SPACES (str);
1662
1663       if (str[0] != ',')
1664         {
1665           gtk_symbolic_color_unref (param_color);
1666           *end_ptr = (gchar *) str;
1667           return NULL;
1668         }
1669
1670       str++;
1671       SKIP_SPACES (str);
1672       factor = g_ascii_strtod (str, end_ptr);
1673
1674       str = *end_ptr;
1675       SKIP_SPACES (str);
1676       *end_ptr = (gchar *) str;
1677
1678       if (str[0] != ')')
1679         {
1680           gtk_symbolic_color_unref (param_color);
1681           return NULL;
1682         }
1683
1684       if (is_shade)
1685         symbolic_color = gtk_symbolic_color_new_shade (param_color, factor);
1686       else
1687         symbolic_color = gtk_symbolic_color_new_alpha (param_color, factor);
1688
1689       gtk_symbolic_color_unref (param_color);
1690       (*end_ptr)++;
1691     }
1692   else if (g_str_has_prefix (str, "mix"))
1693     {
1694       GtkSymbolicColor *color1, *color2;
1695       gdouble factor;
1696
1697       str += strlen ("mix");
1698       SKIP_SPACES (str);
1699
1700       if (*str != '(')
1701         {
1702           *end_ptr = (gchar *) str;
1703           return NULL;
1704         }
1705
1706       str++;
1707       SKIP_SPACES (str);
1708       color1 = symbolic_color_parse_str (str, end_ptr);
1709
1710       if (!color1)
1711         return NULL;
1712
1713       str = *end_ptr;
1714       SKIP_SPACES (str);
1715
1716       if (str[0] != ',')
1717         {
1718           gtk_symbolic_color_unref (color1);
1719           *end_ptr = (gchar *) str;
1720           return NULL;
1721         }
1722
1723       str++;
1724       SKIP_SPACES (str);
1725       color2 = symbolic_color_parse_str (str, end_ptr);
1726
1727       if (!color2 || *end_ptr[0] != ',')
1728         {
1729           gtk_symbolic_color_unref (color1);
1730           return NULL;
1731         }
1732
1733       str = *end_ptr;
1734       SKIP_SPACES (str);
1735
1736       if (str[0] != ',')
1737         {
1738           gtk_symbolic_color_unref (color1);
1739           gtk_symbolic_color_unref (color2);
1740           *end_ptr = (gchar *) str;
1741           return NULL;
1742         }
1743
1744       str++;
1745       SKIP_SPACES (str);
1746       factor = g_ascii_strtod (str, end_ptr);
1747
1748       str = *end_ptr;
1749       SKIP_SPACES (str);
1750       *end_ptr = (gchar *) str;
1751
1752       if (str[0] != ')')
1753         {
1754           gtk_symbolic_color_unref (color1);
1755           gtk_symbolic_color_unref (color2);
1756           return NULL;
1757         }
1758
1759       symbolic_color = gtk_symbolic_color_new_mix (color1, color2, factor);
1760       gtk_symbolic_color_unref (color1);
1761       gtk_symbolic_color_unref (color2);
1762       (*end_ptr)++;
1763     }
1764   else
1765     {
1766       GdkRGBA color;
1767       gchar *color_str;
1768       const gchar *end;
1769
1770       end = str + 1;
1771
1772       if (str[0] == '#')
1773         {
1774           /* Color in hex format */
1775           while (g_ascii_isxdigit (*end))
1776             end++;
1777         }
1778       else if (g_str_has_prefix (str, "rgb"))
1779         {
1780           /* color in rgb/rgba format */
1781           while (*end != ')' && *end != '\0')
1782             end++;
1783
1784           if (*end == ')')
1785             end++;
1786         }
1787       else
1788         {
1789           /* color name? parse until first whitespace */
1790           while (*end != ' ' && *end != '\0')
1791             end++;
1792         }
1793
1794       color_str = g_strndup (str, end - str);
1795       *end_ptr = (gchar *) end;
1796
1797       if (!gdk_rgba_parse (&color, color_str))
1798         {
1799           g_free (color_str);
1800           return NULL;
1801         }
1802
1803       symbolic_color = gtk_symbolic_color_new_literal (&color);
1804       g_free (color_str);
1805     }
1806
1807   return symbolic_color;
1808 }
1809
1810 static GtkSymbolicColor *
1811 symbolic_color_parse (const gchar *str)
1812 {
1813   GtkSymbolicColor *color;
1814   gchar *end;
1815
1816   color = symbolic_color_parse_str (str, &end);
1817
1818   if (*end != '\0')
1819     {
1820       g_warning ("Error parsing symbolic color \"%s\", stopped at char %ld : '%c'",
1821                  str, end - str, *end);
1822
1823       if (color)
1824         {
1825           gtk_symbolic_color_unref (color);
1826           color = NULL;
1827         }
1828     }
1829
1830   return color;
1831 }
1832
1833 static GtkGradient *
1834 gradient_parse_str (const gchar  *str,
1835                     gchar       **end_ptr)
1836 {
1837   GtkGradient *gradient = NULL;
1838   gdouble coords[6];
1839   gchar *end;
1840   guint i;
1841
1842   if (g_str_has_prefix (str, "-gtk-gradient"))
1843     {
1844       cairo_pattern_type_t type;
1845
1846       str += strlen ("-gtk-gradient");
1847       SKIP_SPACES (str);
1848
1849       if (*str != '(')
1850         {
1851           *end_ptr = (gchar *) str;
1852           return NULL;
1853         }
1854
1855       str++;
1856       SKIP_SPACES (str);
1857
1858       /* Parse gradient type */
1859       if (g_str_has_prefix (str, "linear"))
1860         {
1861           type = CAIRO_PATTERN_TYPE_LINEAR;
1862           str += strlen ("linear");
1863         }
1864       else if (g_str_has_prefix (str, "radial"))
1865         {
1866           type = CAIRO_PATTERN_TYPE_RADIAL;
1867           str += strlen ("radial");
1868         }
1869       else
1870         {
1871           *end_ptr = (gchar *) str;
1872           return NULL;
1873         }
1874
1875       SKIP_SPACES (str);
1876
1877       /* Parse start/stop position parameters */
1878       for (i = 0; i < 2; i++)
1879         {
1880           if (*str != ',')
1881             {
1882               *end_ptr = (gchar *) str;
1883               return NULL;
1884             }
1885
1886           str++;
1887           SKIP_SPACES (str);
1888
1889           if (strncmp (str, "left", 4) == 0)
1890             {
1891               coords[i * 3] = 0;
1892               str += strlen ("left");
1893             }
1894           else if (strncmp (str, "right", 5) == 0)
1895             {
1896               coords[i * 3] = 1;
1897               str += strlen ("right");
1898             }
1899           else if (strncmp (str, "center", 6) == 0)
1900             {
1901               coords[i * 3] = 0.5;
1902               str += strlen ("center");
1903             }
1904           else
1905             {
1906               coords[i * 3] = g_ascii_strtod (str, &end);
1907               str = end;
1908             }
1909
1910           SKIP_SPACES (str);
1911
1912           if (strncmp (str, "top", 3) == 0)
1913             {
1914               coords[(i * 3) + 1] = 0;
1915               str += strlen ("top");
1916             }
1917           else if (strncmp (str, "bottom", 6) == 0)
1918             {
1919               coords[(i * 3) + 1] = 1;
1920               str += strlen ("bottom");
1921             }
1922           else if (strncmp (str, "center", 6) == 0)
1923             {
1924               coords[(i * 3) + 1] = 0.5;
1925               str += strlen ("center");
1926             }
1927           else
1928             {
1929               coords[(i * 3) + 1] = g_ascii_strtod (str, &end);
1930               str = end;
1931             }
1932
1933           SKIP_SPACES (str);
1934
1935           if (type == CAIRO_PATTERN_TYPE_RADIAL)
1936             {
1937               /* Parse radius */
1938               if (*str != ',')
1939                 {
1940                   *end_ptr = (gchar *) str;
1941                   return NULL;
1942                 }
1943
1944               str++;
1945               SKIP_SPACES (str);
1946
1947               coords[(i * 3) + 2] = g_ascii_strtod (str, &end);
1948               str = end;
1949
1950               SKIP_SPACES (str);
1951             }
1952         }
1953
1954       if (type == CAIRO_PATTERN_TYPE_LINEAR)
1955         gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
1956       else
1957         gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
1958                                             coords[3], coords[4], coords[5]);
1959
1960       while (*str == ',')
1961         {
1962           GtkSymbolicColor *color;
1963           gdouble position;
1964
1965           if (*str != ',')
1966             {
1967               *end_ptr = (gchar *) str;
1968               return gradient;
1969             }
1970
1971           str++;
1972           SKIP_SPACES (str);
1973
1974           if (g_str_has_prefix (str, "from"))
1975             {
1976               position = 0;
1977               str += strlen ("from");
1978               SKIP_SPACES (str);
1979
1980               if (*str != '(')
1981                 {
1982                   *end_ptr = (gchar *) str;
1983                   return gradient;
1984                 }
1985             }
1986           else if (g_str_has_prefix (str, "to"))
1987             {
1988               position = 1;
1989               str += strlen ("to");
1990               SKIP_SPACES (str);
1991
1992               if (*str != '(')
1993                 {
1994                   *end_ptr = (gchar *) str;
1995                   return gradient;
1996                 }
1997             }
1998           else if (g_str_has_prefix (str, "color-stop"))
1999             {
2000               str += strlen ("color-stop");
2001               SKIP_SPACES (str);
2002
2003               if (*str != '(')
2004                 {
2005                   *end_ptr = (gchar *) str;
2006                   return gradient;
2007                 }
2008
2009               str++;
2010               SKIP_SPACES (str);
2011
2012               position = g_strtod (str, &end);
2013
2014               str = end;
2015               SKIP_SPACES (str);
2016
2017               if (*str != ',')
2018                 {
2019                   *end_ptr = (gchar *) str;
2020                   return gradient;
2021                 }
2022             }
2023           else
2024             {
2025               *end_ptr = (gchar *) str;
2026               return gradient;
2027             }
2028
2029           str++;
2030           SKIP_SPACES (str);
2031
2032           color = symbolic_color_parse_str (str, &end);
2033
2034           str = end;
2035           SKIP_SPACES (str);
2036
2037           if (*str != ')')
2038             {
2039               *end_ptr = (gchar *) str;
2040               return gradient;
2041             }
2042
2043           str++;
2044           SKIP_SPACES (str);
2045
2046           if (color)
2047             {
2048               gtk_gradient_add_color_stop (gradient, position, color);
2049               gtk_symbolic_color_unref (color);
2050             }
2051         }
2052
2053       if (*str != ')')
2054         {
2055           *end_ptr = (gchar *) str;
2056           return gradient;
2057         }
2058
2059       str++;
2060     }
2061
2062   *end_ptr = (gchar *) str;
2063
2064   return gradient;
2065 }
2066
2067 static GtkGradient *
2068 gradient_parse (const gchar *str)
2069 {
2070   GtkGradient *gradient;
2071   gchar *end;
2072
2073   gradient = gradient_parse_str (str, &end);
2074
2075   if (*end != '\0')
2076     {
2077       g_warning ("Error parsing pattern \"%s\", stopped at char %ld : '%c'",
2078                  str, end - str, *end);
2079
2080       if (gradient)
2081         {
2082           gtk_gradient_unref (gradient);
2083           gradient = NULL;
2084         }
2085     }
2086
2087   return gradient;
2088 }
2089
2090 static gchar *
2091 path_parse_str (GtkCssProvider  *css_provider,
2092                 const gchar     *str,
2093                 gchar          **end_ptr)
2094 {
2095   gchar *path, *chr;
2096
2097   if (!g_str_has_prefix (str, "url"))
2098     {
2099       *end_ptr = (gchar *) str;
2100       return NULL;
2101     }
2102
2103   str += strlen ("url");
2104   SKIP_SPACES (str);
2105
2106   if (*str != '(')
2107     {
2108       *end_ptr = (gchar *) str;
2109       return NULL;
2110     }
2111
2112   chr = strchr (str, ')');
2113
2114   if (!chr)
2115     {
2116       *end_ptr = (gchar *) str;
2117       return NULL;
2118     }
2119
2120   str++;
2121   SKIP_SPACES (str);
2122
2123   path = g_strndup (str, chr - str);
2124   g_strstrip (path);
2125
2126   *end_ptr = chr + 1;
2127
2128   /* Always return an absolute path */
2129   if (!g_path_is_absolute (path))
2130     {
2131       GtkCssProviderPrivate *priv;
2132       gchar *dirname, *full_path;
2133
2134       priv = css_provider->priv;
2135
2136       /* Use relative path to the current CSS file path, if any */
2137       dirname = g_path_get_dirname (priv->filename);
2138
2139       full_path = g_build_filename (dirname, path, NULL);
2140       g_free (path);
2141       g_free (dirname);
2142
2143       path = full_path;
2144     }
2145
2146   if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
2147     {
2148       g_warning ("File doesn't exist: %s\n", path);
2149       g_free (path);
2150       path = NULL;
2151     }
2152
2153   return path;
2154 }
2155
2156 static gchar *
2157 path_parse (GtkCssProvider *css_provider,
2158             const gchar    *str)
2159 {
2160   gchar *path, *end;
2161
2162   path = path_parse_str (css_provider, str, &end);
2163
2164   if (*end != '\0')
2165     {
2166       g_warning ("Error parsing file path \"%s\", stopped at char %ld : '%c'",
2167                  str, end - str, *end);
2168
2169       if (path)
2170         {
2171           g_free (path);
2172           path = NULL;
2173         }
2174     }
2175
2176   return path;
2177 }
2178
2179 static Gtk9Slice *
2180 slice_parse_str (GtkCssProvider  *css_provider,
2181                  const gchar     *str,
2182                  gchar          **end_ptr)
2183 {
2184   gdouble distance_top, distance_bottom;
2185   gdouble distance_left, distance_right;
2186   GtkSliceSideModifier mods[2];
2187   GError *error = NULL;
2188   GdkPixbuf *pixbuf;
2189   Gtk9Slice *slice;
2190   gchar *path;
2191   gint i = 0;
2192
2193   SKIP_SPACES (str);
2194
2195   /* Parse image url */
2196   path = path_parse_str (css_provider, str, end_ptr);
2197
2198   if (!path)
2199       return NULL;
2200
2201   str = *end_ptr;
2202   SKIP_SPACES (str);
2203
2204   /* Parse top/left/bottom/right distances */
2205   distance_top = g_strtod (str, end_ptr);
2206
2207   str = *end_ptr;
2208   SKIP_SPACES (str);
2209
2210   distance_right = g_strtod (str, end_ptr);
2211
2212   str = *end_ptr;
2213   SKIP_SPACES (str);
2214
2215   distance_bottom = g_strtod (str, end_ptr);
2216
2217   str = *end_ptr;
2218   SKIP_SPACES (str);
2219
2220   distance_left = g_strtod (str, end_ptr);
2221
2222   str = *end_ptr;
2223   SKIP_SPACES (str);
2224
2225   while (*str && i < 2)
2226     {
2227       if (g_str_has_prefix (str, "stretch"))
2228         {
2229           str += strlen ("stretch");
2230           mods[i] = GTK_SLICE_STRETCH;
2231         }
2232       else if (g_str_has_prefix (str, "repeat"))
2233         {
2234           str += strlen ("repeat");
2235           mods[i] = GTK_SLICE_REPEAT;
2236         }
2237       else
2238         {
2239           g_free (path);
2240           *end_ptr = (gchar *) str;
2241           return NULL;
2242         }
2243
2244       SKIP_SPACES (str);
2245       i++;
2246     }
2247
2248   *end_ptr = (gchar *) str;
2249
2250   if (*str != '\0')
2251     {
2252       g_free (path);
2253       return NULL;
2254     }
2255
2256   if (i != 2)
2257     {
2258       /* Fill in second modifier, same as the first */
2259       mods[1] = mods[0];
2260     }
2261
2262   pixbuf = gdk_pixbuf_new_from_file (path, &error);
2263   g_free (path);
2264
2265   if (error)
2266     {
2267       g_warning ("Pixbuf could not be loaded: %s\n", error->message);
2268       g_error_free (error);
2269       *end_ptr = (gchar *) str;
2270       return NULL;
2271     }
2272
2273   slice = gtk_9slice_new (pixbuf,
2274                           distance_top, distance_bottom,
2275                           distance_left, distance_right,
2276                           mods[0], mods[1]);
2277   g_object_unref (pixbuf);
2278
2279   return slice;
2280 }
2281
2282 static Gtk9Slice *
2283 slice_parse (GtkCssProvider *css_provider,
2284              const gchar    *str)
2285 {
2286   Gtk9Slice *slice;
2287   gchar *end;
2288
2289   slice = slice_parse_str (css_provider, str, &end);
2290
2291   if (*end != '\0')
2292     {
2293       g_warning ("Error parsing sliced image \"%s\", stopped at char %ld : '%c'",
2294                  str, end - str, *end);
2295
2296       if (slice)
2297         {
2298           gtk_9slice_unref (slice);
2299           slice = NULL;
2300         }
2301     }
2302
2303   return slice;
2304 }
2305
2306 static gboolean
2307 css_provider_parse_value (GtkCssProvider *css_provider,
2308                           const gchar    *value_str,
2309                           GValue         *value)
2310 {
2311   GType type;
2312   gboolean parsed = TRUE;
2313
2314   type = G_VALUE_TYPE (value);
2315
2316   if (type == GDK_TYPE_RGBA)
2317     {
2318       GdkRGBA color;
2319
2320       if (gdk_rgba_parse (&color, value_str) == TRUE)
2321         g_value_set_boxed (value, &color);
2322       else
2323         {
2324           GtkSymbolicColor *symbolic_color;
2325
2326           symbolic_color = symbolic_color_parse (value_str);
2327
2328           if (!symbolic_color)
2329             return FALSE;
2330
2331           g_value_unset (value);
2332           g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
2333           g_value_take_boxed (value, symbolic_color);
2334         }
2335     }
2336   else if (type == PANGO_TYPE_FONT_DESCRIPTION)
2337     {
2338       PangoFontDescription *font_desc;
2339
2340       font_desc = pango_font_description_from_string (value_str);
2341       g_value_take_boxed (value, font_desc);
2342     }
2343   else if (type == G_TYPE_BOOLEAN)
2344     {
2345       if (value_str[0] == '1' ||
2346           g_ascii_strcasecmp (value_str, "true") == 0)
2347         g_value_set_boolean (value, TRUE);
2348       else
2349         g_value_set_boolean (value, FALSE);
2350     }
2351   else if (type == G_TYPE_INT)
2352     g_value_set_int (value, atoi (value_str));
2353   else if (type == G_TYPE_DOUBLE)
2354     g_value_set_double (value, g_ascii_strtod (value_str, NULL));
2355   else if (type == GTK_TYPE_THEMING_ENGINE)
2356     {
2357       GtkThemingEngine *engine;
2358
2359       engine = gtk_theming_engine_load (value_str);
2360       g_value_set_object (value, engine);
2361     }
2362   else if (type == GTK_TYPE_ANIMATION_DESCRIPTION)
2363     {
2364       GtkAnimationDescription *desc;
2365
2366       desc = gtk_animation_description_from_string (value_str);
2367
2368       if (desc)
2369         g_value_take_boxed (value, desc);
2370       else
2371         parsed = FALSE;
2372     }
2373   else if (type == GTK_TYPE_BORDER)
2374     {
2375       guint first, second, third, fourth;
2376       GtkBorder border;
2377
2378       /* FIXME: no unit support */
2379       if (sscanf (value_str, "%d %d %d %d",
2380                   &first, &second, &third, &fourth) == 4)
2381         {
2382           border.top = first;
2383           border.right = second;
2384           border.bottom = third;
2385           border.left = fourth;
2386         }
2387       else if (sscanf (value_str, "%d %d %d",
2388                        &first, &second, &third) == 3)
2389         {
2390           border.top = first;
2391           border.left = border.right = second;
2392           border.bottom = third;
2393         }
2394       else if (sscanf (value_str, "%d %d", &first, &second) == 2)
2395         {
2396           border.top = border.bottom = first;
2397           border.left = border.right = second;
2398         }
2399       else if (sscanf (value_str, "%d", &first) == 1)
2400         {
2401           border.top = border.bottom = first;
2402           border.left = border.right = first;
2403         }
2404       else
2405         parsed = FALSE;
2406
2407       if (parsed)
2408         g_value_set_boxed (value, &border);
2409     }
2410   else if (type == CAIRO_GOBJECT_TYPE_PATTERN)
2411     {
2412       GtkGradient *gradient;
2413
2414       gradient = gradient_parse (value_str);
2415
2416       if (gradient)
2417         {
2418           g_value_unset (value);
2419           g_value_init (value, GTK_TYPE_GRADIENT);
2420           g_value_take_boxed (value, gradient);
2421         }
2422       else
2423         parsed = FALSE;
2424     }
2425   else if (G_TYPE_IS_ENUM (type))
2426     {
2427       GEnumClass *class;
2428       GEnumValue *enum_value;
2429
2430       class = g_type_class_ref (type);
2431       enum_value = g_enum_get_value_by_nick (class, value_str);
2432
2433       g_value_set_enum (value, enum_value->value);
2434       g_type_class_unref (class);
2435     }
2436   else if (type == GTK_TYPE_9SLICE)
2437     {
2438       Gtk9Slice *slice;
2439
2440       slice = slice_parse (css_provider, value_str);
2441
2442       if (slice)
2443         g_value_take_boxed (value, slice);
2444       else
2445         parsed = FALSE;
2446     }
2447   else
2448     {
2449       g_warning ("Cannot parse string '%s' for type %s", value_str, g_type_name (type));
2450       parsed = FALSE;
2451     }
2452
2453   return parsed;
2454 }
2455
2456 static GTokenType
2457 parse_rule (GtkCssProvider *css_provider,
2458             GScanner       *scanner)
2459 {
2460   GtkCssProviderPrivate *priv;
2461   GTokenType expected_token;
2462   SelectorPath *selector;
2463
2464   priv = css_provider->priv;
2465
2466   css_provider_push_scope (css_provider, SCOPE_SELECTOR);
2467
2468   /* Handle directives */
2469   if (scanner->token == G_TOKEN_IDENTIFIER &&
2470       scanner->value.v_identifier[0] == '@')
2471     {
2472       gchar *directive;
2473
2474       directive = &scanner->value.v_identifier[1];
2475
2476       if (strcmp (directive, "define-color") == 0)
2477         {
2478           GtkSymbolicColor *color;
2479           gchar *color_name, *color_str;
2480
2481           /* Directive is a color mapping */
2482           g_scanner_get_next_token (scanner);
2483
2484           if (scanner->token != G_TOKEN_IDENTIFIER)
2485             return G_TOKEN_IDENTIFIER;
2486
2487           color_name = g_strdup (scanner->value.v_identifier);
2488           css_provider_push_scope (css_provider, SCOPE_VALUE);
2489           g_scanner_get_next_token (scanner);
2490
2491           if (scanner->token != G_TOKEN_IDENTIFIER)
2492             return G_TOKEN_IDENTIFIER;
2493
2494           color_str = g_strstrip (scanner->value.v_identifier);
2495           color = symbolic_color_parse (color_str);
2496
2497           if (!color)
2498             return G_TOKEN_IDENTIFIER;
2499
2500           g_hash_table_insert (priv->symbolic_colors, color_name, color);
2501
2502           css_provider_pop_scope (css_provider);
2503           g_scanner_get_next_token (scanner);
2504
2505           if (scanner->token != ';')
2506             return ';';
2507
2508           return G_TOKEN_NONE;
2509         }
2510       else if (strcmp (directive, "import") == 0)
2511         {
2512           GScanner *scanner_backup;
2513           GSList *state_backup;
2514           GError *error = NULL;
2515           gboolean loaded;
2516           gchar *path;
2517
2518           css_provider_push_scope (css_provider, SCOPE_VALUE);
2519           g_scanner_get_next_token (scanner);
2520
2521           if (scanner->token != G_TOKEN_IDENTIFIER)
2522             return G_TOKEN_IDENTIFIER;
2523
2524           path = path_parse (css_provider,
2525                              g_strstrip (scanner->value.v_identifier));
2526
2527           if (!path)
2528             return G_TOKEN_IDENTIFIER;
2529
2530           css_provider_pop_scope (css_provider);
2531           g_scanner_get_next_token (scanner);
2532
2533           if (scanner->token != ';')
2534             {
2535               g_free (path);
2536               return ';';
2537             }
2538
2539           /* Snapshot current parser state and scanner in order to restore after importing */
2540           state_backup = priv->state;
2541           scanner_backup = priv->scanner;
2542
2543           priv->state = NULL;
2544           priv->scanner = create_scanner ();
2545
2546           /* FIXME: Avoid recursive importing */
2547           loaded = gtk_css_provider_load_from_path_internal (css_provider, path,
2548                                                              FALSE, &error);
2549
2550           /* Restore previous state */
2551           css_provider_reset_parser (css_provider);
2552           priv->state = state_backup;
2553           g_scanner_destroy (priv->scanner);
2554           priv->scanner = scanner_backup;
2555
2556           g_free (path);
2557
2558           if (!loaded)
2559             {
2560               g_warning ("Error loading imported file \"%s\": %s",
2561                          path, (error) ? error->message : "");
2562               g_error_free (error);
2563               return G_TOKEN_IDENTIFIER;
2564             }
2565           else
2566             return G_TOKEN_NONE;
2567         }
2568       else
2569         return G_TOKEN_IDENTIFIER;
2570     }
2571
2572   expected_token = parse_selector (css_provider, scanner, &selector);
2573
2574   if (expected_token != G_TOKEN_NONE)
2575     {
2576       selector_path_unref (selector);
2577       return expected_token;
2578     }
2579
2580   priv->cur_selectors = g_slist_prepend (priv->cur_selectors, selector);
2581
2582   while (scanner->token == ',')
2583     {
2584       g_scanner_get_next_token (scanner);
2585
2586       expected_token = parse_selector (css_provider, scanner, &selector);
2587
2588       if (expected_token != G_TOKEN_NONE)
2589         {
2590           selector_path_unref (selector);
2591           return expected_token;
2592         }
2593
2594       priv->cur_selectors = g_slist_prepend (priv->cur_selectors, selector);
2595     }
2596
2597   css_provider_pop_scope (css_provider);
2598
2599   if (scanner->token != G_TOKEN_LEFT_CURLY)
2600     return G_TOKEN_LEFT_CURLY;
2601
2602   /* Declarations parsing */
2603   css_provider_push_scope (css_provider, SCOPE_DECLARATION);
2604   g_scanner_get_next_token (scanner);
2605
2606   while (scanner->token == G_TOKEN_IDENTIFIER)
2607     {
2608       const gchar *value_str = NULL;
2609       GtkStylePropertyParser parse_func = NULL;
2610       GType prop_type;
2611       GError *error = NULL;
2612       gchar *prop;
2613
2614       prop = g_strdup (scanner->value.v_identifier);
2615       g_scanner_get_next_token (scanner);
2616
2617       if (scanner->token != ':')
2618         {
2619           g_free (prop);
2620           return ':';
2621         }
2622
2623       css_provider_push_scope (css_provider, SCOPE_VALUE);
2624       g_scanner_get_next_token (scanner);
2625
2626       if (scanner->token != G_TOKEN_IDENTIFIER)
2627         {
2628           g_free (prop);
2629           return G_TOKEN_IDENTIFIER;
2630         }
2631
2632       value_str = g_strstrip (scanner->value.v_identifier);
2633
2634       if (gtk_style_properties_lookup_property (prop, &prop_type, &parse_func))
2635         {
2636           GValue *val;
2637
2638           val = g_slice_new0 (GValue);
2639           g_value_init (val, prop_type);
2640
2641           if (prop_type == G_TYPE_STRING)
2642             {
2643               g_value_set_string (val, value_str);
2644               g_hash_table_insert (priv->cur_properties, prop, val);
2645             }
2646           else if ((parse_func && (parse_func) (value_str, val, &error)) ||
2647                    (!parse_func && css_provider_parse_value (css_provider, value_str, val)))
2648             g_hash_table_insert (priv->cur_properties, prop, val);
2649           else
2650             {
2651               if (error)
2652                 {
2653                   g_warning ("Error parsing property value: %s\n", error->message);
2654                   g_error_free (error);
2655                 }
2656
2657               g_value_unset (val);
2658               g_slice_free (GValue, val);
2659               g_free (prop);
2660
2661               return G_TOKEN_IDENTIFIER;
2662             }
2663         }
2664       else if (prop[0] == '-' &&
2665                g_ascii_isupper (prop[1]))
2666         {
2667           GValue *val;
2668
2669           val = g_slice_new0 (GValue);
2670           g_value_init (val, G_TYPE_STRING);
2671           g_value_set_string (val, value_str);
2672
2673           g_hash_table_insert (priv->cur_properties, prop, val);
2674         }
2675       else
2676         g_free (prop);
2677
2678       css_provider_pop_scope (css_provider);
2679       g_scanner_get_next_token (scanner);
2680
2681       if (scanner->token != ';')
2682         return ';';
2683
2684       g_scanner_get_next_token (scanner);
2685     }
2686
2687   if (scanner->token != G_TOKEN_RIGHT_CURLY)
2688     return G_TOKEN_RIGHT_CURLY;
2689
2690   css_provider_pop_scope (css_provider);
2691
2692   return G_TOKEN_NONE;
2693 }
2694
2695 static gboolean
2696 parse_stylesheet (GtkCssProvider *css_provider)
2697 {
2698   GtkCssProviderPrivate *priv;
2699
2700   priv = css_provider->priv;
2701   g_scanner_get_next_token (priv->scanner);
2702
2703   while (!g_scanner_eof (priv->scanner))
2704     {
2705       GTokenType expected_token;
2706
2707       css_provider_reset_parser (css_provider);
2708       expected_token = parse_rule (css_provider, priv->scanner);
2709
2710       if (expected_token != G_TOKEN_NONE)
2711         {
2712           g_scanner_unexp_token (priv->scanner, expected_token,
2713                                  NULL, NULL, NULL,
2714                                  "Error parsing style resource", FALSE);
2715
2716           while (!g_scanner_eof (priv->scanner) &&
2717                  priv->scanner->token != G_TOKEN_RIGHT_CURLY)
2718             g_scanner_get_next_token (priv->scanner);
2719         }
2720       else
2721         css_provider_commit (css_provider);
2722
2723       g_scanner_get_next_token (priv->scanner);
2724     }
2725
2726   return TRUE;
2727 }
2728
2729 /**
2730  * gtk_css_provider_load_from_data:
2731  * @css_provider: a #GtkCssProvider
2732  * @data: CSS data loaded in memory
2733  * @length: the length of @data in bytes, or -1 for NUL terminated strings
2734  * @error: (out) (allow-none): return location for a #GError, or %NULL
2735  *
2736  * Loads @data into @css_provider, making it clear any previously loaded
2737  * information.
2738  *
2739  * Returns: %TRUE if the data could be loaded.
2740  **/
2741 gboolean
2742 gtk_css_provider_load_from_data (GtkCssProvider *css_provider,
2743                                  const gchar    *data,
2744                                  gssize          length,
2745                                  GError         *error)
2746 {
2747   GtkCssProviderPrivate *priv;
2748
2749   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
2750   g_return_val_if_fail (data != NULL, FALSE);
2751
2752   priv = css_provider->priv;
2753
2754   if (length < 0)
2755     length = strlen (data);
2756
2757   if (priv->selectors_info->len > 0)
2758     g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
2759
2760   priv->scanner->input_name = "-";
2761   g_scanner_input_text (priv->scanner, data, (guint) length);
2762
2763   g_free (priv->filename);
2764   priv->filename = NULL;
2765
2766   parse_stylesheet (css_provider);
2767
2768   return TRUE;
2769 }
2770
2771 /**
2772  * gtk_css_provider_load_from_file:
2773  * @css_provider: a #GtkCssProvider
2774  * @file: #GFile pointing to a file to load
2775  * @error: (out) (allow-none): return location for a #GError, or %NULL
2776  *
2777  * Loads the data contained in @file into @css_provider, making it
2778  * clear any previously loaded information.
2779  *
2780  * Returns: %TRUE if the data could be loaded.
2781  **/
2782 gboolean
2783 gtk_css_provider_load_from_file (GtkCssProvider  *css_provider,
2784                                  GFile           *file,
2785                                  GError         **error)
2786 {
2787   GtkCssProviderPrivate *priv;
2788   GError *internal_error = NULL;
2789   gchar *data;
2790   gsize length;
2791
2792   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
2793   g_return_val_if_fail (G_IS_FILE (file), FALSE);
2794
2795   priv = css_provider->priv;
2796
2797   if (!g_file_load_contents (file, NULL,
2798                              &data, &length,
2799                              NULL, &internal_error))
2800     {
2801       g_propagate_error (error, internal_error);
2802       return FALSE;
2803     }
2804
2805   if (priv->selectors_info->len > 0)
2806     g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
2807
2808   g_free (priv->filename);
2809   priv->filename = g_file_get_path (file);
2810
2811   priv->scanner->input_name = priv->filename;
2812   g_scanner_input_text (priv->scanner, data, (guint) length);
2813
2814   parse_stylesheet (css_provider);
2815
2816   g_free (data);
2817
2818   return TRUE;
2819 }
2820
2821 static gboolean
2822 gtk_css_provider_load_from_path_internal (GtkCssProvider  *css_provider,
2823                                           const gchar     *path,
2824                                           gboolean         reset,
2825                                           GError         **error)
2826 {
2827   GtkCssProviderPrivate *priv;
2828   GError *internal_error = NULL;
2829   GMappedFile *mapped_file;
2830   const gchar *data;
2831   gsize length;
2832
2833   priv = css_provider->priv;
2834
2835   mapped_file = g_mapped_file_new (path, FALSE, &internal_error);
2836
2837   if (internal_error)
2838     {
2839       g_propagate_error (error, internal_error);
2840       return FALSE;
2841     }
2842
2843   length = g_mapped_file_get_length (mapped_file);
2844   data = g_mapped_file_get_contents (mapped_file);
2845
2846   /* FIXME: Set error */
2847   if (!data)
2848     return FALSE;
2849
2850   if (reset)
2851     {
2852       if (priv->selectors_info->len > 0)
2853         g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
2854
2855       g_free (priv->filename);
2856       priv->filename = g_strdup (path);
2857     }
2858
2859   priv->scanner->input_name = priv->filename;
2860   g_scanner_input_text (priv->scanner, data, (guint) length);
2861
2862   parse_stylesheet (css_provider);
2863
2864   g_mapped_file_unref (mapped_file);
2865
2866   return TRUE;
2867 }
2868
2869 /**
2870  * gtk_css_provider_load_from_path:
2871  * @css_provider: a #GtkCssProvider
2872  * @path: the path of a filename to load, in the GLib filename encoding
2873  * @error: (out) (allow-none): return location for a #GError, or %NULL
2874  *
2875  * Loads the data contained in @path into @css_provider, making it clear
2876  * any previously loaded information.
2877  *
2878  * Returns: %TRUE if the data could be loaded.
2879  **/
2880 gboolean
2881 gtk_css_provider_load_from_path (GtkCssProvider  *css_provider,
2882                                  const gchar     *path,
2883                                  GError         **error)
2884 {
2885   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
2886   g_return_val_if_fail (path != NULL, FALSE);
2887
2888   return gtk_css_provider_load_from_path_internal (css_provider, path,
2889                                                    TRUE, error);
2890 }
2891
2892 /**
2893  * gtk_css_provider_get_default:
2894  *
2895  * Returns the provider containing the style settings used as a
2896  * fallback for all widgets.
2897  *
2898  * Returns: (transfer none): The provider used for fallback styling.
2899  *          This memory is owned by GTK+, and you must not free it.
2900  **/
2901 GtkCssProvider *
2902 gtk_css_provider_get_default (void)
2903 {
2904   static GtkCssProvider *provider;
2905
2906   if (G_UNLIKELY (!provider))
2907     {
2908       const gchar *str =
2909         "@define-color fg_color #000; \n"
2910         "@define-color bg_color #dcdad5; \n"
2911         "@define-color text_color #000; \n"
2912         "@define-color base_color #fff; \n"
2913         "@define-color selected_bg_color #4b6983; \n"
2914         "@define-color selected_fg_color #fff; \n"
2915         "@define-color tooltip_bg_color #eee1b3; \n"
2916         "@define-color tooltip_fg_color #000; \n"
2917         "\n"
2918         "*,\n"
2919         "GtkTreeView > GtkButton {\n"
2920         "  background-color: @bg_color;\n"
2921         "  color: @fg_color;\n"
2922         "  border-color: shade (@bg_color, 0.7);\n"
2923         "  padding: 2 2; \n"
2924         "}\n"
2925         "\n"
2926         "*:prelight {\n"
2927         "  background-color: shade (@bg_color, 2.0);\n"
2928         "  color: shade (@fg_color, 1.3);\n"
2929         "}\n"
2930         "\n"
2931         "*:selected {\n"
2932         "  background-color: @selected_bg_color;\n"
2933         "  color: @selected_fg_color;\n"
2934         "}\n"
2935         "\n"
2936         "*:insensitive {\n"
2937         "  background-color: shade (@bg_color, 0.7);\n"
2938         "  color: shade (@fg_color, 0.7);\n"
2939         "}\n"
2940         "\n"
2941         "GtkTreeView, GtkIconView, GtkTextView {\n"
2942         "  background-color: @base_color;\n"
2943         "  color: @text_color;\n"
2944         "}\n"
2945         "\n"
2946         "GtkTreeView > row {\n"
2947         "  background-color: @base_color;\n"
2948         "  color: @text_color;\n"
2949         "}\n"
2950         "\n"
2951         "GtkTreeView > row:nth-child(odd) { \n"
2952         "  background-color: shade (@base_color, 0.93); \n"
2953         "}\n"
2954         "\n"
2955         ".tooltip {\n"
2956         "  background-color: @tooltip_bg_color; \n"
2957         "  color: @tooltip_fg_color; \n"
2958         "}\n"
2959         "\n"
2960         ".button,\n"
2961         ".slider {\n"
2962         "  border-style: outset; \n"
2963         "  border-width: 2; \n"
2964         "}\n"
2965         "\n"
2966         ".button:active {\n"
2967         "  background-color: shade (@bg_color, 0.7);\n"
2968         "  border-style: inset; \n"
2969         "}\n"
2970         "\n"
2971         ".button:prelight,\n"
2972         ".slider:prelight {\n"
2973         "  background-color: @selected_bg_color;\n"
2974         "  color: @selected_fg_color;\n"
2975         "  border-color: shade (@selected_bg_color, 0.7);\n"
2976         "}\n"
2977         "\n"
2978         ".trough {\n"
2979         "  border-style: inset;\n"
2980         "  border-width: 1;\n"
2981         "}\n"
2982         "\n"
2983         ".entry {\n"
2984         "  border-style: inset;\n"
2985         "  border-width: 2;\n"
2986         "  background-color: @base_color;\n"
2987         "  color: @text_color;\n"
2988         "}\n"
2989         "\n"
2990         ".progressbar,\n"
2991         ".entry.progressbar {\n"
2992         "  background-color: @selected_bg_color;\n"
2993         "  border-color: shade (@selected_bg_color, 0.7);\n"
2994         "}\n"
2995         "\n"
2996         ".check, .radio {\n"
2997         "  background-color: @base_color;\n"
2998         "  color: @text_color;\n"
2999         "}\n"
3000         "\n"
3001         ".menu.check,\n"
3002         ".menu.radio {\n"
3003         "  color: @fg_color;\n"
3004         "}\n"
3005         "\n"
3006         ".menu:hover {\n"
3007         "  background-color: @selected_bg_color;\n"
3008         "  border-style: none;\n"
3009         "}\n"
3010         "\n"
3011         ".viewport {\n"
3012         "  border-style: inset;\n"
3013         "  border-width: 2;\n"
3014         "}\n"
3015         "\n";
3016
3017       provider = gtk_css_provider_new ();
3018       gtk_css_provider_load_from_data (provider, str, -1, NULL);
3019     }
3020
3021   return provider;
3022 }
3023
3024 static gchar *
3025 css_provider_get_theme_dir (void)
3026 {
3027   const gchar *var;
3028   gchar *path;
3029
3030   var = g_getenv ("GTK_DATA_PREFIX");
3031
3032   if (var)
3033     path = g_build_filename (var, "share", "themes", NULL);
3034   else
3035     path = g_build_filename (GTK_DATA_PREFIX, "share", "themes", NULL);
3036
3037   return path;
3038 }
3039
3040 /**
3041  * gtk_css_provider_get_named:
3042  * @name: A theme name
3043  * @variant: variant to load, for example, "dark", or %NULL for the default
3044  *
3045  * 
3046  *
3047  * Returns: (transfer none): The provider used for fallback styling.
3048  *          This memory is owned by GTK+, and you must not free it.
3049  **/
3050 GtkCssProvider *
3051 gtk_css_provider_get_named (const gchar *name,
3052                             const gchar *variant)
3053 {
3054   static GHashTable *themes = NULL;
3055   GtkCssProvider *provider;
3056
3057   if (G_UNLIKELY (!themes))
3058     themes = g_hash_table_new (g_str_hash, g_str_equal);
3059
3060   provider = g_hash_table_lookup (themes, name);
3061
3062   if (!provider)
3063     {
3064       const gchar *home_dir;
3065       gchar *subpath, *path = NULL;
3066
3067       if (variant)
3068         subpath = g_strdup_printf ("gtk-%d.0" G_DIR_SEPARATOR_S "gtk-%s.css", GTK_MAJOR_VERSION, variant);
3069       else
3070         subpath = g_strdup_printf ("gtk-%d.0" G_DIR_SEPARATOR_S "gtk.css", GTK_MAJOR_VERSION);
3071
3072       /* First look in the users home directory
3073        */
3074       home_dir = g_get_home_dir ();
3075       if (home_dir)
3076         {
3077           path = g_build_filename (home_dir, ".themes", name, subpath, NULL);
3078
3079           if (!g_file_test (path, G_FILE_TEST_EXISTS))
3080             {
3081               g_free (path);
3082               path = NULL;
3083             }
3084         }
3085
3086       if (!path)
3087         {
3088           gchar *theme_dir = css_provider_get_theme_dir ();
3089           path = g_build_filename (theme_dir, name, subpath, NULL);
3090           g_free (theme_dir);
3091
3092           if (!g_file_test (path, G_FILE_TEST_EXISTS))
3093             {
3094               g_free (path);
3095               path = NULL;
3096             }
3097         }
3098
3099       if (path)
3100         {
3101           GtkCssProvider *provider;
3102           GError *error = NULL;
3103
3104           provider = gtk_css_provider_new ();
3105           gtk_css_provider_load_from_path (provider, path, &error);
3106
3107           if (error)
3108             {
3109               g_warning ("Could not load named theme \"%s\": %s", name, error->message);
3110               g_error_free (error);
3111
3112               g_object_unref (provider);
3113               provider = NULL;
3114             }
3115           else
3116             g_hash_table_insert (themes, g_strdup (name), provider);
3117         }
3118     }
3119
3120   return provider;
3121 }