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