]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssprovider.c
widgetpath: allow GTypes non-derived from GTK_TYPE_WIDGET
[~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 the #GtkStyleProvider interface.
40  * It is able to parse <ulink url="http://www.w3.org/TR/CSS2">CSS</ulink>-like
41  * input in order to style widgets.
42  *
43  * <refsect2 id="gtkcssprovider-files">
44  * <title>Default files</title>
45  * <para>
46  * An application can cause GTK+ to parse a specific CSS style sheet by
47  * calling gtk_css_provider_load_from_file() and adding the provider with
48  * gtk_style_context_add_provider() or gtk_style_context_add_provider_for_screen().
49  * In addition, certain files will be read when GTK+ is initialized. First,
50  * the file <filename><replaceable>XDG_CONFIG_HOME</replaceable>/gtk-3.0/gtk.css</filename>
51  * is loaded if it exists. Then, GTK+ tries to load
52  * <filename><replaceable>HOME</replaceable>/.themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
53  * falling back to
54  * <filename><replaceable>GTK_DATA_PREFIX</replaceable>/share/themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
55  * where <replaceable>theme-name</replaceable> is the name of the current theme
56  * (see the #GtkSettings:gtk-theme-name setting) and <replaceable>GTK_DATA_PREFIX</replaceable>
57  * is the prefix configured when GTK+ was compiled, unless overridden by the
58  * <envar>GTK_DATA_PREFIX</envar> environment variable.
59  * </para>
60  * </refsect2>
61  * <refsect2 id="gtkcssprovider-stylesheets">
62  * <title>Style sheets</title>
63  * <para>
64  * The basic structure of the style sheets understood by this provider is
65  * a series of statements, which are either rule sets or '@-rules', separated
66  * by whitespace.
67  * </para>
68  * <para>
69  * A rule set consists of a selector and a declaration block, which is
70  * a series of declarations enclosed in curly braces ({ and }). The
71  * declarations are separated by semicolons (;). Multiple selectors can
72  * share the same declaration block, by putting all the separators in
73  * front of the block, separated by commas.
74  * </para>
75  * <example><title>A rule set with two selectors</title>
76  * <programlisting language="text">
77  * GtkButton, GtkEntry {
78  *     color: &num;ff00ea;
79  *     font: Comic Sans 12
80  * }
81  * </programlisting>
82  * </example>
83  * </refsect2>
84  * <refsect2 id="gtkcssprovider-selectors">
85  * <title>Selectors</title>
86  * <para>
87  * Selectors work very similar to the way they do in CSS, with widget class
88  * names taking the role of element names, and widget names taking the role
89  * of IDs. When used in a selector, widget names must be prefixed with a
90  * '&num;' character. The '*' character represents the so-called universal
91  * selector, which matches any widget.
92  * </para>
93  * <para>
94  * To express more complicated situations, selectors can be combined in
95  * various ways:
96  * <itemizedlist>
97  * <listitem><para>To require that a widget satisfies several conditions,
98  *   combine several selectors into one by concatenating them. E.g.
99  *   <literal>GtkButton&num;button1</literal> matches a GtkButton widget
100  *   with the name button1.</para></listitem>
101  * <listitem><para>To only match a widget when it occurs inside some other
102  *   widget, write the two selectors after each other, separated by whitespace.
103  *   E.g. <literal>GtkToolBar GtkButton</literal> matches GtkButton widgets
104  *   that occur inside a GtkToolBar.</para></listitem>
105  * <listitem><para>In the previous example, the GtkButton is matched even
106  *   if it occurs deeply nested inside the toolbar. To restrict the match
107  *   to direct children of the parent widget, insert a '>' character between
108  *   the two selectors. E.g. <literal>GtkNotebook > GtkLabel</literal> matches
109  *   GtkLabel widgets that are direct children of a GtkNotebook.</para></listitem>
110  * </itemizedlist>
111  * </para>
112  * <example>
113  * <title>Widget classes and names in selectors</title>
114  * <programlisting language="text">
115  * /&ast; Theme labels that are descendants of a window &ast;/
116  * GtkWindow GtkLabel {
117  *     background-color: &num;898989
118  * }
119  *
120  * /&ast; Theme notebooks, and anything that's within these &ast;/
121  * GtkNotebook {
122  *     background-color: &num;a939f0
123  * }
124  *
125  * /&ast; Theme combo boxes, and entries that
126  *  are direct children of a notebook &ast;/
127  * GtkComboBox,
128  * GtkNotebook > GtkEntry {
129  *     color: @fg_color;
130  *     background-color: &num;1209a2
131  * }
132  *
133  * /&ast; Theme any widget within a GtkBin &ast;/
134  * GtkBin * {
135  *     font-name: Sans 20
136  * }
137  *
138  * /&ast; Theme a label named title-label &ast;/
139  * GtkLabel&num;title-label {
140  *     font-name: Sans 15
141  * }
142  *
143  * /&ast; Theme any widget named main-entry &ast;/
144  * &num;main-entry {
145  *     background-color: &num;f0a810
146  * }
147  * </programlisting>
148  * </example>
149  * <para>
150  * Widgets may also define style classes, which can be used for matching.
151  * When used in a selector, style classes must be prefixed with a '.'
152  * character.
153  * </para>
154  * <para>
155  * Refer to the documentation of individual widgets to learn which
156  * style classes they define and see <xref linkend="gtkstylecontext-classes"/>
157  * for a list of all style classes used by GTK+ widgets.
158  * </para>
159  * <example>
160  * <title>Style classes in selectors</title>
161  * <programlisting language="text">
162  * /&ast; Theme all widgets defining the class entry &ast;/
163  * .entry {
164  *     color: &num;39f1f9;
165  * }
166  *
167  * /&ast; Theme spinbuttons' entry &ast;/
168  * GtkSpinButton.entry {
169  *     color: &num;900185
170  * }
171  * </programlisting>
172  * </example>
173  * <para>
174  * In complicated widgets like e.g. a GtkNotebook, it may be desirable
175  * to style different parts of the widget differently. To make this
176  * possible, container widgets may define regions, whose names
177  * may be used for matching in selectors.
178  * </para>
179  * <para>
180  * Some containers allow to further differentiate between regions by
181  * applying so-called pseudo-classes to the region. For example, the
182  * tab region in GtkNotebook allows to single out the first or last
183  * tab by using the :first-child or :last-child pseudo-class.
184  * When used in selectors, pseudo-classes must be prefixed with a
185  * ':' character.
186  * </para>
187  * <para>
188  * Refer to the documentation of individual widgets to learn which
189  * regions and pseudo-classes they define and see
190  * <xref linkend="gtkstylecontext-classes"/> for a list of all regions
191  * used by GTK+ widgets.
192  * </para>
193  * <example>
194  * <title>Regions in selectors</title>
195  * <programlisting language="text">
196  * /&ast; Theme any label within a notebook &ast;/
197  * GtkNotebook GtkLabel {
198  *     color: &num;f90192;
199  * }
200  *
201  * /&ast; Theme labels within notebook tabs &ast;/
202  * GtkNotebook tab GtkLabel {
203  *     color: &num;703910;
204  * }
205  *
206  * /&ast; Theme labels in the any first notebook
207  *  tab, both selectors are equivalent &ast;/
208  * GtkNotebook tab:nth-child(first) GtkLabel,
209  * GtkNotebook tab:first-child GtkLabel {
210  *     color: &num;89d012;
211  * }
212  * </programlisting>
213  * </example>
214  * <para>
215  * Another use of pseudo-classes is to match widgets depending on their
216  * state. This is conceptually similar to the :hover, :active or :focus
217  * pseudo-classes in CSS. The available pseudo-classes for widget states
218  * are :active, :prelight (or :hover), :insensitive, :selected, :focused
219  * and :inconsistent.
220  * </para>
221  * <example>
222  * <title>Styling specific widget states</title>
223  * <programlisting language="text">
224  * /&ast; Theme active (pressed) buttons &ast;/
225  * GtkButton:active {
226  *     background-color: &num;0274d9;
227  * }
228  *
229  * /&ast; Theme buttons with the mouse pointer on it,
230  *    both are equivalent &ast;/
231  * GtkButton:hover,
232  * GtkButton:prelight {
233  *     background-color: &num;3085a9;
234  * }
235  *
236  * /&ast; Theme insensitive widgets, both are equivalent &ast;/
237  * :insensitive,
238  * *:insensitive {
239  *     background-color: &num;320a91;
240  * }
241  *
242  * /&ast; Theme selection colors in entries &ast;/
243  * GtkEntry:selected {
244  *     background-color: &num;56f9a0;
245  * }
246  *
247  * /&ast; Theme focused labels &ast;/
248  * GtkLabel:focused {
249  *     background-color: &num;b4940f;
250  * }
251  *
252  * /&ast; Theme inconsistent checkbuttons &ast;/
253  * GtkCheckButton:inconsistent {
254  *     background-color: &num;20395a;
255  * }
256  * </programlisting>
257  * </example>
258  * <para>
259  * Widget state pseudoclasses may only apply to the last element
260  * in a selector.
261  * </para>
262  * <para>
263  * To determine the effective style for a widget, all the matching rule
264  * sets are merged. As in CSS, rules apply by specificity, so the rules
265  * whose selectors more closely match a widget path will take precedence
266  * over the others.
267  * </para>
268  * </refsect2>
269  * <refsect2 id="gtkcssprovider-rules">
270  * <title>&commat; Rules</title>
271  * <para>
272  * GTK+'s CSS supports the &commat;import rule, in order to load another
273  * CSS style sheet in addition to the currently parsed one.
274  * </para>
275  * <example>
276  * <title>Using the &commat;import rule</title>
277  * <programlisting language="text">
278  * &commat;import url ("path/to/common.css");
279  * </programlisting>
280  * </example>
281  * <para>
282  * GTK+ also supports an additional &commat;define-color rule, in order
283  * to define a color name which may be used instead of color numeric
284  * representations. Also see the #GtkSettings:gtk-color-scheme setting
285  * for a way to override the values of these named colors.
286  * </para>
287  * <example>
288  * <title>Defining colors</title>
289  * <programlisting language="text">
290  * &commat;define-color bg_color &num;f9a039;
291  *
292  * &ast; {
293  *     background-color: &commat;bg_color;
294  * }
295  * </programlisting>
296  * </example>
297  * </refsect2>
298  * <refsect2 id="gtkcssprovider-symbolic-colors">
299  * <title>Symbolic colors</title>
300  * <para>
301  * Besides being able to define color names, the CSS parser is also able
302  * to read different color expressions, which can also be nested, providing
303  * a rich language to define colors which are derived from a set of base
304  * colors.
305  * </para>
306  * <example>
307  * <title>Using symbolic colors</title>
308  * <programlisting language="text">
309  * &commat;define-color entry-color shade (&commat;bg_color, 0.7);
310  *
311  * GtkEntry {
312  *     background-color: @entry-color;
313  * }
314  *
315  * GtkEntry:focused {
316  *     background-color: mix (&commat;entry-color,
317  *                            shade (&num;fff, 0.5),
318  *                            0.8);
319  * }
320  * </programlisting>
321  * </example>
322  * <para>
323  *   The various ways to express colors in GTK+ CSS are:
324  * </para>
325  * <informaltable>
326  *   <tgroup cols="3">
327  *     <thead>
328  *       <row>
329  *         <entry>Syntax</entry>
330  *         <entry>Explanation</entry>
331  *         <entry>Examples</entry>
332  *       </row>
333  *     </thead>
334  *     <tbody>
335  *       <row>
336  *         <entry>rgb(@r, @g, @b)</entry>
337  *         <entry>An opaque color; @r, @g, @b can be either integers between
338  *                0 and 255 or percentages</entry>
339  *         <entry><literallayout>rgb(128, 10, 54)
340  * rgb(20%, 30%, 0%)</literallayout></entry>
341  *       </row>
342  *       <row>
343  *         <entry>rgba(@r, @g, @b, @a)</entry>
344  *         <entry>A translucent color; @r, @g, @b are as in the previous row,
345  *                @a is a floating point number between 0 and 1</entry>
346  *         <entry><literallayout>rgba(255, 255, 0, 0.5)</literallayout></entry>
347  *       </row>
348  *       <row>
349  *         <entry>&num;@xxyyzz</entry>
350  *         <entry>An opaque color; @xx, @yy, @zz are hexadecimal numbers
351  *                specifying @r, @g, @b variants with between 1 and 4
352  *                hexadecimal digits per component are allowed</entry>
353  *         <entry><literallayout>&num;ff12ab
354  * &num;f0c</literallayout></entry>
355  *       </row>
356  *       <row>
357  *         <entry>&commat;name</entry>
358  *         <entry>Reference to a color that has been defined with
359  *                &commat;define-color
360  *         </entry>
361  *         <entry>&commat;bg_color</entry>
362  *       </row>
363  *       <row>
364  *         <entry>mix(@color1, @color2, @f)</entry>
365  *         <entry>A linear combination of @color1 and @color2. @f is a
366  *                floating point number between 0 and 1.</entry>
367  *         <entry><literallayout>mix(&num;ff1e0a, &commat;bg_color, 0.8)</literallayout></entry>
368  *       </row>
369  *       <row>
370  *         <entry>shade(@color, @f)</entry>
371  *         <entry>A lighter or darker variant of @color. @f is a
372  *                floating point number.
373  *         </entry>
374  *         <entry>shade(&commat;fg_color, 0.5)</entry>
375  *       </row>
376  *       <row>
377  *         <entry>lighter(@color)</entry>
378  *         <entry>A lighter variant of @color</entry>
379  *       </row>
380  *       <row>
381  *         <entry>darker(@color)</entry>
382  *         <entry>A darker variant of @color</entry>
383  *       </row>
384  *     </tbody>
385  *   </tgroup>
386  * </informaltable>
387  * </refsect2>
388  * <refsect2 id="gtkcssprovider-gradients">
389  * <title>Gradients</title>
390  * <para>
391  * Linear or radial Gradients can be used as background images.
392  * </para>
393  * <para>
394  * A linear gradient along the line from (@start_x, @start_y) to
395  * (@end_x, @end_y) is specified using the syntax
396  * <literallayout>-gtk-gradient (linear,
397  *               @start_x @start_y, @end_x @end_y,
398  *               color-stop (@position, @color),
399  *               ...)</literallayout>
400  * where @start_x and @end_x can be either a floating point number between
401  * 0 and 1 or one of the special values 'left', 'right' or 'center', @start_y
402  * and @end_y can be either a floating point number between 0 and 1 or one
403  * of the special values 'top', 'bottom' or 'center', @position is a floating
404  * point number between 0 and 1 and @color is a color expression (see above).
405  * The color-stop can be repeated multiple times to add more than one color
406  * stop. 'from (@color)' and 'to (@color)' can be used as abbreviations for
407  * color stops with position 0 and 1, respectively.
408  * </para>
409  * <example>
410  * <title>A linear gradient</title>
411  * <inlinegraphic fileref="gradient1.png" format="PNG"/>
412  * <para>This gradient was specified with
413  * <literallayout>-gtk-gradient (linear,
414  *                left top, right bottom,
415  *                from(&commat;yellow), to(&commat;blue))</literallayout></para>
416  * </example>
417  * <example>
418  * <title>Another linear gradient</title>
419  * <inlinegraphic fileref="gradient2.png" format="PNG"/>
420  * <para>This gradient was specified with
421  * <literallayout>-gtk-gradient (linear,
422  *                0 0, 0 1,
423  *                color-stop(0, &commat;yellow),
424  *                color-stop(0.2, &commat;blue),
425  *                color-stop(1, &num;0f0))</literallayout></para>
426  * </example>
427  * <para>
428  * A radial gradient along the two circles defined by (@start_x, @start_y,
429  * @start_radius) and (@end_x, @end_y, @end_radius) is specified using the
430  * syntax
431  * <literallayout>-gtk-gradient (radial,
432  *                @start_x @start_y, @start_radius,
433  *                @end_x @end_y, @end_radius,
434  *                color-stop (@position, @color),
435  *                ...)</literallayout>
436  * where @start_radius and @end_radius are floating point numbers and
437  * the other parameters are as before.
438  * </para>
439  * <example>
440  * <title>A radial gradient</title>
441  * <inlinegraphic fileref="gradient3.png" format="PNG"/>
442  * <para>This gradient was specified with
443  * <literallayout>-gtk-gradient (radial,
444  *                center center, 0,
445  *                center center, 1,
446  *                from(&commat;yellow), to(&commat;green))</literallayout></para>
447  * </example>
448  * <example>
449  * <title>Another radial gradient</title>
450  * <inlinegraphic fileref="gradient4.png" format="PNG"/>
451  * <para>This gradient was specified with
452  * <literallayout>-gtk-gradient (radial,
453  *                0.4 0.4, 0.1,
454  *                0.6 0.6, 0.7,
455  *                color-stop (0, &num;f00),
456  *                color-stop (0.1, &num;a0f),
457  *                color-stop (0.2, &commat;yellow),
458  *                color-stop (1, &commat;green))</literallayout></para>
459  * </example>
460  * </refsect2>
461  * <refsect2 id="gtkcssprovider-slices">
462  * <title>Border images</title>
463  * <para>
464  * Images can be used in 'slices' for the purpose of creating scalable
465  * borders.
466  * </para>
467  * <inlinegraphic fileref="slices.png" format="PNG"/>
468  * <para>
469  * The syntax for specifying border images of this kind is:
470  * <literallayout>url(@path) @top @right @bottom @left [repeat|stretch]? [repeat|stretch]?</literallayout>
471  * The sizes of the 'cut off' portions are specified
472  * with the @top, @right, @bottom and @left parameters.
473  * The 'middle' sections can be repeated or stretched to create
474  * the desired effect, by adding the 'repeat' or 'stretch' options after
475  * the dimensions. If two options are specified, the first one affects
476  * the horizontal behaviour and the second one the vertical behaviour.
477  * If only one option is specified, it affects both.
478  * </para>
479  * <example>
480  * <title>A border image</title>
481  * <inlinegraphic fileref="border1.png" format="PNG"/>
482  * <para>This border image was specified with
483  * <literallayout>url("gradient1.png") 10 10 10 10</literallayout>
484  * </para>
485  * </example>
486  * <example>
487  * <title>A repeating border image</title>
488  * <inlinegraphic fileref="border2.png" format="PNG"/>
489  * <para>This border image was specified with
490  * <literallayout>url("gradient1.png") 10 10 10 10 repeat</literallayout>
491  * </para>
492  * </example>
493  * <example>
494  * <title>A stretched border image</title>
495  * <inlinegraphic fileref="border3.png" format="PNG"/>
496  * <para>This border image was specified with
497  * <literallayout>url("gradient1.png") 10 10 10 10 stretch</literallayout>
498  * </para>
499  * </example>
500  * </refsect2>
501  * <refsect2 id="gtkcssprovider-transitions">
502  * <para>Styles can specify transitions that will be used to create a gradual
503  * change in the appearance when a widget state changes. The following
504  * syntax is used to specify transitions:
505  * <literallayout>@duration [s|ms] [linear|ease|ease-in|ease-out|ease-in-out] [loop]?</literallayout>
506  * The @duration is the amount of time that the animation will take for
507  * a complete cycle from start to end. If the loop option is given, the
508  * animation will be repated until the state changes again.
509  * The option after the duration determines the transition function from a
510  * small set of predefined functions.
511  * <figure><title>Linear transition</title>
512  * <graphic fileref="linear.png" format="PNG"/>
513  * </figure>
514  * <figure><title>Ease transition</title>
515  * <graphic fileref="ease.png" format="PNG"/>
516  * </figure>
517  * <figure><title>Ease-in-out transition</title>
518  * <graphic fileref="ease-in-out.png" format="PNG"/>
519  * </figure>
520  * <figure><title>Ease-in transition</title>
521  * <graphic fileref="ease-in.png" format="PNG"/>
522  * </figure>
523  * <figure><title>Ease-out transition</title>
524  * <graphic fileref="ease-out.png" format="PNG"/>
525  * </figure>
526  * </para>
527  * </refsect2>
528  * <refsect2 id="gtkcssprovider-properties">
529  * <title>Supported properties</title>
530  * <para>
531  * Properties are the part that differ the most to common CSS,
532  * not all properties are supported (some are planned to be
533  * supported eventually, some others are meaningless or don't
534  * map intuitively in a widget based environment).
535  * </para>
536  * <para>
537  * There is also a difference in shorthand properties, for
538  * example in common CSS it is fine to define a font through
539  * the different @font-family, @font-style, @font-size
540  * properties, meanwhile in GTK+'s CSS only the canonical
541  * @font property is supported.
542  * </para>
543  * <para>
544  * The currently supported properties are:
545  * </para>
546  * <informaltable>
547  *   <tgroup cols="4">
548  *     <thead>
549  *       <row>
550  *         <entry>Property name</entry>
551  *         <entry>Syntax</entry>
552  *         <entry>Maps to</entry>
553  *         <entry>Examples</entry>
554  *       </row>
555  *     </thead>
556  *     <tbody>
557  *       <row>
558  *         <entry>engine</entry>
559  *         <entry>engine-name</entry>
560  *         <entry>#GtkThemingEngine</entry>
561  *         <entry>engine: clearlooks;</entry>
562  *       </row>
563  *       <row>
564  *         <entry>background-color</entry>
565  *         <entry morerows="2">color (see above)</entry>
566  *         <entry morerows="2">#GdkRGBA</entry>
567  *         <entry morerows="2"><literallayout>background-color: &num;fff;
568  * color: &amp;color1;
569  * background-color: shade (&amp;color1, 0.5);
570  * color: mix (&amp;color1, &num;f0f, 0.8);</literallayout>
571  *         </entry>
572  *       </row>
573  *       <row>
574  *         <entry>color</entry>
575  *       </row>
576  *       <row>
577  *         <entry>border-color</entry>
578  *       </row>
579  *       <row>
580  *         <entry>font</entry>
581  *         <entry>@family [@style] [@size]</entry>
582  *         <entry>#PangoFontDescription</entry>
583  *         <entry>font: Sans 15;</entry>
584  *       </row>
585  *       <row>
586  *         <entry>margin</entry>
587  *         <entry morerows="1"><literallayout>@width
588  * @vertical_width @horizontal_width
589  * @top_width @horizontal_width @bottom_width
590  * @top_width @right_width @bottom_width @left_width</literallayout>
591  *         </entry>
592  *         <entry morerows="1">#GtkBorder</entry>
593  *         <entry morerows="1"><literallayout>margin: 5;
594  * margin: 5 10;
595  * margin: 5 10 3;
596  * margin: 5 10 3 5;</literallayout>
597  *         </entry>
598  *       </row>
599  *       <row>
600  *         <entry>padding</entry>
601  *       </row>
602  *       <row>
603  *         <entry>background-image</entry>
604  *         <entry><literallayout>gradient (see above) or
605  * url(@path)</literallayout></entry>
606  *         <entry>#cairo_pattern_t</entry>
607  *         <entry><literallayout>-gtk-gradient (linear,
608  *                left top, right top,
609  *                from (&num;fff), to (&num;000));
610  * -gtk-gradient (linear, 0.0 0.5, 0.5 1.0,
611  *                from (&num;fff),
612  *                color-stop (0.5, &num;f00),
613  *                to (&num;000));
614  * -gtk-gradient (radial,
615  *                center center, 0.2,
616  *                center center, 0.8,
617  *                color-stop (0.0, &num;fff),
618  *                color-stop (1.0, &num;000));
619  * url ('background.png');</literallayout>
620  *         </entry>
621  *       </row>
622  *       <row>
623  *         <entry>border-width</entry>
624  *         <entry>integer</entry>
625  *         <entry>#gint</entry>
626  *         <entry>border-width: 5;</entry>
627  *       </row>
628  *       <row>
629  *         <entry>border-radius</entry>
630  *         <entry>integer</entry>
631  *         <entry>#gint</entry>
632  *         <entry>border-radius: 5;</entry>
633  *       </row>
634  *       <row>
635  *         <entry>border-style</entry>
636  *         <entry>[none|solid|inset|outset]</entry>
637  *         <entry>#GtkBorderStyle</entry>
638  *         <entry>border-style: solid;</entry>
639  *       </row>
640  *       <row>
641  *         <entry>border-image</entry>
642  *         <entry><literallayout>border image (see above)</literallayout></entry>
643  *         <entry>internal use only</entry>
644  *         <entry><literallayout>border-image: url("/path/to/image.png") 3 4 3 4 stretch;
645  * border-image: url("/path/to/image.png") 3 4 4 3 repeat stretch;</literallayout>
646  *         </entry>
647  *       </row>
648  *       <row>
649  *         <entry>transition</entry>
650  *         <entry>transition (see above)</entry>
651  *         <entry>internal use only</entry>
652  *         <entry><literallayout>transition: 150ms ease-in-out;
653  * transition: 1s linear loop;</literallayout>
654  *         </entry>
655  *       </row>
656  *     </tbody>
657  *   </tgroup>
658  * </informaltable>
659  * <para>
660  * GtkThemingEngines can register their own, engine-specific style properties
661  * with the function gtk_theming_engine_register_property(). These properties
662  * can be set in CSS like other properties, using a name of the form
663  * <literallayout>-<replaceable>namespace</replaceable>-<replaceable>name</replaceable></literallayout>, where <replaceable>namespace</replaceable> is typically
664  * the name of the theming engine, and <replaceable>name</replaceable> is the
665  * name of the property. Style properties that have been registered by widgets
666  * using gtk_widget_class_install_style_property() can also be set in this
667  * way, using the widget class name for <replaceable>namespace</replaceable>.
668  * </para>
669  * <example>
670  * <title>Using engine-specific style properties</title>
671  * <programlisting>
672  * * {
673  *     engine: clearlooks;
674  *     border-radius: 4;
675  *     -GtkPaned-handle-size: 6;
676  *     -clearlooks-colorize-scrollbar: false;
677  * }
678  * </programlisting>
679  * </example>
680  * </refsect2>
681  */
682
683 typedef struct GtkCssProviderPrivate GtkCssProviderPrivate;
684 typedef struct SelectorElement SelectorElement;
685 typedef struct SelectorPath SelectorPath;
686 typedef struct SelectorStyleInfo SelectorStyleInfo;
687 typedef enum SelectorElementType SelectorElementType;
688 typedef enum CombinatorType CombinatorType;
689 typedef enum ParserScope ParserScope;
690 typedef enum ParserSymbol ParserSymbol;
691
692 enum SelectorElementType {
693   SELECTOR_TYPE_NAME,
694   SELECTOR_NAME,
695   SELECTOR_GTYPE,
696   SELECTOR_REGION,
697   SELECTOR_CLASS,
698   SELECTOR_GLOB
699 };
700
701 enum CombinatorType {
702   COMBINATOR_DESCENDANT, /* No direct relation needed */
703   COMBINATOR_CHILD       /* Direct child */
704 };
705
706 struct SelectorElement
707 {
708   SelectorElementType elem_type;
709   CombinatorType combinator;
710
711   union
712   {
713     GQuark name;
714     GType type;
715
716     struct
717     {
718       GQuark name;
719       GtkRegionFlags flags;
720     } region;
721   };
722 };
723
724 struct SelectorPath
725 {
726   GSList *elements;
727   GtkStateFlags state;
728   guint ref_count;
729 };
730
731 struct SelectorStyleInfo
732 {
733   SelectorPath *path;
734   GHashTable *style;
735 };
736
737 struct GtkCssProviderPrivate
738 {
739   GScanner *scanner;
740   gchar *filename;
741
742   const gchar *buffer;
743   const gchar *value_pos;
744
745   GHashTable *symbolic_colors;
746
747   GPtrArray *selectors_info;
748
749   /* Current parser state */
750   GSList *state;
751   GSList *cur_selectors;
752   GHashTable *cur_properties;
753 };
754
755 enum ParserScope {
756   SCOPE_SELECTOR,
757   SCOPE_PSEUDO_CLASS,
758   SCOPE_NTH_CHILD,
759   SCOPE_DECLARATION,
760   SCOPE_VALUE
761 };
762
763 /* Extend GtkStateType, since these
764  * values are also used as symbols
765  */
766 enum ParserSymbol {
767   /* Scope: pseudo-class */
768   SYMBOL_NTH_CHILD = GTK_STATE_FOCUSED + 1,
769   SYMBOL_FIRST_CHILD,
770   SYMBOL_LAST_CHILD,
771   SYMBOL_SORTED_CHILD,
772
773   /* Scope: nth-child */
774   SYMBOL_NTH_CHILD_EVEN,
775   SYMBOL_NTH_CHILD_ODD,
776   SYMBOL_NTH_CHILD_FIRST,
777   SYMBOL_NTH_CHILD_LAST
778 };
779
780 static void gtk_css_provider_finalize (GObject *object);
781 static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
782
783 static void scanner_apply_scope (GScanner    *scanner,
784                                  ParserScope  scope);
785 static gboolean css_provider_parse_value (GtkCssProvider  *css_provider,
786                                           const gchar     *value_str,
787                                           GValue          *value,
788                                           GError         **error);
789 static gboolean gtk_css_provider_load_from_path_internal (GtkCssProvider  *css_provider,
790                                                           const gchar     *path,
791                                                           gboolean         reset,
792                                                           GError         **error);
793
794 GQuark
795 gtk_css_provider_error_quark (void)
796 {
797   return g_quark_from_static_string ("gtk-css-provider-error-quark");
798 }
799
800 G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0,
801                         G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
802                                                gtk_css_style_provider_iface_init));
803
804 static void
805 gtk_css_provider_class_init (GtkCssProviderClass *klass)
806 {
807   GObjectClass *object_class = G_OBJECT_CLASS (klass);
808
809   object_class->finalize = gtk_css_provider_finalize;
810
811   g_type_class_add_private (object_class, sizeof (GtkCssProviderPrivate));
812 }
813
814 static SelectorPath *
815 selector_path_new (void)
816 {
817   SelectorPath *path;
818
819   path = g_slice_new0 (SelectorPath);
820   path->ref_count = 1;
821
822   return path;
823 }
824
825 static SelectorPath *
826 selector_path_ref (SelectorPath *path)
827 {
828   path->ref_count++;
829   return path;
830 }
831
832 static void
833 selector_path_unref (SelectorPath *path)
834 {
835   path->ref_count--;
836
837   if (path->ref_count > 0)
838     return;
839
840   while (path->elements)
841     {
842       g_slice_free (SelectorElement, path->elements->data);
843       path->elements = g_slist_delete_link (path->elements, path->elements);
844     }
845
846   g_slice_free (SelectorPath, path);
847 }
848
849 static void
850 selector_path_prepend_type (SelectorPath *path,
851                             const gchar  *type_name)
852 {
853   SelectorElement *elem;
854   GType type;
855
856   elem = g_slice_new (SelectorElement);
857   elem->combinator = COMBINATOR_DESCENDANT;
858   type = g_type_from_name (type_name);
859
860   if (type == G_TYPE_INVALID)
861     {
862       elem->elem_type = SELECTOR_TYPE_NAME;
863       elem->name = g_quark_from_string (type_name);
864     }
865   else
866     {
867       elem->elem_type = SELECTOR_GTYPE;
868       elem->type = type;
869     }
870
871   path->elements = g_slist_prepend (path->elements, elem);
872 }
873
874 static void
875 selector_path_prepend_glob (SelectorPath *path)
876 {
877   SelectorElement *elem;
878
879   elem = g_slice_new (SelectorElement);
880   elem->elem_type = SELECTOR_GLOB;
881   elem->combinator = COMBINATOR_DESCENDANT;
882
883   path->elements = g_slist_prepend (path->elements, elem);
884 }
885
886 static void
887 selector_path_prepend_region (SelectorPath   *path,
888                               const gchar    *name,
889                               GtkRegionFlags  flags)
890 {
891   SelectorElement *elem;
892
893   elem = g_slice_new (SelectorElement);
894   elem->combinator = COMBINATOR_DESCENDANT;
895   elem->elem_type = SELECTOR_REGION;
896
897   elem->region.name = g_quark_from_string (name);
898   elem->region.flags = flags;
899
900   path->elements = g_slist_prepend (path->elements, elem);
901 }
902
903 static void
904 selector_path_prepend_name (SelectorPath *path,
905                             const gchar  *name)
906 {
907   SelectorElement *elem;
908
909   elem = g_slice_new (SelectorElement);
910   elem->combinator = COMBINATOR_DESCENDANT;
911   elem->elem_type = SELECTOR_NAME;
912
913   elem->name = g_quark_from_string (name);
914
915   path->elements = g_slist_prepend (path->elements, elem);
916 }
917
918 static void
919 selector_path_prepend_class (SelectorPath *path,
920                              const gchar  *name)
921 {
922   SelectorElement *elem;
923
924   elem = g_slice_new (SelectorElement);
925   elem->combinator = COMBINATOR_DESCENDANT;
926   elem->elem_type = SELECTOR_CLASS;
927
928   elem->name = g_quark_from_string (name);
929
930   path->elements = g_slist_prepend (path->elements, elem);
931 }
932
933 static void
934 selector_path_prepend_combinator (SelectorPath   *path,
935                                   CombinatorType  combinator)
936 {
937   SelectorElement *elem;
938
939   g_assert (path->elements != NULL);
940
941   /* It is actually stored in the last element */
942   elem = path->elements->data;
943   elem->combinator = combinator;
944 }
945
946 static gint
947 selector_path_depth (SelectorPath *path)
948 {
949   return g_slist_length (path->elements);
950 }
951
952 static SelectorStyleInfo *
953 selector_style_info_new (SelectorPath *path)
954 {
955   SelectorStyleInfo *info;
956
957   info = g_slice_new0 (SelectorStyleInfo);
958   info->path = selector_path_ref (path);
959
960   return info;
961 }
962
963 static void
964 selector_style_info_free (SelectorStyleInfo *info)
965 {
966   if (info->style)
967     g_hash_table_unref (info->style);
968
969   if (info->path)
970     selector_path_unref (info->path);
971 }
972
973 static void
974 selector_style_info_set_style (SelectorStyleInfo *info,
975                                GHashTable        *style)
976 {
977   if (info->style)
978     g_hash_table_unref (info->style);
979
980   if (style)
981     info->style = g_hash_table_ref (style);
982   else
983     info->style = NULL;
984 }
985
986 static GScanner *
987 create_scanner (void)
988 {
989   GScanner *scanner;
990
991   scanner = g_scanner_new (NULL);
992
993   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "active", GUINT_TO_POINTER (GTK_STATE_ACTIVE));
994   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "prelight", GUINT_TO_POINTER (GTK_STATE_PRELIGHT));
995   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "hover", GUINT_TO_POINTER (GTK_STATE_PRELIGHT));
996   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "selected", GUINT_TO_POINTER (GTK_STATE_SELECTED));
997   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "insensitive", GUINT_TO_POINTER (GTK_STATE_INSENSITIVE));
998   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "inconsistent", GUINT_TO_POINTER (GTK_STATE_INCONSISTENT));
999   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "focused", GUINT_TO_POINTER (GTK_STATE_FOCUSED));
1000   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "focus", GUINT_TO_POINTER (GTK_STATE_FOCUSED));
1001
1002   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "nth-child", GUINT_TO_POINTER (SYMBOL_NTH_CHILD));
1003   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "first-child", GUINT_TO_POINTER (SYMBOL_FIRST_CHILD));
1004   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "last-child", GUINT_TO_POINTER (SYMBOL_LAST_CHILD));
1005   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "sorted", GUINT_TO_POINTER (SYMBOL_SORTED_CHILD));
1006
1007   g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "even", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_EVEN));
1008   g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "odd", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_ODD));
1009   g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "first", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_FIRST));
1010   g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "last", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_LAST));
1011
1012   scanner_apply_scope (scanner, SCOPE_SELECTOR);
1013
1014   return scanner;
1015 }
1016
1017 static void
1018 gtk_css_provider_init (GtkCssProvider *css_provider)
1019 {
1020   GtkCssProviderPrivate *priv;
1021
1022   priv = css_provider->priv = G_TYPE_INSTANCE_GET_PRIVATE (css_provider,
1023                                                            GTK_TYPE_CSS_PROVIDER,
1024                                                            GtkCssProviderPrivate);
1025
1026   priv->selectors_info = g_ptr_array_new_with_free_func ((GDestroyNotify) selector_style_info_free);
1027   priv->scanner = create_scanner ();
1028
1029   priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal,
1030                                                  (GDestroyNotify) g_free,
1031                                                  (GDestroyNotify) gtk_symbolic_color_unref);
1032 }
1033
1034 typedef struct ComparePathData ComparePathData;
1035
1036 struct ComparePathData
1037 {
1038   guint64 score;
1039   SelectorPath *path;
1040   GSList *iter;
1041 };
1042
1043 static gboolean
1044 compare_selector_element (GtkWidgetPath   *path,
1045                           guint            index,
1046                           SelectorElement *elem,
1047                           guint8          *score)
1048 {
1049   *score = 0;
1050
1051   if (elem->elem_type == SELECTOR_TYPE_NAME)
1052     {
1053       const gchar *type_name;
1054       GType resolved_type;
1055
1056       /* Resolve the type name */
1057       type_name = g_quark_to_string (elem->name);
1058       resolved_type = g_type_from_name (type_name);
1059
1060       if (resolved_type == G_TYPE_INVALID)
1061         {
1062           /* Type couldn't be resolved, so the selector
1063            * clearly doesn't affect the given widget path
1064            */
1065           return FALSE;
1066         }
1067
1068       elem->elem_type = SELECTOR_GTYPE;
1069       elem->type = resolved_type;
1070     }
1071
1072   if (elem->elem_type == SELECTOR_GTYPE)
1073     {
1074       GType type;
1075
1076       type = gtk_widget_path_iter_get_object_type (path, index);
1077
1078       if (!g_type_is_a (type, elem->type))
1079         return FALSE;
1080
1081       if (type == elem->type)
1082         *score |= 0xF;
1083       else
1084         {
1085           GType parent = type;
1086
1087           *score = 0xE;
1088
1089           while ((parent = g_type_parent (parent)) != G_TYPE_INVALID)
1090             {
1091               if (parent == elem->type)
1092                 break;
1093
1094               *score -= 1;
1095
1096               if (*score == 1)
1097                 {
1098                   g_warning ("Hierarchy is higher than expected.");
1099                   break;
1100                 }
1101             }
1102         }
1103
1104       return TRUE;
1105     }
1106   else if (elem->elem_type == SELECTOR_REGION)
1107     {
1108       GtkRegionFlags flags;
1109
1110       if (!gtk_widget_path_iter_has_qregion (path, index,
1111                                              elem->region.name,
1112                                              &flags))
1113         return FALSE;
1114
1115       if (elem->region.flags != 0 &&
1116           (flags & elem->region.flags) == 0)
1117         return FALSE;
1118
1119       *score = 0xF;
1120       return TRUE;
1121     }
1122   else if (elem->elem_type == SELECTOR_GLOB)
1123     {
1124       /* Treat as lowest matching type */
1125       *score = 1;
1126       return TRUE;
1127     }
1128   else if (elem->elem_type == SELECTOR_NAME)
1129     {
1130       if (!gtk_widget_path_iter_has_qname (path, index, elem->name))
1131         return FALSE;
1132
1133       *score = 0xF;
1134       return TRUE;
1135     }
1136   else if (elem->elem_type == SELECTOR_CLASS)
1137     {
1138       if (!gtk_widget_path_iter_has_qclass (path, index, elem->name))
1139         return FALSE;
1140
1141       *score = 0xF;
1142       return TRUE;
1143     }
1144
1145   return FALSE;
1146 }
1147
1148 static guint64
1149 compare_selector (GtkWidgetPath *path,
1150                   SelectorPath  *selector)
1151 {
1152   GSList *elements = selector->elements;
1153   gboolean match = TRUE, first = TRUE, first_match = FALSE;
1154   guint64 score = 0;
1155   gint i;
1156
1157   i = gtk_widget_path_length (path) - 1;
1158
1159   while (elements && match && i >= 0)
1160     {
1161       SelectorElement *elem;
1162       guint8 elem_score;
1163
1164       elem = elements->data;
1165
1166       match = compare_selector_element (path, i, elem, &elem_score);
1167
1168       if (match && first)
1169         first_match = TRUE;
1170
1171       /* Only move on to the next index if there is no match
1172        * with the current element (whether to continue or not
1173        * handled right after in the combinator check), or a
1174        * GType or glob has just been matched.
1175        *
1176        * Region and widget names do not trigger this because
1177        * the next element in the selector path could also be
1178        * related to the same index.
1179        */
1180       if (!match ||
1181           (elem->elem_type == SELECTOR_GTYPE ||
1182            elem->elem_type == SELECTOR_GLOB))
1183         i--;
1184
1185       if (!match &&
1186           elem->elem_type != SELECTOR_TYPE_NAME &&
1187           elem->combinator == COMBINATOR_DESCENDANT)
1188         {
1189           /* With descendant combinators there may
1190            * be intermediate chidren in the hierarchy
1191            */
1192           match = TRUE;
1193         }
1194       else if (match)
1195         elements = elements->next;
1196
1197       if (match)
1198         {
1199           /* Only 4 bits are actually used */
1200           score <<= 4;
1201           score |= elem_score;
1202         }
1203
1204       first = FALSE;
1205     }
1206
1207   /* If there are pending selector
1208    * elements to compare, it's not
1209    * a match.
1210    */
1211   if (elements)
1212     match = FALSE;
1213
1214   if (!match)
1215     score = 0;
1216   else if (first_match)
1217     {
1218       /* Assign more weight to these selectors
1219        * that matched right from the first element.
1220        */
1221       score <<= 4;
1222     }
1223
1224   return score;
1225 }
1226
1227 typedef struct StylePriorityInfo StylePriorityInfo;
1228
1229 struct StylePriorityInfo
1230 {
1231   guint64 score;
1232   GHashTable *style;
1233   GtkStateFlags state;
1234 };
1235
1236 static GArray *
1237 css_provider_get_selectors (GtkCssProvider *css_provider,
1238                             GtkWidgetPath  *path)
1239 {
1240   GtkCssProviderPrivate *priv;
1241   GArray *priority_info;
1242   guint i, j;
1243
1244   priv = css_provider->priv;
1245   priority_info = g_array_new (FALSE, FALSE, sizeof (StylePriorityInfo));
1246
1247   for (i = 0; i < priv->selectors_info->len; i++)
1248     {
1249       SelectorStyleInfo *info;
1250       StylePriorityInfo new;
1251       gboolean added = FALSE;
1252       guint64 score;
1253
1254       info = g_ptr_array_index (priv->selectors_info, i);
1255       score = compare_selector (path, info->path);
1256
1257       if (score <= 0)
1258         continue;
1259
1260       new.score = score;
1261       new.style = info->style;
1262       new.state = info->path->state;
1263
1264       for (j = 0; j < priority_info->len; j++)
1265         {
1266           StylePriorityInfo *cur;
1267
1268           cur = &g_array_index (priority_info, StylePriorityInfo, j);
1269
1270           if (cur->score > new.score)
1271             {
1272               g_array_insert_val (priority_info, j, new);
1273               added = TRUE;
1274               break;
1275             }
1276         }
1277
1278       if (!added)
1279         g_array_append_val (priority_info, new);
1280     }
1281
1282   return priority_info;
1283 }
1284
1285 static void
1286 css_provider_dump_symbolic_colors (GtkCssProvider     *css_provider,
1287                                    GtkStyleProperties *props)
1288 {
1289   GtkCssProviderPrivate *priv;
1290   GHashTableIter iter;
1291   gpointer key, value;
1292
1293   priv = css_provider->priv;
1294   g_hash_table_iter_init (&iter, priv->symbolic_colors);
1295
1296   while (g_hash_table_iter_next (&iter, &key, &value))
1297     {
1298       const gchar *name;
1299       GtkSymbolicColor *color;
1300
1301       name = key;
1302       color = value;
1303
1304       gtk_style_properties_map_color (props, name, color);
1305     }
1306 }
1307
1308 static GtkStyleProperties *
1309 gtk_css_provider_get_style (GtkStyleProvider *provider,
1310                             GtkWidgetPath    *path)
1311 {
1312   GtkCssProvider *css_provider;
1313   GtkCssProviderPrivate *priv;
1314   GtkStyleProperties *props;
1315   GArray *priority_info;
1316   guint i;
1317
1318   css_provider = GTK_CSS_PROVIDER (provider);
1319   props = gtk_style_properties_new ();
1320   priv = css_provider->priv;
1321
1322   css_provider_dump_symbolic_colors (css_provider, props);
1323   priority_info = css_provider_get_selectors (css_provider, path);
1324
1325   for (i = 0; i < priority_info->len; i++)
1326     {
1327       StylePriorityInfo *info;
1328       GHashTableIter iter;
1329       gpointer key, value;
1330
1331       info = &g_array_index (priority_info, StylePriorityInfo, i);
1332       g_hash_table_iter_init (&iter, info->style);
1333
1334       while (g_hash_table_iter_next (&iter, &key, &value))
1335         {
1336           gchar *prop = key;
1337
1338           /* Properties starting with '-' may be both widget style properties
1339            * or custom properties from the theming engine, so check whether
1340            * the type is registered or not.
1341            */
1342           if (prop[0] == '-' &&
1343               !gtk_style_properties_lookup_property (prop, NULL, NULL))
1344             continue;
1345
1346           gtk_style_properties_set_property (props, key, info->state, value);
1347         }
1348     }
1349
1350   g_array_free (priority_info, TRUE);
1351
1352   return props;
1353 }
1354
1355 static gboolean
1356 gtk_css_provider_get_style_property (GtkStyleProvider *provider,
1357                                      GtkWidgetPath    *path,
1358                                      GtkStateFlags     state,
1359                                      GParamSpec       *pspec,
1360                                      GValue           *value)
1361 {
1362   GArray *priority_info;
1363   gboolean found = FALSE;
1364   gchar *prop_name;
1365   gint i;
1366
1367   prop_name = g_strdup_printf ("-%s-%s",
1368                                g_type_name (pspec->owner_type),
1369                                pspec->name);
1370
1371   priority_info = css_provider_get_selectors (GTK_CSS_PROVIDER (provider), path);
1372
1373   for (i = priority_info->len - 1; i >= 0; i--)
1374     {
1375       StylePriorityInfo *info;
1376       GValue *val;
1377
1378       info = &g_array_index (priority_info, StylePriorityInfo, i);
1379       val = g_hash_table_lookup (info->style, prop_name);
1380
1381       if (val &&
1382           (info->state == 0 ||
1383            info->state == state ||
1384            ((info->state & state) != 0 &&
1385             (info->state & ~(state)) == 0)))
1386         {
1387           const gchar *val_str;
1388
1389           val_str = g_value_get_string (val);
1390           found = TRUE;
1391
1392           css_provider_parse_value (GTK_CSS_PROVIDER (provider), val_str, value, NULL);
1393           break;
1394         }
1395     }
1396
1397   g_array_free (priority_info, TRUE);
1398   g_free (prop_name);
1399
1400   return found;
1401 }
1402
1403 static void
1404 gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface)
1405 {
1406   iface->get_style = gtk_css_provider_get_style;
1407   iface->get_style_property = gtk_css_provider_get_style_property;
1408 }
1409
1410 static void
1411 gtk_css_provider_finalize (GObject *object)
1412 {
1413   GtkCssProvider *css_provider;
1414   GtkCssProviderPrivate *priv;
1415
1416   css_provider = GTK_CSS_PROVIDER (object);
1417   priv = css_provider->priv;
1418
1419   g_scanner_destroy (priv->scanner);
1420   g_free (priv->filename);
1421
1422   g_ptr_array_free (priv->selectors_info, TRUE);
1423
1424   g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL);
1425   g_slist_free (priv->cur_selectors);
1426
1427   if (priv->cur_properties)
1428     g_hash_table_unref (priv->cur_properties);
1429   if (priv->symbolic_colors)
1430     g_hash_table_destroy (priv->symbolic_colors);
1431
1432   G_OBJECT_CLASS (gtk_css_provider_parent_class)->finalize (object);
1433 }
1434
1435 /**
1436  * gtk_css_provider_new:
1437  *
1438  * Returns a newly created #GtkCssProvider.
1439  *
1440  * Returns: A new #GtkCssProvider
1441  **/
1442 GtkCssProvider *
1443 gtk_css_provider_new (void)
1444 {
1445   return g_object_new (GTK_TYPE_CSS_PROVIDER, NULL);
1446 }
1447
1448 static void
1449 property_value_free (GValue *value)
1450 {
1451   if (G_IS_VALUE (value))
1452     g_value_unset (value);
1453
1454   g_slice_free (GValue, value);
1455 }
1456
1457 static void
1458 scanner_apply_scope (GScanner    *scanner,
1459                      ParserScope  scope)
1460 {
1461   g_scanner_set_scope (scanner, scope);
1462
1463   if (scope == SCOPE_VALUE)
1464     {
1465       scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "@#-_";
1466       scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "@#-_ +(),.%\t\n'/\"";
1467       scanner->config->scan_identifier_1char = TRUE;
1468     }
1469   else if (scope == SCOPE_SELECTOR)
1470     {
1471       scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "*@";
1472       scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_#.";
1473       scanner->config->scan_identifier_1char = TRUE;
1474     }
1475   else if (scope == SCOPE_PSEUDO_CLASS ||
1476            scope == SCOPE_NTH_CHILD ||
1477            scope == SCOPE_DECLARATION)
1478     {
1479       scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "-_";
1480       scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_";
1481       scanner->config->scan_identifier_1char = FALSE;
1482     }
1483   else
1484     g_assert_not_reached ();
1485
1486   scanner->config->scan_float = FALSE;
1487   scanner->config->cpair_comment_single = NULL;
1488 }
1489
1490 static void
1491 css_provider_push_scope (GtkCssProvider *css_provider,
1492                          ParserScope     scope)
1493 {
1494   GtkCssProviderPrivate *priv;
1495
1496   priv = css_provider->priv;
1497   priv->state = g_slist_prepend (priv->state, GUINT_TO_POINTER (scope));
1498
1499   scanner_apply_scope (priv->scanner, scope);
1500 }
1501
1502 static ParserScope
1503 css_provider_pop_scope (GtkCssProvider *css_provider)
1504 {
1505   GtkCssProviderPrivate *priv;
1506   ParserScope scope = SCOPE_SELECTOR;
1507
1508   priv = css_provider->priv;
1509
1510   if (!priv->state)
1511     {
1512       g_warning ("Push/pop calls to parser scope aren't paired");
1513       scanner_apply_scope (priv->scanner, SCOPE_SELECTOR);
1514       return SCOPE_SELECTOR;
1515     }
1516
1517   priv->state = g_slist_delete_link (priv->state, priv->state);
1518
1519   /* Fetch new scope */
1520   if (priv->state)
1521     scope = GPOINTER_TO_INT (priv->state->data);
1522
1523   scanner_apply_scope (priv->scanner, scope);
1524
1525   return scope;
1526 }
1527
1528 static void
1529 css_provider_reset_parser (GtkCssProvider *css_provider)
1530 {
1531   GtkCssProviderPrivate *priv;
1532
1533   priv = css_provider->priv;
1534
1535   g_slist_free (priv->state);
1536   priv->state = NULL;
1537
1538   scanner_apply_scope (priv->scanner, SCOPE_SELECTOR);
1539   priv->scanner->user_data = NULL;
1540   priv->value_pos = NULL;
1541
1542   g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL);
1543   g_slist_free (priv->cur_selectors);
1544   priv->cur_selectors = NULL;
1545
1546   if (priv->cur_properties)
1547     g_hash_table_unref (priv->cur_properties);
1548
1549   priv->cur_properties = g_hash_table_new_full (g_str_hash,
1550                                                 g_str_equal,
1551                                                 (GDestroyNotify) g_free,
1552                                                 (GDestroyNotify) property_value_free);
1553 }
1554
1555 static void
1556 css_provider_commit (GtkCssProvider *css_provider)
1557 {
1558   GtkCssProviderPrivate *priv;
1559   GSList *l;
1560
1561   priv = css_provider->priv;
1562   l = priv->cur_selectors;
1563
1564   while (l)
1565     {
1566       SelectorPath *path = l->data;
1567       SelectorStyleInfo *info;
1568
1569       info = selector_style_info_new (path);
1570       selector_style_info_set_style (info, priv->cur_properties);
1571
1572       g_ptr_array_add (priv->selectors_info, info);
1573       l = l->next;
1574     }
1575 }
1576
1577 static GTokenType
1578 parse_nth_child (GtkCssProvider *css_provider,
1579                  GScanner       *scanner,
1580                  GtkRegionFlags *flags)
1581 {
1582   ParserSymbol symbol;
1583
1584   g_scanner_get_next_token (scanner);
1585
1586   if (scanner->token != G_TOKEN_SYMBOL)
1587     return G_TOKEN_SYMBOL;
1588
1589   symbol = GPOINTER_TO_INT (scanner->value.v_symbol);
1590
1591   if (symbol == SYMBOL_NTH_CHILD)
1592     {
1593       g_scanner_get_next_token (scanner);
1594
1595       if (scanner->token != G_TOKEN_LEFT_PAREN)
1596         return G_TOKEN_LEFT_PAREN;
1597
1598       css_provider_push_scope (css_provider, SCOPE_NTH_CHILD);
1599       g_scanner_get_next_token (scanner);
1600
1601       if (scanner->token != G_TOKEN_SYMBOL)
1602         return G_TOKEN_SYMBOL;
1603
1604       symbol = GPOINTER_TO_INT (scanner->value.v_symbol);
1605
1606       switch (symbol)
1607         {
1608         case SYMBOL_NTH_CHILD_EVEN:
1609           *flags = GTK_REGION_EVEN;
1610           break;
1611         case SYMBOL_NTH_CHILD_ODD:
1612           *flags = GTK_REGION_ODD;
1613           break;
1614         case SYMBOL_NTH_CHILD_FIRST:
1615           *flags = GTK_REGION_FIRST;
1616           break;
1617         case SYMBOL_NTH_CHILD_LAST:
1618           *flags = GTK_REGION_LAST;
1619           break;
1620         default:
1621           break;
1622         }
1623
1624       g_scanner_get_next_token (scanner);
1625
1626       if (scanner->token != G_TOKEN_RIGHT_PAREN)
1627         return G_TOKEN_RIGHT_PAREN;
1628
1629       css_provider_pop_scope (css_provider);
1630     }
1631   else if (symbol == SYMBOL_FIRST_CHILD)
1632     *flags = GTK_REGION_FIRST;
1633   else if (symbol == SYMBOL_LAST_CHILD)
1634     *flags = GTK_REGION_LAST;
1635   else if (symbol == SYMBOL_SORTED_CHILD)
1636     *flags = GTK_REGION_SORTED;
1637   else
1638     {
1639       *flags = 0;
1640       return G_TOKEN_SYMBOL;
1641     }
1642
1643   return G_TOKEN_NONE;
1644 }
1645
1646 static GTokenType
1647 parse_pseudo_class (GtkCssProvider *css_provider,
1648                     GScanner       *scanner,
1649                     SelectorPath   *selector)
1650 {
1651   GtkStateType state;
1652
1653   g_scanner_get_next_token (scanner);
1654
1655   if (scanner->token != G_TOKEN_SYMBOL)
1656     return G_TOKEN_SYMBOL;
1657
1658   state = GPOINTER_TO_INT (scanner->value.v_symbol);
1659
1660   switch (state)
1661     {
1662     case GTK_STATE_ACTIVE:
1663       selector->state |= GTK_STATE_FLAG_ACTIVE;
1664       break;
1665     case GTK_STATE_PRELIGHT:
1666       selector->state |= GTK_STATE_FLAG_PRELIGHT;
1667       break;
1668     case GTK_STATE_SELECTED:
1669       selector->state |= GTK_STATE_FLAG_SELECTED;
1670       break;
1671     case GTK_STATE_INSENSITIVE:
1672       selector->state |= GTK_STATE_FLAG_INSENSITIVE;
1673       break;
1674     case GTK_STATE_INCONSISTENT:
1675       selector->state |= GTK_STATE_FLAG_INCONSISTENT;
1676       break;
1677     case GTK_STATE_FOCUSED:
1678       selector->state |= GTK_STATE_FLAG_FOCUSED;
1679       break;
1680     default:
1681       return G_TOKEN_SYMBOL;
1682     }
1683
1684   return G_TOKEN_NONE;
1685 }
1686
1687 /* Parses a number of concatenated classes */
1688 static void
1689 parse_classes (SelectorPath   *path,
1690                const gchar    *str)
1691 {
1692   gchar *pos;
1693
1694   if ((pos = strchr (str, '.')) != NULL)
1695     {
1696       /* Leave the last class to the call after the loop */
1697       while (pos)
1698         {
1699           *pos = '\0';
1700           selector_path_prepend_class (path, str);
1701
1702           str = pos + 1;
1703           pos = strchr (str, '.');
1704         }
1705     }
1706
1707   selector_path_prepend_class (path, str);
1708 }
1709
1710 static GTokenType
1711 parse_selector (GtkCssProvider  *css_provider,
1712                 GScanner        *scanner,
1713                 SelectorPath   **selector_out)
1714 {
1715   SelectorPath *path;
1716
1717   path = selector_path_new ();
1718   *selector_out = path;
1719
1720   if (scanner->token != ':' &&
1721       scanner->token != '#' &&
1722       scanner->token != '.' &&
1723       scanner->token != G_TOKEN_IDENTIFIER)
1724     return G_TOKEN_IDENTIFIER;
1725
1726   while (scanner->token == '#' ||
1727          scanner->token == '.' ||
1728          scanner->token == G_TOKEN_IDENTIFIER)
1729     {
1730       if (scanner->token == '#' ||
1731           scanner->token == '.')
1732         {
1733           gboolean is_class;
1734           gchar *pos;
1735
1736           is_class = (scanner->token == '.');
1737
1738           g_scanner_get_next_token (scanner);
1739
1740           if (scanner->token != G_TOKEN_IDENTIFIER)
1741             return G_TOKEN_IDENTIFIER;
1742
1743           selector_path_prepend_glob (path);
1744           selector_path_prepend_combinator (path, COMBINATOR_CHILD);
1745
1746           if (is_class)
1747             parse_classes (path, scanner->value.v_identifier);
1748           else
1749             {
1750               if ((pos = strchr (scanner->value.v_identifier, '.')) != NULL)
1751                 *pos = '\0';
1752
1753               selector_path_prepend_name (path, scanner->value.v_identifier);
1754
1755               /* Parse any remaining classes */
1756               if (pos)
1757                 parse_classes (path, pos + 1);
1758             }
1759         }
1760       else if (g_ascii_isupper (scanner->value.v_identifier[0]))
1761         {
1762           gchar *pos;
1763
1764           if ((pos = strchr (scanner->value.v_identifier, '#')) != NULL ||
1765               (pos = strchr (scanner->value.v_identifier, '.')) != NULL)
1766             {
1767               gchar *type_name, *name;
1768               gboolean is_class;
1769
1770               is_class = (*pos == '.');
1771
1772               /* Widget type and name/class put together */
1773               name = pos + 1;
1774               *pos = '\0';
1775               type_name = scanner->value.v_identifier;
1776
1777               selector_path_prepend_type (path, type_name);
1778
1779               /* This is only so there is a direct relationship
1780                * between widget type and its name.
1781                */
1782               selector_path_prepend_combinator (path, COMBINATOR_CHILD);
1783
1784               if (is_class)
1785                 parse_classes (path, name);
1786               else
1787                 {
1788                   if ((pos = strchr (name, '.')) != NULL)
1789                     *pos = '\0';
1790
1791                   selector_path_prepend_name (path, name);
1792
1793                   /* Parse any remaining classes */
1794                   if (pos)
1795                     parse_classes (path, pos + 1);
1796                 }
1797             }
1798           else
1799             selector_path_prepend_type (path, scanner->value.v_identifier);
1800         }
1801       else if (g_ascii_islower (scanner->value.v_identifier[0]))
1802         {
1803           GtkRegionFlags flags = 0;
1804           gchar *region_name;
1805
1806           region_name = g_strdup (scanner->value.v_identifier);
1807
1808           if (g_scanner_peek_next_token (scanner) == ':')
1809             {
1810               ParserSymbol symbol;
1811
1812               g_scanner_get_next_token (scanner);
1813               css_provider_push_scope (css_provider, SCOPE_PSEUDO_CLASS);
1814
1815               /* Check for the next token being nth-child, parse in that
1816                * case, and fallback into common state parsing if not.
1817                */
1818               if (g_scanner_peek_next_token (scanner) != G_TOKEN_SYMBOL)
1819                 return G_TOKEN_SYMBOL;
1820
1821               symbol = GPOINTER_TO_INT (scanner->next_value.v_symbol);
1822
1823               if (symbol == SYMBOL_FIRST_CHILD ||
1824                   symbol == SYMBOL_LAST_CHILD ||
1825                   symbol == SYMBOL_NTH_CHILD ||
1826                   symbol == SYMBOL_SORTED_CHILD)
1827                 {
1828                   GTokenType token;
1829
1830                   if ((token = parse_nth_child (css_provider, scanner, &flags)) != G_TOKEN_NONE)
1831                     return token;
1832
1833                   css_provider_pop_scope (css_provider);
1834                 }
1835               else
1836                 {
1837                   css_provider_pop_scope (css_provider);
1838                   selector_path_prepend_region (path, region_name, 0);
1839                   g_free (region_name);
1840                   break;
1841                 }
1842             }
1843
1844           selector_path_prepend_region (path, region_name, flags);
1845           g_free (region_name);
1846         }
1847       else if (scanner->value.v_identifier[0] == '*')
1848         selector_path_prepend_glob (path);
1849       else
1850         return G_TOKEN_IDENTIFIER;
1851
1852       g_scanner_get_next_token (scanner);
1853
1854       if (scanner->token == '>')
1855         {
1856           selector_path_prepend_combinator (path, COMBINATOR_CHILD);
1857           g_scanner_get_next_token (scanner);
1858         }
1859     }
1860
1861   if (scanner->token == ':')
1862     {
1863       /* Add glob selector if path is empty */
1864       if (selector_path_depth (path) == 0)
1865         selector_path_prepend_glob (path);
1866
1867       css_provider_push_scope (css_provider, SCOPE_PSEUDO_CLASS);
1868
1869       while (scanner->token == ':')
1870         {
1871           GTokenType token;
1872
1873           if ((token = parse_pseudo_class (css_provider, scanner, path)) != G_TOKEN_NONE)
1874             return token;
1875
1876           g_scanner_get_next_token (scanner);
1877         }
1878
1879       css_provider_pop_scope (css_provider);
1880     }
1881
1882   return G_TOKEN_NONE;
1883 }
1884
1885 #define SKIP_SPACES(s) while (s[0] == ' ' || s[0] == '\t' || s[0] == '\n') s++;
1886 #define SKIP_SPACES_BACK(s) while (s[0] == ' ' || s[0] == '\t' || s[0] == '\n') s--;
1887
1888 static GtkSymbolicColor *
1889 symbolic_color_parse_str (const gchar  *string,
1890                           gchar       **end_ptr)
1891 {
1892   GtkSymbolicColor *symbolic_color = NULL;
1893   gchar *str;
1894
1895   str = (gchar *) string;
1896   *end_ptr = str;
1897
1898   if (str[0] == '@')
1899     {
1900       const gchar *end;
1901       gchar *name;
1902
1903       str++;
1904       end = str;
1905
1906       while (*end == '-' || *end == '_' || g_ascii_isalpha (*end))
1907         end++;
1908
1909       name = g_strndup (str, end - str);
1910       symbolic_color = gtk_symbolic_color_new_name (name);
1911       g_free (name);
1912
1913       *end_ptr = (gchar *) end;
1914     }
1915   else if (g_str_has_prefix (str, "lighter") ||
1916            g_str_has_prefix (str, "darker"))
1917     {
1918       GtkSymbolicColor *param_color;
1919       gboolean is_lighter = FALSE;
1920
1921       is_lighter = g_str_has_prefix (str, "lighter");
1922
1923       if (is_lighter)
1924         str += strlen ("lighter");
1925       else
1926         str += strlen ("darker");
1927
1928       SKIP_SPACES (str);
1929
1930       if (*str != '(')
1931         {
1932           *end_ptr = (gchar *) str;
1933           return NULL;
1934         }
1935
1936       str++;
1937       SKIP_SPACES (str);
1938       param_color = symbolic_color_parse_str (str, end_ptr);
1939
1940       if (!param_color)
1941         return NULL;
1942
1943       str = *end_ptr;
1944       SKIP_SPACES (str);
1945       *end_ptr = (gchar *) str;
1946
1947       if (*str != ')')
1948         {
1949           gtk_symbolic_color_unref (param_color);
1950           return NULL;
1951         }
1952
1953       if (is_lighter)
1954         symbolic_color = gtk_symbolic_color_new_shade (param_color, 1.3);
1955       else
1956         symbolic_color = gtk_symbolic_color_new_shade (param_color, 0.7);
1957
1958       gtk_symbolic_color_unref (param_color);
1959       (*end_ptr)++;
1960     }
1961   else if (g_str_has_prefix (str, "shade") ||
1962            g_str_has_prefix (str, "alpha"))
1963     {
1964       GtkSymbolicColor *param_color;
1965       gboolean is_shade = FALSE;
1966       gdouble factor;
1967
1968       is_shade = g_str_has_prefix (str, "shade");
1969
1970       if (is_shade)
1971         str += strlen ("shade");
1972       else
1973         str += strlen ("alpha");
1974
1975       SKIP_SPACES (str);
1976
1977       if (*str != '(')
1978         {
1979           *end_ptr = (gchar *) str;
1980           return NULL;
1981         }
1982
1983       str++;
1984       SKIP_SPACES (str);
1985       param_color = symbolic_color_parse_str (str, end_ptr);
1986
1987       if (!param_color)
1988         return NULL;
1989
1990       str = *end_ptr;
1991       SKIP_SPACES (str);
1992
1993       if (str[0] != ',')
1994         {
1995           gtk_symbolic_color_unref (param_color);
1996           *end_ptr = (gchar *) str;
1997           return NULL;
1998         }
1999
2000       str++;
2001       SKIP_SPACES (str);
2002       factor = g_ascii_strtod (str, end_ptr);
2003
2004       str = *end_ptr;
2005       SKIP_SPACES (str);
2006       *end_ptr = (gchar *) str;
2007
2008       if (str[0] != ')')
2009         {
2010           gtk_symbolic_color_unref (param_color);
2011           return NULL;
2012         }
2013
2014       if (is_shade)
2015         symbolic_color = gtk_symbolic_color_new_shade (param_color, factor);
2016       else
2017         symbolic_color = gtk_symbolic_color_new_alpha (param_color, factor);
2018
2019       gtk_symbolic_color_unref (param_color);
2020       (*end_ptr)++;
2021     }
2022   else if (g_str_has_prefix (str, "mix"))
2023     {
2024       GtkSymbolicColor *color1, *color2;
2025       gdouble factor;
2026
2027       str += strlen ("mix");
2028       SKIP_SPACES (str);
2029
2030       if (*str != '(')
2031         {
2032           *end_ptr = (gchar *) str;
2033           return NULL;
2034         }
2035
2036       str++;
2037       SKIP_SPACES (str);
2038       color1 = symbolic_color_parse_str (str, end_ptr);
2039
2040       if (!color1)
2041         return NULL;
2042
2043       str = *end_ptr;
2044       SKIP_SPACES (str);
2045
2046       if (str[0] != ',')
2047         {
2048           gtk_symbolic_color_unref (color1);
2049           *end_ptr = (gchar *) str;
2050           return NULL;
2051         }
2052
2053       str++;
2054       SKIP_SPACES (str);
2055       color2 = symbolic_color_parse_str (str, end_ptr);
2056
2057       if (!color2 || *end_ptr[0] != ',')
2058         {
2059           gtk_symbolic_color_unref (color1);
2060           return NULL;
2061         }
2062
2063       str = *end_ptr;
2064       SKIP_SPACES (str);
2065
2066       if (str[0] != ',')
2067         {
2068           gtk_symbolic_color_unref (color1);
2069           gtk_symbolic_color_unref (color2);
2070           *end_ptr = (gchar *) str;
2071           return NULL;
2072         }
2073
2074       str++;
2075       SKIP_SPACES (str);
2076       factor = g_ascii_strtod (str, end_ptr);
2077
2078       str = *end_ptr;
2079       SKIP_SPACES (str);
2080       *end_ptr = (gchar *) str;
2081
2082       if (str[0] != ')')
2083         {
2084           gtk_symbolic_color_unref (color1);
2085           gtk_symbolic_color_unref (color2);
2086           return NULL;
2087         }
2088
2089       symbolic_color = gtk_symbolic_color_new_mix (color1, color2, factor);
2090       gtk_symbolic_color_unref (color1);
2091       gtk_symbolic_color_unref (color2);
2092       (*end_ptr)++;
2093     }
2094   else
2095     {
2096       GdkRGBA color;
2097       gchar *color_str;
2098       const gchar *end;
2099
2100       end = str + 1;
2101
2102       if (str[0] == '#')
2103         {
2104           /* Color in hex format */
2105           while (g_ascii_isxdigit (*end))
2106             end++;
2107         }
2108       else if (g_str_has_prefix (str, "rgb"))
2109         {
2110           /* color in rgb/rgba format */
2111           while (*end != ')' && *end != '\0')
2112             end++;
2113
2114           if (*end == ')')
2115             end++;
2116         }
2117       else
2118         {
2119           /* color name? parse until first whitespace */
2120           while (*end != ' ' && *end != '\0')
2121             end++;
2122         }
2123
2124       color_str = g_strndup (str, end - str);
2125       *end_ptr = (gchar *) end;
2126
2127       if (!gdk_rgba_parse (&color, color_str))
2128         {
2129           g_free (color_str);
2130           return NULL;
2131         }
2132
2133       symbolic_color = gtk_symbolic_color_new_literal (&color);
2134       g_free (color_str);
2135     }
2136
2137   return symbolic_color;
2138 }
2139
2140 static GtkSymbolicColor *
2141 symbolic_color_parse (const gchar  *str,
2142                       GError      **error)
2143 {
2144   GtkSymbolicColor *color;
2145   gchar *end;
2146
2147   color = symbolic_color_parse_str (str, &end);
2148
2149   if (*end != '\0')
2150     {
2151       g_set_error_literal (error,
2152                            GTK_CSS_PROVIDER_ERROR,
2153                            GTK_CSS_PROVIDER_ERROR_FAILED,
2154                            "Could not parse symbolic color");
2155
2156       if (color)
2157         {
2158           gtk_symbolic_color_unref (color);
2159           color = NULL;
2160         }
2161     }
2162
2163   return color;
2164 }
2165
2166 static GtkGradient *
2167 gradient_parse_str (const gchar  *str,
2168                     gchar       **end_ptr)
2169 {
2170   GtkGradient *gradient = NULL;
2171   gdouble coords[6];
2172   gchar *end;
2173   guint i;
2174
2175   if (g_str_has_prefix (str, "-gtk-gradient"))
2176     {
2177       cairo_pattern_type_t type;
2178
2179       str += strlen ("-gtk-gradient");
2180       SKIP_SPACES (str);
2181
2182       if (*str != '(')
2183         {
2184           *end_ptr = (gchar *) str;
2185           return NULL;
2186         }
2187
2188       str++;
2189       SKIP_SPACES (str);
2190
2191       /* Parse gradient type */
2192       if (g_str_has_prefix (str, "linear"))
2193         {
2194           type = CAIRO_PATTERN_TYPE_LINEAR;
2195           str += strlen ("linear");
2196         }
2197       else if (g_str_has_prefix (str, "radial"))
2198         {
2199           type = CAIRO_PATTERN_TYPE_RADIAL;
2200           str += strlen ("radial");
2201         }
2202       else
2203         {
2204           *end_ptr = (gchar *) str;
2205           return NULL;
2206         }
2207
2208       SKIP_SPACES (str);
2209
2210       /* Parse start/stop position parameters */
2211       for (i = 0; i < 2; i++)
2212         {
2213           if (*str != ',')
2214             {
2215               *end_ptr = (gchar *) str;
2216               return NULL;
2217             }
2218
2219           str++;
2220           SKIP_SPACES (str);
2221
2222           if (strncmp (str, "left", 4) == 0)
2223             {
2224               coords[i * 3] = 0;
2225               str += strlen ("left");
2226             }
2227           else if (strncmp (str, "right", 5) == 0)
2228             {
2229               coords[i * 3] = 1;
2230               str += strlen ("right");
2231             }
2232           else if (strncmp (str, "center", 6) == 0)
2233             {
2234               coords[i * 3] = 0.5;
2235               str += strlen ("center");
2236             }
2237           else
2238             {
2239               coords[i * 3] = g_ascii_strtod (str, &end);
2240
2241               if (str == end)
2242                 {
2243                   *end_ptr = (gchar *) str;
2244                   return NULL;
2245                 }
2246
2247               str = end;
2248             }
2249
2250           SKIP_SPACES (str);
2251
2252           if (strncmp (str, "top", 3) == 0)
2253             {
2254               coords[(i * 3) + 1] = 0;
2255               str += strlen ("top");
2256             }
2257           else if (strncmp (str, "bottom", 6) == 0)
2258             {
2259               coords[(i * 3) + 1] = 1;
2260               str += strlen ("bottom");
2261             }
2262           else if (strncmp (str, "center", 6) == 0)
2263             {
2264               coords[(i * 3) + 1] = 0.5;
2265               str += strlen ("center");
2266             }
2267           else
2268             {
2269               coords[(i * 3) + 1] = g_ascii_strtod (str, &end);
2270
2271               if (str == end)
2272                 {
2273                   *end_ptr = (gchar *) str;
2274                   return NULL;
2275                 }
2276
2277               str = end;
2278             }
2279
2280           SKIP_SPACES (str);
2281
2282           if (type == CAIRO_PATTERN_TYPE_RADIAL)
2283             {
2284               /* Parse radius */
2285               if (*str != ',')
2286                 {
2287                   *end_ptr = (gchar *) str;
2288                   return NULL;
2289                 }
2290
2291               str++;
2292               SKIP_SPACES (str);
2293
2294               coords[(i * 3) + 2] = g_ascii_strtod (str, &end);
2295               str = end;
2296
2297               SKIP_SPACES (str);
2298             }
2299         }
2300
2301       if (type == CAIRO_PATTERN_TYPE_LINEAR)
2302         gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
2303       else
2304         gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
2305                                             coords[3], coords[4], coords[5]);
2306
2307       while (*str == ',')
2308         {
2309           GtkSymbolicColor *color;
2310           gdouble position;
2311
2312           if (*str != ',')
2313             {
2314               *end_ptr = (gchar *) str;
2315               return gradient;
2316             }
2317
2318           str++;
2319           SKIP_SPACES (str);
2320
2321           if (g_str_has_prefix (str, "from"))
2322             {
2323               position = 0;
2324               str += strlen ("from");
2325               SKIP_SPACES (str);
2326
2327               if (*str != '(')
2328                 {
2329                   *end_ptr = (gchar *) str;
2330                   return gradient;
2331                 }
2332             }
2333           else if (g_str_has_prefix (str, "to"))
2334             {
2335               position = 1;
2336               str += strlen ("to");
2337               SKIP_SPACES (str);
2338
2339               if (*str != '(')
2340                 {
2341                   *end_ptr = (gchar *) str;
2342                   return gradient;
2343                 }
2344             }
2345           else if (g_str_has_prefix (str, "color-stop"))
2346             {
2347               str += strlen ("color-stop");
2348               SKIP_SPACES (str);
2349
2350               if (*str != '(')
2351                 {
2352                   *end_ptr = (gchar *) str;
2353                   return gradient;
2354                 }
2355
2356               str++;
2357               SKIP_SPACES (str);
2358
2359               position = g_ascii_strtod (str, &end);
2360
2361               str = end;
2362               SKIP_SPACES (str);
2363
2364               if (*str != ',')
2365                 {
2366                   *end_ptr = (gchar *) str;
2367                   return gradient;
2368                 }
2369             }
2370           else
2371             {
2372               *end_ptr = (gchar *) str;
2373               return gradient;
2374             }
2375
2376           str++;
2377           SKIP_SPACES (str);
2378
2379           color = symbolic_color_parse_str (str, &end);
2380
2381           str = end;
2382           SKIP_SPACES (str);
2383
2384           if (*str != ')')
2385             {
2386               *end_ptr = (gchar *) str;
2387               return gradient;
2388             }
2389
2390           str++;
2391           SKIP_SPACES (str);
2392
2393           if (color)
2394             {
2395               gtk_gradient_add_color_stop (gradient, position, color);
2396               gtk_symbolic_color_unref (color);
2397             }
2398         }
2399
2400       if (*str != ')')
2401         {
2402           *end_ptr = (gchar *) str;
2403           return gradient;
2404         }
2405
2406       str++;
2407     }
2408
2409   *end_ptr = (gchar *) str;
2410
2411   return gradient;
2412 }
2413
2414 static gchar *
2415 path_parse_str (GtkCssProvider  *css_provider,
2416                 const gchar     *str,
2417                 gchar          **end_ptr,
2418                 GError         **error)
2419 {
2420   gchar *path, *chr;
2421   const gchar *start, *end;
2422   start = str;
2423
2424   if (g_str_has_prefix (str, "url"))
2425     {
2426       str += strlen ("url");
2427       SKIP_SPACES (str);
2428
2429       if (*str != '(')
2430         {
2431           *end_ptr = (gchar *) str;
2432           return NULL;
2433         }
2434
2435       chr = strchr (str, ')');
2436       if (!chr)
2437         {
2438           *end_ptr = (gchar *) str;
2439           return NULL;
2440         }
2441
2442       end = chr + 1;
2443
2444       str++;
2445       SKIP_SPACES (str);
2446
2447       if (*str == '"' || *str == '\'')
2448         {
2449           const gchar *p;
2450           p = str;
2451           str++;
2452
2453           chr--;
2454           SKIP_SPACES_BACK (chr);
2455
2456           if (*chr != *p || chr == p)
2457             {
2458               *end_ptr = (gchar *)str;
2459               return NULL;
2460             }
2461         }
2462       else
2463         {
2464           *end_ptr = (gchar *)str;
2465           return NULL;
2466         }
2467
2468       path = g_strndup (str, chr - str);
2469       g_strstrip (path);
2470
2471       *end_ptr = (gchar *)end;
2472     }
2473   else
2474     {
2475       path = g_strdup (str);
2476       *end_ptr = (gchar *)str + strlen (str);
2477     }
2478
2479   /* Always return an absolute path */
2480   if (!g_path_is_absolute (path))
2481     {
2482       GtkCssProviderPrivate *priv;
2483       gchar *dirname, *full_path;
2484
2485       priv = css_provider->priv;
2486
2487       /* Use relative path to the current CSS file path, if any */
2488       if (priv->filename)
2489         dirname = g_path_get_dirname (priv->filename);
2490       else
2491         dirname = g_get_current_dir ();
2492
2493       full_path = g_build_filename (dirname, path, NULL);
2494       g_free (path);
2495       g_free (dirname);
2496
2497       path = full_path;
2498     }
2499
2500   if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
2501     {
2502       g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_EXIST,
2503                    "File doesn't exist: %s", path);
2504       g_free (path);
2505       path = NULL;
2506       *end_ptr = (gchar *)start;
2507     }
2508
2509   return path;
2510 }
2511
2512 static gchar *
2513 path_parse (GtkCssProvider  *css_provider,
2514             const gchar     *str,
2515             GError         **error)
2516 {
2517   gchar *path;
2518   gchar *end;
2519
2520   path = path_parse_str (css_provider, str, &end, error);
2521
2522   if (!path)
2523     return NULL;
2524
2525   if (*end != '\0')
2526     {
2527       g_set_error_literal (error,
2528                            GTK_CSS_PROVIDER_ERROR,
2529                            GTK_CSS_PROVIDER_ERROR_FAILED,
2530                            "Error parsing path");
2531       g_free (path);
2532       path = NULL;
2533     }
2534
2535   return path;
2536 }
2537
2538 static Gtk9Slice *
2539 slice_parse_str (GtkCssProvider  *css_provider,
2540                  const gchar     *str,
2541                  gchar          **end_ptr,
2542                  GError         **error)
2543 {
2544   gdouble distance_top, distance_bottom;
2545   gdouble distance_left, distance_right;
2546   GtkSliceSideModifier mods[2];
2547   GdkPixbuf *pixbuf;
2548   Gtk9Slice *slice;
2549   gchar *path;
2550   gint i = 0;
2551
2552   SKIP_SPACES (str);
2553
2554   /* Parse image url */
2555   path = path_parse_str (css_provider, str, end_ptr, error);
2556
2557   if (!path)
2558       return NULL;
2559
2560   str = *end_ptr;
2561   SKIP_SPACES (str);
2562
2563   /* Parse top/left/bottom/right distances */
2564   distance_top = g_ascii_strtod (str, end_ptr);
2565
2566   str = *end_ptr;
2567   SKIP_SPACES (str);
2568
2569   distance_right = g_ascii_strtod (str, end_ptr);
2570
2571   str = *end_ptr;
2572   SKIP_SPACES (str);
2573
2574   distance_bottom = g_ascii_strtod (str, end_ptr);
2575
2576   str = *end_ptr;
2577   SKIP_SPACES (str);
2578
2579   distance_left = g_ascii_strtod (str, end_ptr);
2580
2581   str = *end_ptr;
2582   SKIP_SPACES (str);
2583
2584   while (*str && i < 2)
2585     {
2586       if (g_str_has_prefix (str, "stretch"))
2587         {
2588           str += strlen ("stretch");
2589           mods[i] = GTK_SLICE_STRETCH;
2590         }
2591       else if (g_str_has_prefix (str, "repeat"))
2592         {
2593           str += strlen ("repeat");
2594           mods[i] = GTK_SLICE_REPEAT;
2595         }
2596       else
2597         {
2598           g_free (path);
2599           *end_ptr = (gchar *) str;
2600           return NULL;
2601         }
2602
2603       SKIP_SPACES (str);
2604       i++;
2605     }
2606
2607   *end_ptr = (gchar *) str;
2608
2609   if (*str != '\0')
2610     {
2611       g_free (path);
2612       return NULL;
2613     }
2614
2615   if (i != 2)
2616     {
2617       /* Fill in second modifier, same as the first */
2618       mods[1] = mods[0];
2619     }
2620
2621   pixbuf = gdk_pixbuf_new_from_file (path, error);
2622   g_free (path);
2623
2624   if (!pixbuf)
2625     {
2626       *end_ptr = (gchar *) str;
2627       return NULL;
2628     }
2629
2630   slice = _gtk_9slice_new (pixbuf,
2631                            distance_top, distance_bottom,
2632                            distance_left, distance_right,
2633                            mods[0], mods[1]);
2634   g_object_unref (pixbuf);
2635
2636   return slice;
2637 }
2638
2639 static gdouble
2640 unit_parse_str (const gchar     *str,
2641                 gchar          **end_str)
2642 {
2643   gdouble unit;
2644
2645   SKIP_SPACES (str);
2646   unit = g_ascii_strtod (str, end_str);
2647   str = *end_str;
2648
2649   /* Now parse the unit type, if any. We
2650    * don't admit spaces between these.
2651    */
2652   if (*str != ' ' && *str != '\0')
2653     {
2654       while (**end_str != ' ' && **end_str != '\0')
2655         (*end_str)++;
2656
2657       /* Only handle pixels at the moment */
2658       if (strncmp (str, "px", 2) != 0)
2659         {
2660           gchar *type;
2661
2662           type = g_strndup (str, *end_str - str);
2663           g_warning ("Unknown unit '%s', only pixel units are "
2664                      "currently supported in CSS style", type);
2665           g_free (type);
2666         }
2667     }
2668
2669   return unit;
2670 }
2671
2672 static GtkBorder *
2673 border_parse_str (const gchar  *str,
2674                   gchar       **end_str)
2675 {
2676   gdouble first, second, third, fourth;
2677   GtkBorder *border;
2678
2679   border = gtk_border_new ();
2680
2681   SKIP_SPACES (str);
2682   if (!g_ascii_isdigit (*str))
2683     return border;
2684
2685   first = unit_parse_str (str, end_str);
2686   str = *end_str;
2687   SKIP_SPACES (str);
2688
2689   if (!g_ascii_isdigit (*str))
2690     {
2691       border->left = border->right = border->top = border->bottom = (gint) first;
2692       *end_str = (gchar *) str;
2693       return border;
2694     }
2695
2696   second = unit_parse_str (str, end_str);
2697   str = *end_str;
2698   SKIP_SPACES (str);
2699
2700   if (!g_ascii_isdigit (*str))
2701     {
2702       border->top = border->bottom = (gint) first;
2703       border->left = border->right = (gint) second;
2704       *end_str = (gchar *) str;
2705       return border;
2706     }
2707
2708   third = unit_parse_str (str, end_str);
2709   str = *end_str;
2710   SKIP_SPACES (str);
2711
2712   if (!g_ascii_isdigit (*str))
2713     {
2714       border->top = (gint) first;
2715       border->left = border->right = (gint) second;
2716       border->bottom = (gint) third;
2717       *end_str = (gchar *) str;
2718       return border;
2719     }
2720
2721   fourth = unit_parse_str (str, end_str);
2722
2723   border->top = (gint) first;
2724   border->right = (gint) second;
2725   border->bottom = (gint) third;
2726   border->left = (gint) fourth;
2727
2728   return border;
2729 }
2730
2731 static gboolean
2732 css_provider_parse_value (GtkCssProvider  *css_provider,
2733                           const gchar     *value_str,
2734                           GValue          *value,
2735                           GError         **error)
2736 {
2737   GtkCssProviderPrivate *priv;
2738   GType type;
2739   gboolean parsed = TRUE;
2740   gchar *end = NULL;
2741
2742   priv = css_provider->priv;
2743   type = G_VALUE_TYPE (value);
2744
2745   if (type == GDK_TYPE_RGBA ||
2746       type == GDK_TYPE_COLOR)
2747     {
2748       GdkRGBA rgba;
2749       GdkColor color;
2750
2751       if (type == GDK_TYPE_RGBA &&
2752           gdk_rgba_parse (&rgba, value_str))
2753         g_value_set_boxed (value, &rgba);
2754       else if (type == GDK_TYPE_COLOR &&
2755                gdk_color_parse (value_str, &color))
2756         g_value_set_boxed (value, &color);
2757       else
2758         {
2759           GtkSymbolicColor *symbolic_color;
2760
2761           symbolic_color = symbolic_color_parse_str (value_str, &end);
2762
2763           if (symbolic_color)
2764             {
2765               g_value_unset (value);
2766               g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
2767               g_value_take_boxed (value, symbolic_color);
2768             }
2769           else
2770             parsed = FALSE;
2771         }
2772     }
2773   else if (type == PANGO_TYPE_FONT_DESCRIPTION)
2774     {
2775       PangoFontDescription *font_desc;
2776
2777       font_desc = pango_font_description_from_string (value_str);
2778       g_value_take_boxed (value, font_desc);
2779     }
2780   else if (type == G_TYPE_BOOLEAN)
2781     {
2782       if (value_str[0] == '1' ||
2783           g_ascii_strcasecmp (value_str, "true") == 0)
2784         g_value_set_boolean (value, TRUE);
2785       else
2786         g_value_set_boolean (value, FALSE);
2787     }
2788   else if (type == G_TYPE_INT)
2789     g_value_set_int (value, atoi (value_str));
2790   else if (type == G_TYPE_UINT)
2791     g_value_set_uint (value, (guint) atoi (value_str));
2792   else if (type == G_TYPE_DOUBLE)
2793     g_value_set_double (value, g_ascii_strtod (value_str, NULL));
2794   else if (type == G_TYPE_FLOAT)
2795     g_value_set_float (value, (gfloat) g_ascii_strtod (value_str, NULL));
2796   else if (type == GTK_TYPE_THEMING_ENGINE)
2797     {
2798       GtkThemingEngine *engine;
2799
2800       engine = gtk_theming_engine_load (value_str);
2801       if (engine)
2802         g_value_set_object (value, engine);
2803       else
2804         parsed = FALSE;
2805     }
2806   else if (type == GTK_TYPE_ANIMATION_DESCRIPTION)
2807     {
2808       GtkAnimationDescription *desc;
2809
2810       desc = _gtk_animation_description_from_string (value_str);
2811
2812       if (desc)
2813         g_value_take_boxed (value, desc);
2814       else
2815         parsed = FALSE;
2816     }
2817   else if (type == GTK_TYPE_BORDER)
2818     {
2819       GtkBorder *border;
2820
2821       border = border_parse_str (value_str, &end);
2822       g_value_take_boxed (value, border);
2823     }
2824   else if (type == CAIRO_GOBJECT_TYPE_PATTERN)
2825     {
2826       GtkGradient *gradient;
2827
2828       gradient = gradient_parse_str (value_str, &end);
2829
2830       if (gradient)
2831         {
2832           g_value_unset (value);
2833           g_value_init (value, GTK_TYPE_GRADIENT);
2834           g_value_take_boxed (value, gradient);
2835         }
2836       else
2837         {
2838           gchar *path;
2839           GdkPixbuf *pixbuf;
2840
2841           g_clear_error (error);
2842           path = path_parse_str (css_provider, value_str, &end, error);
2843
2844           if (path)
2845             {
2846               pixbuf = gdk_pixbuf_new_from_file (path, NULL);
2847               g_free (path);
2848
2849               if (pixbuf)
2850                 {
2851                   cairo_surface_t *surface;
2852                   cairo_pattern_t *pattern;
2853                   cairo_t *cr;
2854                   cairo_matrix_t matrix;
2855
2856                   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
2857                                                         gdk_pixbuf_get_width (pixbuf),
2858                                                         gdk_pixbuf_get_height (pixbuf));
2859                   cr = cairo_create (surface);
2860                   gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
2861                   cairo_paint (cr);
2862                   pattern = cairo_pattern_create_for_surface (surface);
2863
2864                   cairo_matrix_init_scale (&matrix,
2865                                            gdk_pixbuf_get_width (pixbuf),
2866                                            gdk_pixbuf_get_height (pixbuf));
2867                   cairo_pattern_set_matrix (pattern, &matrix);
2868
2869                   cairo_surface_destroy (surface);
2870                   cairo_destroy (cr);
2871                   g_object_unref (pixbuf);
2872
2873                   g_value_take_boxed (value, pattern);
2874                 }
2875               else
2876                 parsed = FALSE;
2877             }
2878           else
2879             parsed = FALSE;
2880         }
2881     }
2882   else if (G_TYPE_IS_ENUM (type))
2883     {
2884       GEnumClass *enum_class;
2885       GEnumValue *enum_value;
2886
2887       enum_class = g_type_class_ref (type);
2888       enum_value = g_enum_get_value_by_nick (enum_class, value_str);
2889
2890       if (!enum_value)
2891         {
2892           g_set_error (error,
2893                        GTK_CSS_PROVIDER_ERROR,
2894                        GTK_CSS_PROVIDER_ERROR_FAILED,
2895                        "Unknown value '%s' for enum type '%s'",
2896                        value_str, g_type_name (type));
2897           parsed = FALSE;
2898         }
2899       else
2900         g_value_set_enum (value, enum_value->value);
2901
2902       g_type_class_unref (enum_class);
2903     }
2904   else if (G_TYPE_IS_FLAGS (type))
2905     {
2906       GFlagsClass *flags_class;
2907       GFlagsValue *flag_value;
2908       guint flags = 0;
2909       gchar *ptr;
2910
2911       flags_class = g_type_class_ref (type);
2912
2913       /* Parse comma separated values */
2914       ptr = strchr (value_str, ',');
2915
2916       while (ptr && parsed)
2917         {
2918           gchar *flag_str;
2919
2920           *ptr = '\0';
2921           ptr++;
2922
2923           flag_str = (gchar *) value_str;
2924           flag_value = g_flags_get_value_by_nick (flags_class,
2925                                                   g_strstrip (flag_str));
2926
2927           if (!flag_value)
2928             {
2929               g_set_error (error,
2930                            GTK_CSS_PROVIDER_ERROR,
2931                            GTK_CSS_PROVIDER_ERROR_FAILED,
2932                            "Unknown flag '%s' for type '%s'",
2933                            value_str, g_type_name (type));
2934               parsed = FALSE;
2935             }
2936           else
2937             flags |= flag_value->value;
2938
2939           value_str = ptr;
2940           ptr = strchr (value_str, ',');
2941         }
2942
2943       /* Store last/only value */
2944       flag_value = g_flags_get_value_by_nick (flags_class, value_str);
2945
2946       if (!flag_value)
2947         {
2948           g_set_error (error,
2949                        GTK_CSS_PROVIDER_ERROR,
2950                        GTK_CSS_PROVIDER_ERROR_FAILED,
2951                        "Unknown flag '%s' for type '%s'",
2952                        value_str, g_type_name (type));
2953           parsed = FALSE;
2954         }
2955       else
2956         flags |= flag_value->value;
2957
2958       if (parsed)
2959         g_value_set_enum (value, flags);
2960
2961       g_type_class_unref (flags_class);
2962     }
2963   else if (type == GTK_TYPE_9SLICE)
2964     {
2965       Gtk9Slice *slice;
2966
2967       slice = slice_parse_str (css_provider, value_str, &end, error);
2968
2969       if (slice)
2970         g_value_take_boxed (value, slice);
2971       else
2972         parsed = FALSE;
2973     }
2974   else
2975     {
2976       g_set_error (error,
2977                    GTK_CSS_PROVIDER_ERROR,
2978                    GTK_CSS_PROVIDER_ERROR_FAILED,
2979                    "Cannot parse string '%s' for type %s",
2980                    value_str, g_type_name (type));
2981       parsed = FALSE;
2982     }
2983
2984   if (end && *end)
2985     {
2986       /* Set error position in the scanner
2987        * according to what we've parsed so far
2988        */
2989       priv->value_pos += (end - value_str);
2990
2991       if (error && !*error)
2992         g_set_error_literal (error,
2993                              GTK_CSS_PROVIDER_ERROR,
2994                              GTK_CSS_PROVIDER_ERROR_FAILED,
2995                              "Failed to parse value");
2996     }
2997
2998   return parsed;
2999 }
3000
3001 static void
3002 scanner_report_warning (GtkCssProvider *css_provider,
3003                         GTokenType      expected_token,
3004                         GError         *error)
3005 {
3006   GtkCssProviderPrivate *priv;
3007   const gchar *line_end, *line_start;
3008   const gchar *expected_str;
3009   gchar buf[2], *line, *str;
3010   guint pos;
3011
3012   priv = css_provider->priv;
3013
3014   if (error)
3015     str = g_strdup (error->message);
3016   else
3017     {
3018       if (priv->scanner->user_data)
3019         expected_str = priv->scanner->user_data;
3020       else
3021         {
3022           switch (expected_token)
3023             {
3024             case G_TOKEN_SYMBOL:
3025               expected_str = "Symbol";
3026             case G_TOKEN_IDENTIFIER:
3027               expected_str = "Identifier";
3028             default:
3029               buf[0] = expected_token;
3030               buf[1] = '\0';
3031               expected_str = buf;
3032             }
3033         }
3034
3035       str = g_strdup_printf ("Parse error, expecting a %s '%s'",
3036                              (expected_str != buf) ? "valid" : "",
3037                              expected_str);
3038     }
3039
3040   if (priv->value_pos)
3041     line_start = priv->value_pos - 1;
3042   else
3043     line_start = priv->scanner->text - 1;
3044
3045   while (*line_start != '\n' &&
3046          line_start != priv->buffer)
3047     line_start--;
3048
3049   if (*line_start == '\n')
3050     line_start++;
3051
3052   if (priv->value_pos)
3053     pos = priv->value_pos - line_start + 1;
3054   else
3055     pos = priv->scanner->text - line_start - 1;
3056
3057   line_end = strchr (line_start, '\n');
3058
3059   if (line_end)
3060     line = g_strndup (line_start, (line_end - line_start));
3061   else
3062     line = g_strdup (line_start);
3063
3064   g_message ("CSS: %s\n"
3065              "%s, line %d, char %d:\n"
3066              "%*c %s\n"
3067              "%*c ^",
3068              str, priv->scanner->input_name,
3069              priv->scanner->line, priv->scanner->position,
3070              3, ' ', line,
3071              3 + pos, ' ');
3072
3073   g_free (line);
3074   g_free (str);
3075 }
3076
3077 static GTokenType
3078 parse_rule (GtkCssProvider  *css_provider,
3079             GScanner        *scanner,
3080             GError         **error)
3081 {
3082   GtkCssProviderPrivate *priv;
3083   GTokenType expected_token;
3084   SelectorPath *selector;
3085
3086   priv = css_provider->priv;
3087
3088   css_provider_push_scope (css_provider, SCOPE_SELECTOR);
3089
3090   /* Handle directives */
3091   if (scanner->token == G_TOKEN_IDENTIFIER &&
3092       scanner->value.v_identifier[0] == '@')
3093     {
3094       gchar *directive;
3095
3096       directive = &scanner->value.v_identifier[1];
3097
3098       if (strcmp (directive, "define-color") == 0)
3099         {
3100           GtkSymbolicColor *color;
3101           gchar *color_name, *color_str;
3102
3103           /* Directive is a color mapping */
3104           g_scanner_get_next_token (scanner);
3105
3106           if (scanner->token != G_TOKEN_IDENTIFIER)
3107             {
3108               scanner->user_data = "Color name";
3109               return G_TOKEN_IDENTIFIER;
3110             }
3111
3112           color_name = g_strdup (scanner->value.v_identifier);
3113           css_provider_push_scope (css_provider, SCOPE_VALUE);
3114           g_scanner_get_next_token (scanner);
3115
3116           if (scanner->token != G_TOKEN_IDENTIFIER)
3117             {
3118               scanner->user_data = "Color definition";
3119               return G_TOKEN_IDENTIFIER;
3120             }
3121
3122           color_str = g_strstrip (scanner->value.v_identifier);
3123           color = symbolic_color_parse (color_str, error);
3124
3125           if (!color)
3126             {
3127               scanner->user_data = "Color definition";
3128               return G_TOKEN_IDENTIFIER;
3129             }
3130
3131           g_hash_table_insert (priv->symbolic_colors, color_name, color);
3132
3133           css_provider_pop_scope (css_provider);
3134           g_scanner_get_next_token (scanner);
3135
3136           if (scanner->token != ';')
3137             return ';';
3138
3139           return G_TOKEN_NONE;
3140         }
3141       else if (strcmp (directive, "import") == 0)
3142         {
3143           GScanner *scanner_backup;
3144           GSList *state_backup;
3145           gboolean loaded;
3146           gchar *path = NULL;
3147
3148           css_provider_push_scope (css_provider, SCOPE_VALUE);
3149           g_scanner_get_next_token (scanner);
3150
3151           if (scanner->token == G_TOKEN_IDENTIFIER &&
3152               g_str_has_prefix (scanner->value.v_identifier, "url"))
3153             path = path_parse (css_provider,
3154                                g_strstrip (scanner->value.v_identifier),
3155                                error);
3156           else if (scanner->token == G_TOKEN_STRING)
3157             path = path_parse (css_provider,
3158                                g_strstrip (scanner->value.v_string),
3159                                error);
3160
3161           if (path == NULL)
3162             {
3163               scanner->user_data = "File URL";
3164               return G_TOKEN_IDENTIFIER;
3165             }
3166
3167           css_provider_pop_scope (css_provider);
3168           g_scanner_get_next_token (scanner);
3169
3170           if (scanner->token != ';')
3171             {
3172               g_free (path);
3173               return ';';
3174             }
3175
3176           /* Snapshot current parser state and scanner in order to restore after importing */
3177           state_backup = priv->state;
3178           scanner_backup = priv->scanner;
3179
3180           priv->state = NULL;
3181           priv->scanner = create_scanner ();
3182
3183           /* FIXME: Avoid recursive importing */
3184           loaded = gtk_css_provider_load_from_path_internal (css_provider, path,
3185                                                              FALSE, error);
3186
3187           /* Restore previous state */
3188           css_provider_reset_parser (css_provider);
3189           priv->state = state_backup;
3190           g_scanner_destroy (priv->scanner);
3191           priv->scanner = scanner_backup;
3192
3193           g_free (path);
3194
3195           if (!loaded)
3196             {
3197               scanner->user_data = "File URL";
3198               return G_TOKEN_IDENTIFIER;
3199             }
3200           else
3201             return G_TOKEN_NONE;
3202         }
3203       else
3204         {
3205           scanner->user_data = "Directive";
3206           return G_TOKEN_IDENTIFIER;
3207         }
3208     }
3209
3210   expected_token = parse_selector (css_provider, scanner, &selector);
3211
3212   if (expected_token != G_TOKEN_NONE)
3213     {
3214       selector_path_unref (selector);
3215       scanner->user_data = "Selector";
3216       return expected_token;
3217     }
3218
3219   priv->cur_selectors = g_slist_prepend (priv->cur_selectors, selector);
3220
3221   while (scanner->token == ',')
3222     {
3223       g_scanner_get_next_token (scanner);
3224
3225       expected_token = parse_selector (css_provider, scanner, &selector);
3226
3227       if (expected_token != G_TOKEN_NONE)
3228         {
3229           selector_path_unref (selector);
3230           scanner->user_data = "Selector";
3231           return expected_token;
3232         }
3233
3234       priv->cur_selectors = g_slist_prepend (priv->cur_selectors, selector);
3235     }
3236
3237   css_provider_pop_scope (css_provider);
3238
3239   if (scanner->token != G_TOKEN_LEFT_CURLY)
3240     return G_TOKEN_LEFT_CURLY;
3241
3242   /* Declarations parsing */
3243   css_provider_push_scope (css_provider, SCOPE_DECLARATION);
3244   g_scanner_get_next_token (scanner);
3245
3246   while (scanner->token == G_TOKEN_IDENTIFIER)
3247     {
3248       gchar *value_str = NULL;
3249       GtkStylePropertyParser parse_func = NULL;
3250       GParamSpec *pspec;
3251       gchar *prop;
3252
3253       prop = g_strdup (scanner->value.v_identifier);
3254       g_scanner_get_next_token (scanner);
3255
3256       if (scanner->token != ':')
3257         {
3258           g_free (prop);
3259           return ':';
3260         }
3261
3262       priv->value_pos = priv->scanner->text;
3263
3264       css_provider_push_scope (css_provider, SCOPE_VALUE);
3265       g_scanner_get_next_token (scanner);
3266
3267       if (scanner->token != G_TOKEN_IDENTIFIER)
3268         {
3269           g_free (prop);
3270           scanner->user_data = "Property value";
3271           return G_TOKEN_IDENTIFIER;
3272         }
3273
3274       value_str = scanner->value.v_identifier;
3275       SKIP_SPACES (value_str);
3276       g_strchomp (value_str);
3277
3278       if (gtk_style_properties_lookup_property (prop, &parse_func, &pspec))
3279         {
3280           GValue *val;
3281
3282           val = g_slice_new0 (GValue);
3283           g_value_init (val, pspec->value_type);
3284
3285           if (strcmp (value_str, "none") == 0)
3286             {
3287               /* Insert the default value, so it has an opportunity
3288                * to override other style providers when merged
3289                */
3290               g_param_value_set_default (pspec, val);
3291               g_hash_table_insert (priv->cur_properties, prop, val);
3292             }
3293           else if (pspec->value_type == G_TYPE_STRING)
3294             {
3295               g_value_set_string (val, value_str);
3296               g_hash_table_insert (priv->cur_properties, prop, val);
3297             }
3298           else if ((parse_func && (parse_func) (value_str, val, error)) ||
3299                    (!parse_func && css_provider_parse_value (css_provider, value_str, val, error)))
3300             g_hash_table_insert (priv->cur_properties, prop, val);
3301           else
3302             {
3303               g_value_unset (val);
3304               g_slice_free (GValue, val);
3305               g_free (prop);
3306
3307               scanner->user_data = "Property value";
3308               return G_TOKEN_IDENTIFIER;
3309             }
3310         }
3311       else if (prop[0] == '-' &&
3312                g_ascii_isupper (prop[1]))
3313         {
3314           GValue *val;
3315
3316           val = g_slice_new0 (GValue);
3317           g_value_init (val, G_TYPE_STRING);
3318           g_value_set_string (val, value_str);
3319
3320           g_hash_table_insert (priv->cur_properties, prop, val);
3321         }
3322       else
3323         g_free (prop);
3324
3325       css_provider_pop_scope (css_provider);
3326       g_scanner_get_next_token (scanner);
3327
3328       if (scanner->token != ';')
3329         break;
3330
3331       g_scanner_get_next_token (scanner);
3332     }
3333
3334   if (scanner->token != G_TOKEN_RIGHT_CURLY)
3335     return G_TOKEN_RIGHT_CURLY;
3336
3337   css_provider_pop_scope (css_provider);
3338
3339   return G_TOKEN_NONE;
3340 }
3341
3342 static gboolean
3343 parse_stylesheet (GtkCssProvider  *css_provider,
3344                   GError         **error)
3345 {
3346   GtkCssProviderPrivate *priv;
3347   gboolean result;
3348
3349   result = TRUE;
3350
3351   priv = css_provider->priv;
3352   g_scanner_get_next_token (priv->scanner);
3353
3354   while (!g_scanner_eof (priv->scanner))
3355     {
3356       GTokenType expected_token;
3357       GError *err = NULL;
3358
3359       css_provider_reset_parser (css_provider);
3360       expected_token = parse_rule (css_provider, priv->scanner, &err);
3361
3362       if (expected_token != G_TOKEN_NONE)
3363         {
3364           /* If a GError was passed in, propagate the error and bail out,
3365            * else report a warning and keep going
3366            */
3367           if (error != NULL)
3368             {
3369               result = FALSE;
3370               if (err)
3371                 g_propagate_error (error, err);
3372               else
3373                 g_set_error_literal (error,
3374                                      GTK_CSS_PROVIDER_ERROR,
3375                                      GTK_CSS_PROVIDER_ERROR_FAILED,
3376                                      "Error parsing stylesheet");
3377               break;
3378             }
3379           else
3380             {
3381               scanner_report_warning (css_provider, expected_token, err);
3382               g_clear_error (&err);
3383             }
3384
3385           while (!g_scanner_eof (priv->scanner) &&
3386                  priv->scanner->token != G_TOKEN_RIGHT_CURLY)
3387             g_scanner_get_next_token (priv->scanner);
3388         }
3389       else
3390         css_provider_commit (css_provider);
3391
3392       g_scanner_get_next_token (priv->scanner);
3393     }
3394
3395   return result;
3396 }
3397
3398 /**
3399  * gtk_css_provider_load_from_data:
3400  * @css_provider: a #GtkCssProvider
3401  * @data: CSS data loaded in memory
3402  * @length: the length of @data in bytes, or -1 for NUL terminated strings
3403  * @error: (out) (allow-none): return location for a #GError, or %NULL
3404  *
3405  * Loads @data into @css_provider, making it clear any previously loaded
3406  * information.
3407  *
3408  * Returns: %TRUE if the data could be loaded.
3409  **/
3410 gboolean
3411 gtk_css_provider_load_from_data (GtkCssProvider  *css_provider,
3412                                  const gchar     *data,
3413                                  gssize           length,
3414                                  GError         **error)
3415 {
3416   GtkCssProviderPrivate *priv;
3417
3418   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
3419   g_return_val_if_fail (data != NULL, FALSE);
3420
3421   priv = css_provider->priv;
3422
3423   if (length < 0)
3424     length = strlen (data);
3425
3426   if (priv->selectors_info->len > 0)
3427     g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
3428
3429   priv->scanner->input_name = "-";
3430   priv->buffer = data;
3431   g_scanner_input_text (priv->scanner, data, (guint) length);
3432
3433   g_free (priv->filename);
3434   priv->filename = NULL;
3435   priv->buffer = NULL;
3436
3437   return parse_stylesheet (css_provider, error);
3438 }
3439
3440 /**
3441  * gtk_css_provider_load_from_file:
3442  * @css_provider: a #GtkCssProvider
3443  * @file: #GFile pointing to a file to load
3444  * @error: (out) (allow-none): return location for a #GError, or %NULL
3445  *
3446  * Loads the data contained in @file into @css_provider, making it
3447  * clear any previously loaded information.
3448  *
3449  * Returns: %TRUE if the data could be loaded.
3450  **/
3451 gboolean
3452 gtk_css_provider_load_from_file (GtkCssProvider  *css_provider,
3453                                  GFile           *file,
3454                                  GError         **error)
3455 {
3456   GtkCssProviderPrivate *priv;
3457   GError *internal_error = NULL;
3458   gchar *data;
3459   gsize length;
3460   gboolean ret;
3461
3462   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
3463   g_return_val_if_fail (G_IS_FILE (file), FALSE);
3464
3465   priv = css_provider->priv;
3466
3467   if (!g_file_load_contents (file, NULL,
3468                              &data, &length,
3469                              NULL, &internal_error))
3470     {
3471       g_propagate_error (error, internal_error);
3472       return FALSE;
3473     }
3474
3475   if (priv->selectors_info->len > 0)
3476     g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
3477
3478   g_free (priv->filename);
3479   priv->filename = g_file_get_path (file);
3480
3481   priv->scanner->input_name = priv->filename;
3482   priv->buffer = data;
3483   g_scanner_input_text (priv->scanner, data, (guint) length);
3484
3485   ret = parse_stylesheet (css_provider, error);
3486
3487   priv->buffer = NULL;
3488   g_free (data);
3489
3490   return ret;
3491 }
3492
3493 static gboolean
3494 gtk_css_provider_load_from_path_internal (GtkCssProvider  *css_provider,
3495                                           const gchar     *path,
3496                                           gboolean         reset,
3497                                           GError         **error)
3498 {
3499   GtkCssProviderPrivate *priv;
3500   GError *internal_error = NULL;
3501   GMappedFile *mapped_file;
3502   const gchar *data;
3503   gsize length;
3504   gboolean ret;
3505
3506   priv = css_provider->priv;
3507
3508   mapped_file = g_mapped_file_new (path, FALSE, &internal_error);
3509
3510   if (internal_error)
3511     {
3512       g_propagate_error (error, internal_error);
3513       return FALSE;
3514     }
3515
3516   length = g_mapped_file_get_length (mapped_file);
3517   data = g_mapped_file_get_contents (mapped_file);
3518
3519   if (!data)
3520     data = "";
3521
3522   if (reset)
3523     {
3524       if (priv->selectors_info->len > 0)
3525         g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
3526
3527       g_free (priv->filename);
3528       priv->filename = g_strdup (path);
3529     }
3530
3531   priv->scanner->input_name = priv->filename;
3532   priv->buffer = data;
3533   g_scanner_input_text (priv->scanner, data, (guint) length);
3534
3535   ret = parse_stylesheet (css_provider, error);
3536
3537   priv->buffer = NULL;
3538   g_mapped_file_unref (mapped_file);
3539
3540   return ret;
3541 }
3542
3543 /**
3544  * gtk_css_provider_load_from_path:
3545  * @css_provider: a #GtkCssProvider
3546  * @path: the path of a filename to load, in the GLib filename encoding
3547  * @error: (out) (allow-none): return location for a #GError, or %NULL
3548  *
3549  * Loads the data contained in @path into @css_provider, making it clear
3550  * any previously loaded information.
3551  *
3552  * Returns: %TRUE if the data could be loaded.
3553  **/
3554 gboolean
3555 gtk_css_provider_load_from_path (GtkCssProvider  *css_provider,
3556                                  const gchar     *path,
3557                                  GError         **error)
3558 {
3559   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
3560   g_return_val_if_fail (path != NULL, FALSE);
3561
3562   return gtk_css_provider_load_from_path_internal (css_provider, path,
3563                                                    TRUE, error);
3564 }
3565
3566 /**
3567  * gtk_css_provider_get_default:
3568  *
3569  * Returns the provider containing the style settings used as a
3570  * fallback for all widgets.
3571  *
3572  * Returns: (transfer none): The provider used for fallback styling.
3573  *          This memory is owned by GTK+, and you must not free it.
3574  **/
3575 GtkCssProvider *
3576 gtk_css_provider_get_default (void)
3577 {
3578   static GtkCssProvider *provider;
3579
3580   if (G_UNLIKELY (!provider))
3581     {
3582       const gchar *str =
3583         "@define-color fg_color #000; \n"
3584         "@define-color bg_color #dcdad5; \n"
3585         "@define-color text_color #000; \n"
3586         "@define-color base_color #fff; \n"
3587         "@define-color selected_bg_color #4b6983; \n"
3588         "@define-color selected_fg_color #fff; \n"
3589         "@define-color tooltip_bg_color #eee1b3; \n"
3590         "@define-color tooltip_fg_color #000; \n"
3591         "\n"
3592         "@define-color info_fg_color rgb (181, 171, 156);\n"
3593         "@define-color info_bg_color rgb (252, 252, 189);\n"
3594         "@define-color warning_fg_color rgb (173, 120, 41);\n"
3595         "@define-color warning_bg_color rgb (250, 173, 61);\n"
3596         "@define-color question_fg_color rgb (97, 122, 214);\n"
3597         "@define-color question_bg_color rgb (138, 173, 212);\n"
3598         "@define-color error_fg_color rgb (166, 38, 38);\n"
3599         "@define-color error_bg_color rgb (237, 54, 54);\n"
3600         "\n"
3601         "*,\n"
3602         "GtkTreeView > GtkButton {\n"
3603         "  background-color: @bg_color;\n"
3604         "  color: @fg_color;\n"
3605         "  border-color: shade (@bg_color, 0.6);\n"
3606         "  padding: 2;\n"
3607         "  border-width: 0;\n"
3608         "}\n"
3609         "\n"
3610         "*:prelight {\n"
3611         "  background-color: shade (@bg_color, 1.05);\n"
3612         "  color: shade (@fg_color, 1.3);\n"
3613         "}\n"
3614         "\n"
3615         "*:selected {\n"
3616         "  background-color: @selected_bg_color;\n"
3617         "  color: @selected_fg_color;\n"
3618         "}\n"
3619         "\n"
3620         ".expander {\n"
3621         "  color: #fff;\n"
3622         "}\n"
3623         "\n"
3624         ".expander:prelight {\n"
3625         "  color: @text_color;\n"
3626         "}\n"
3627         "\n"
3628         ".expander:active {\n"
3629         "  transition: 300ms linear;\n"
3630         "}\n"
3631         "\n"
3632         "*:insensitive {\n"
3633         "  border-color: shade (@bg_color, 0.7);\n"
3634         "  background-color: shade (@bg_color, 0.9);\n"
3635         "  color: shade (@bg_color, 0.7);\n"
3636         "}\n"
3637         "\n"
3638         "GtkTreeView, GtkIconView, GtkTextView {\n"
3639         "  background-color: @base_color;\n"
3640         "  color: @text_color;\n"
3641         "}\n"
3642         "\n"
3643         "GtkTreeView > row {\n"
3644         "  background-color: @base_color;\n"
3645         "  color: @text_color;\n"
3646         "}\n"
3647         "\n"
3648         "GtkTreeView > row:nth-child(odd) { \n"
3649         "  background-color: shade (@base_color, 0.93); \n"
3650         "}\n"
3651         "\n"
3652         ".tooltip {\n"
3653         "  background-color: @tooltip_bg_color; \n"
3654         "  color: @tooltip_fg_color; \n"
3655         "}\n"
3656         "\n"
3657         ".button,\n"
3658         ".slider {\n"
3659         "  border-style: outset; \n"
3660         "  border-width: 2; \n"
3661         "}\n"
3662         "\n"
3663         ".button:active {\n"
3664         "  background-color: shade (@bg_color, 0.7);\n"
3665         "  border-style: inset; \n"
3666         "}\n"
3667         "\n"
3668         ".button:prelight,\n"
3669         ".slider:prelight {\n"
3670         "  background-color: @selected_bg_color;\n"
3671         "  color: @selected_fg_color;\n"
3672         "  border-color: shade (@selected_bg_color, 0.7);\n"
3673         "}\n"
3674         "\n"
3675         ".trough {\n"
3676         "  border-style: inset;\n"
3677         "  border-width: 1;\n"
3678         "}\n"
3679         "\n"
3680         ".entry {\n"
3681         "  border-style: inset;\n"
3682         "  border-width: 2;\n"
3683         "  background-color: @base_color;\n"
3684         "  color: @text_color;\n"
3685         "}\n"
3686         "\n"
3687         ".entry:insensitive {\n"
3688         "  background-color: shade (@base_color, 0.9);\n"
3689         "  color: shade (@base_color, 0.7);\n"
3690         "}\n"
3691         ".entry:active {\n"
3692         "  background-color: #c4c2bd;\n"
3693         "  color: #000;\n"
3694         "}\n"
3695         "\n"
3696         ".progressbar:prelight,\n"
3697         ".entry.progressbar {\n"
3698         "  background-color: @selected_bg_color;\n"
3699         "  border-color: shade (@selected_bg_color, 0.7);\n"
3700         "  color: @selected_fg_color;\n"
3701         "}\n"
3702         "\n"
3703         "GtkCheckButton:hover,\n"
3704         "GtkCheckButton:selected,\n"
3705         "GtkRadioButton:hover,\n"
3706         "GtkRadioButton:selected {\n"
3707         "  background-color: shade (@bg_color, 1.05);\n"
3708         "}\n"
3709         "\n"
3710         ".check, .radio,\n"
3711         ".check:active, .radio:active,\n"
3712         ".check:hover, .radio:hover {\n"
3713         "  background-color: @base_color;\n"
3714         "  border-color: @fg_color;\n"
3715         "  color: @text_color;\n"
3716         "  border-style: solid;\n"
3717         "  border-width: 1;\n"
3718         "}\n"
3719         "\n"
3720         ".check:selected, .radio:selected {\n"
3721         "  background-color: @selected_bg_color;\n"
3722         "  color: @selected_fg_color;\n"
3723         "}\n"
3724         "\n"
3725         ".menu.check, .menu.radio {\n"
3726         "  color: @fg_color;\n"
3727         "}\n"
3728         "\n"
3729         ".menu:hover {\n"
3730         "  background-color: @selected_bg_color;\n"
3731         "  border-style: none;\n"
3732         "}\n"
3733         "\n"
3734         ".popup {\n"
3735         "  border-style: outset;\n"
3736         "  border-width: 1;\n"
3737         "}\n"
3738         "\n"
3739         ".viewport {\n"
3740         "  border-style: inset;\n"
3741         "  border-width: 2;\n"
3742         "}\n"
3743         "\n"
3744         ".notebook {\n"
3745         "  border-style: outset;\n"
3746         "  border-width: 1;\n"
3747         "}\n"
3748         "\n"
3749         ".frame {\n"
3750         "  border-style: inset;\n"
3751         "  border-width: 1;\n"
3752         "}\n"
3753         "\n"
3754         ".menu,\n"
3755         ".menubar,\n"
3756         ".toolbar {\n"
3757         "  border-style: outset;\n"
3758         "  border-width: 1;\n"
3759         "}\n"
3760         "\n"
3761         ".menu:hover,\n"
3762         ".menubar:hover {\n"
3763         "  background-color: @selected_bg_color;\n"
3764         "  color: @selected_fg_color;\n"
3765         "}\n"
3766         "\n"
3767         ".menu .check,\n"
3768         ".menu .radio,\n"
3769         ".menu .check:active,\n"
3770         ".menu .radio:active {\n"
3771         "  border-style: none;\n"
3772         "}\n"
3773         "\n"
3774         "GtkSpinButton.button {\n"
3775         "  border-width: 1;\n"
3776         "}\n"
3777         "\n"
3778         ".scale.slider:hover,\n"
3779         "GtkSpinButton.button:hover {\n"
3780         "  background-color: shade (@bg_color, 1.05);\n"
3781         "  border-color: shade (@bg_color, 0.8);\n"
3782         "}\n"
3783         "\n"
3784         "GtkToggleButton.button:inconsistent {\n"
3785         "  border-style: outset;\n"
3786         "  border-width: 1px;\n"
3787         "  background-color: shade (@bg_color, 0.9);\n"
3788         "  border-color: shade (@bg_color, 0.7);\n"
3789         "}\n"
3790         "\n"
3791         "GtkLabel:selected {\n"
3792         "  background-color: shade (@bg_color, 0.9);\n"
3793         "  color: @fg_color;\n"
3794         "}\n"
3795         "\n"
3796         "GtkLabel:selected:focused {\n"
3797         "  background-color: @selected_bg_color;\n"
3798         "  color: @selected_fg_color;\n"
3799         "}\n"
3800         "\n"
3801         ".spinner:active {\n"
3802         "  transition: 750ms linear loop;\n"
3803         "}\n"
3804         "\n"
3805         ".info {\n"
3806         "  background-color: @info_bg_color;\n"
3807         "  color: @info_fg_color;\n"
3808         "}\n"
3809         "\n"
3810         ".warning {\n"
3811         "  background-color: @warning_bg_color;\n"
3812         "  color: @warning_fg_color;\n"
3813         "}\n"
3814         "\n"
3815         ".question {\n"
3816         "  background-color: @question_bg_color;\n"
3817         "  color: @question_fg_color;\n"
3818         "}\n"
3819         "\n"
3820         ".error {\n"
3821         "  background-color: @error_bg_color;\n"
3822         "  color: @error_fg_color;\n"
3823         "}\n"
3824         "\n";
3825
3826       provider = gtk_css_provider_new ();
3827       if (!gtk_css_provider_load_from_data (provider, str, -1, NULL))
3828         {
3829           g_error ("Failed to load the internal default CSS.");
3830         }
3831     }
3832
3833   return provider;
3834 }
3835
3836 static gchar *
3837 css_provider_get_theme_dir (void)
3838 {
3839   const gchar *var;
3840   gchar *path;
3841
3842   var = g_getenv ("GTK_DATA_PREFIX");
3843
3844   if (var)
3845     path = g_build_filename (var, "share", "themes", NULL);
3846   else
3847     path = g_build_filename (GTK_DATA_PREFIX, "share", "themes", NULL);
3848
3849   return path;
3850 }
3851
3852 /**
3853  * gtk_css_provider_get_named:
3854  * @name: A theme name
3855  * @variant: variant to load, for example, "dark", or %NULL for the default
3856  *
3857  * Loads a theme from the usual theme paths
3858  *
3859  * Returns: (transfer none): a #GtkCssProvider with the theme loaded.
3860  *          This memory is owned by GTK+, and you must not free it.
3861  **/
3862 GtkCssProvider *
3863 gtk_css_provider_get_named (const gchar *name,
3864                             const gchar *variant)
3865 {
3866   static GHashTable *themes = NULL;
3867   GtkCssProvider *provider;
3868
3869   if (G_UNLIKELY (!themes))
3870     themes = g_hash_table_new (g_str_hash, g_str_equal);
3871
3872   provider = g_hash_table_lookup (themes, name);
3873
3874   if (!provider)
3875     {
3876       const gchar *home_dir;
3877       gchar *subpath, *path = NULL;
3878
3879       if (variant)
3880         subpath = g_strdup_printf ("gtk-3.0" G_DIR_SEPARATOR_S "gtk-%s.css", variant);
3881       else
3882         subpath = g_strdup ("gtk-3.0" G_DIR_SEPARATOR_S "gtk.css");
3883
3884       /* First look in the users home directory
3885        */
3886       home_dir = g_get_home_dir ();
3887       if (home_dir)
3888         {
3889           path = g_build_filename (home_dir, ".themes", name, subpath, NULL);
3890
3891           if (!g_file_test (path, G_FILE_TEST_EXISTS))
3892             {
3893               g_free (path);
3894               path = NULL;
3895             }
3896         }
3897
3898       if (!path)
3899         {
3900           gchar *theme_dir = css_provider_get_theme_dir ();
3901           path = g_build_filename (theme_dir, name, subpath, NULL);
3902           g_free (theme_dir);
3903
3904           if (!g_file_test (path, G_FILE_TEST_EXISTS))
3905             {
3906               g_free (path);
3907               path = NULL;
3908             }
3909         }
3910
3911       g_free (subpath);
3912
3913       if (path)
3914         {
3915           GError *error = NULL;
3916
3917           provider = gtk_css_provider_new ();
3918           gtk_css_provider_load_from_path (provider, path, &error);
3919
3920           if (error)
3921             {
3922               g_warning ("Could not load named theme \"%s\": %s", name, error->message);
3923               g_error_free (error);
3924
3925               g_object_unref (provider);
3926               provider = NULL;
3927             }
3928           else
3929             g_hash_table_insert (themes, g_strdup (name), provider);
3930         }
3931     }
3932
3933   return provider;
3934 }