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