]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssprovider.c
css: Make _gtk_css_selector_matches() take a path length
[~andy/gtk] / gtk / gtkcssprovider.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include <string.h>
23 #include <stdlib.h>
24
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26 #include <cairo-gobject.h>
27
28 #include "gtkcssproviderprivate.h"
29
30 #include "gtkcssparserprivate.h"
31 #include "gtkcssselectorprivate.h"
32 #include "gtkcssstringfuncsprivate.h"
33 #include "gtksymboliccolor.h"
34 #include "gtkstyleprovider.h"
35 #include "gtkstylecontextprivate.h"
36 #include "gtkbindings.h"
37 #include "gtkmarshalers.h"
38 #include "gtkprivate.h"
39 #include "gtkintl.h"
40
41 /**
42  * SECTION:gtkcssprovider
43  * @Short_description: CSS-like styling for widgets
44  * @Title: GtkCssProvider
45  * @See_also: #GtkStyleContext, #GtkStyleProvider
46  *
47  * GtkCssProvider is an object implementing the #GtkStyleProvider interface.
48  * It is able to parse <ulink url="http://www.w3.org/TR/CSS2">CSS</ulink>-like
49  * input in order to style widgets.
50  *
51  * <refsect2 id="gtkcssprovider-files">
52  * <title>Default files</title>
53  * <para>
54  * An application can cause GTK+ to parse a specific CSS style sheet by
55  * calling gtk_css_provider_load_from_file() and adding the provider with
56  * gtk_style_context_add_provider() or gtk_style_context_add_provider_for_screen().
57  * In addition, certain files will be read when GTK+ is initialized. First,
58  * the file <filename><envar>$XDG_CONFIG_HOME</envar>/gtk-3.0/gtk.css</filename>
59  * is loaded if it exists. Then, GTK+ tries to load
60  * <filename><envar>$HOME</envar>/.themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
61  * falling back to
62  * <filename><replaceable>datadir</replaceable>/share/themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
63  * where <replaceable>theme-name</replaceable> is the name of the current theme
64  * (see the #GtkSettings:gtk-theme-name setting) and <replaceable>datadir</replaceable>
65  * is the prefix configured when GTK+ was compiled, unless overridden by the
66  * <envar>GTK_DATA_PREFIX</envar> environment variable.
67  * </para>
68  * </refsect2>
69  * <refsect2 id="gtkcssprovider-stylesheets">
70  * <title>Style sheets</title>
71  * <para>
72  * The basic structure of the style sheets understood by this provider is
73  * a series of statements, which are either rule sets or '@-rules', separated
74  * by whitespace.
75  * </para>
76  * <para>
77  * A rule set consists of a selector and a declaration block, which is
78  * a series of declarations enclosed in curly braces ({ and }). The
79  * declarations are separated by semicolons (;). Multiple selectors can
80  * share the same declaration block, by putting all the separators in
81  * front of the block, separated by commas.
82  * </para>
83  * <example><title>A rule set with two selectors</title>
84  * <programlisting language="text">
85  * GtkButton, GtkEntry {
86  *     color: &num;ff00ea;
87  *     font: Comic Sans 12
88  * }
89  * </programlisting>
90  * </example>
91  * </refsect2>
92  * <refsect2 id="gtkcssprovider-selectors">
93  * <title>Selectors</title>
94  * <para>
95  * Selectors work very similar to the way they do in CSS, with widget class
96  * names taking the role of element names, and widget names taking the role
97  * of IDs. When used in a selector, widget names must be prefixed with a
98  * '&num;' character. The '*' character represents the so-called universal
99  * selector, which matches any widget.
100  * </para>
101  * <para>
102  * To express more complicated situations, selectors can be combined in
103  * various ways:
104  * <itemizedlist>
105  * <listitem><para>To require that a widget satisfies several conditions,
106  *   combine several selectors into one by concatenating them. E.g.
107  *   <literal>GtkButton&num;button1</literal> matches a GtkButton widget
108  *   with the name button1.</para></listitem>
109  * <listitem><para>To only match a widget when it occurs inside some other
110  *   widget, write the two selectors after each other, separated by whitespace.
111  *   E.g. <literal>GtkToolBar GtkButton</literal> matches GtkButton widgets
112  *   that occur inside a GtkToolBar.</para></listitem>
113  * <listitem><para>In the previous example, the GtkButton is matched even
114  *   if it occurs deeply nested inside the toolbar. To restrict the match
115  *   to direct children of the parent widget, insert a '>' character between
116  *   the two selectors. E.g. <literal>GtkNotebook > GtkLabel</literal> matches
117  *   GtkLabel widgets that are direct children of a GtkNotebook.</para></listitem>
118  * </itemizedlist>
119  * </para>
120  * <example>
121  * <title>Widget classes and names in selectors</title>
122  * <programlisting language="text">
123  * /&ast; Theme labels that are descendants of a window &ast;/
124  * GtkWindow GtkLabel {
125  *     background-color: &num;898989
126  * }
127  *
128  * /&ast; Theme notebooks, and anything that's within these &ast;/
129  * GtkNotebook {
130  *     background-color: &num;a939f0
131  * }
132  *
133  * /&ast; Theme combo boxes, and entries that
134  *  are direct children of a notebook &ast;/
135  * GtkComboBox,
136  * GtkNotebook > GtkEntry {
137  *     color: @fg_color;
138  *     background-color: &num;1209a2
139  * }
140  *
141  * /&ast; Theme any widget within a GtkBin &ast;/
142  * GtkBin * {
143  *     font-name: Sans 20
144  * }
145  *
146  * /&ast; Theme a label named title-label &ast;/
147  * GtkLabel&num;title-label {
148  *     font-name: Sans 15
149  * }
150  *
151  * /&ast; Theme any widget named main-entry &ast;/
152  * &num;main-entry {
153  *     background-color: &num;f0a810
154  * }
155  * </programlisting>
156  * </example>
157  * <para>
158  * Widgets may also define style classes, which can be used for matching.
159  * When used in a selector, style classes must be prefixed with a '.'
160  * character.
161  * </para>
162  * <para>
163  * Refer to the documentation of individual widgets to learn which
164  * style classes they define and see <xref linkend="gtkstylecontext-classes"/>
165  * for a list of all style classes used by GTK+ widgets.
166  * </para>
167  * <para>
168  * Note that there is some ambiguity in the selector syntax when it comes
169  * to differentiation widget class names from regions. GTK+ currently treats
170  * a string as a widget class name if it contains any uppercase characters
171  * (which should work for more widgets with names like GtkLabel).
172  * </para>
173  * <example>
174  * <title>Style classes in selectors</title>
175  * <programlisting language="text">
176  * /&ast; Theme all widgets defining the class entry &ast;/
177  * .entry {
178  *     color: &num;39f1f9;
179  * }
180  *
181  * /&ast; Theme spinbuttons' entry &ast;/
182  * GtkSpinButton.entry {
183  *     color: &num;900185
184  * }
185  * </programlisting>
186  * </example>
187  * <para>
188  * In complicated widgets like e.g. a GtkNotebook, it may be desirable
189  * to style different parts of the widget differently. To make this
190  * possible, container widgets may define regions, whose names
191  * may be used for matching in selectors.
192  * </para>
193  * <para>
194  * Some containers allow to further differentiate between regions by
195  * applying so-called pseudo-classes to the region. For example, the
196  * tab region in GtkNotebook allows to single out the first or last
197  * tab by using the :first-child or :last-child pseudo-class.
198  * When used in selectors, pseudo-classes must be prefixed with a
199  * ':' character.
200  * </para>
201  * <para>
202  * Refer to the documentation of individual widgets to learn which
203  * regions and pseudo-classes they define and see
204  * <xref linkend="gtkstylecontext-classes"/> for a list of all regions
205  * used by GTK+ widgets.
206  * </para>
207  * <example>
208  * <title>Regions in selectors</title>
209  * <programlisting language="text">
210  * /&ast; Theme any label within a notebook &ast;/
211  * GtkNotebook GtkLabel {
212  *     color: &num;f90192;
213  * }
214  *
215  * /&ast; Theme labels within notebook tabs &ast;/
216  * GtkNotebook tab GtkLabel {
217  *     color: &num;703910;
218  * }
219  *
220  * /&ast; Theme labels in the any first notebook
221  *  tab, both selectors are equivalent &ast;/
222  * GtkNotebook tab:nth-child(first) GtkLabel,
223  * GtkNotebook tab:first-child GtkLabel {
224  *     color: &num;89d012;
225  * }
226  * </programlisting>
227  * </example>
228  * <para>
229  * Another use of pseudo-classes is to match widgets depending on their
230  * state. This is conceptually similar to the :hover, :active or :focus
231  * pseudo-classes in CSS. The available pseudo-classes for widget states
232  * are :active, :prelight (or :hover), :insensitive, :selected, :focused
233  * and :inconsistent.
234  * </para>
235  * <example>
236  * <title>Styling specific widget states</title>
237  * <programlisting language="text">
238  * /&ast; Theme active (pressed) buttons &ast;/
239  * GtkButton:active {
240  *     background-color: &num;0274d9;
241  * }
242  *
243  * /&ast; Theme buttons with the mouse pointer on it,
244  *    both are equivalent &ast;/
245  * GtkButton:hover,
246  * GtkButton:prelight {
247  *     background-color: &num;3085a9;
248  * }
249  *
250  * /&ast; Theme insensitive widgets, both are equivalent &ast;/
251  * :insensitive,
252  * *:insensitive {
253  *     background-color: &num;320a91;
254  * }
255  *
256  * /&ast; Theme selection colors in entries &ast;/
257  * GtkEntry:selected {
258  *     background-color: &num;56f9a0;
259  * }
260  *
261  * /&ast; Theme focused labels &ast;/
262  * GtkLabel:focused {
263  *     background-color: &num;b4940f;
264  * }
265  *
266  * /&ast; Theme inconsistent checkbuttons &ast;/
267  * GtkCheckButton:inconsistent {
268  *     background-color: &num;20395a;
269  * }
270  * </programlisting>
271  * </example>
272  * <para>
273  * Widget state pseudoclasses may only apply to the last element
274  * in a selector.
275  * </para>
276  * <para>
277  * To determine the effective style for a widget, all the matching rule
278  * sets are merged. As in CSS, rules apply by specificity, so the rules
279  * whose selectors more closely match a widget path will take precedence
280  * over the others.
281  * </para>
282  * </refsect2>
283  * <refsect2 id="gtkcssprovider-rules">
284  * <title>&commat; Rules</title>
285  * <para>
286  * GTK+'s CSS supports the &commat;import rule, in order to load another
287  * CSS style sheet in addition to the currently parsed one.
288  * </para>
289  * <example>
290  * <title>Using the &commat;import rule</title>
291  * <programlisting language="text">
292  * &commat;import url ("path/to/common.css");
293  * </programlisting>
294  * </example>
295  * <para id="css-binding-set">
296  * In order to extend key bindings affecting different widgets, GTK+
297  * supports the &commat;binding-set rule to parse a set of bind/unbind
298  * directives, see #GtkBindingSet for the supported syntax. Note that
299  * the binding sets defined in this way must be associated with rule sets
300  * by setting the gtk-key-bindings style property.
301  * </para>
302  * <para>
303  * Customized key bindings are typically defined in a separate
304  * <filename>gtk-keys.css</filename> CSS file and GTK+ loads this file
305  * according to the current key theme, which is defined by the
306  * #GtkSettings:gtk-key-theme-name setting.
307  * </para>
308  * <example>
309  * <title>Using the &commat;binding rule</title>
310  * <programlisting language="text">
311  * &commat;binding-set binding-set1 {
312  *   bind "&lt;alt&gt;Left" { "move-cursor" (visual-positions, -3, 0) };
313  *   unbind "End";
314  * };
315  *
316  * &commat;binding-set binding-set2 {
317  *   bind "&lt;alt&gt;Right" { "move-cursor" (visual-positions, 3, 0) };
318  *   bind "&lt;alt&gt;KP_space" { "delete-from-cursor" (whitespace, 1)
319  *                          "insert-at-cursor" (" ") };
320  * };
321  *
322  * GtkEntry {
323  *   gtk-key-bindings: binding-set1, binding-set2;
324  * }
325  * </programlisting>
326  * </example>
327  * <para>
328  * GTK+ also supports an additional &commat;define-color rule, in order
329  * to define a color name which may be used instead of color numeric
330  * representations. Also see the #GtkSettings:gtk-color-scheme setting
331  * for a way to override the values of these named colors.
332  * </para>
333  * <example>
334  * <title>Defining colors</title>
335  * <programlisting language="text">
336  * &commat;define-color bg_color &num;f9a039;
337  *
338  * &ast; {
339  *     background-color: &commat;bg_color;
340  * }
341  * </programlisting>
342  * </example>
343  * </refsect2>
344  * <refsect2 id="gtkcssprovider-symbolic-colors">
345  * <title>Symbolic colors</title>
346  * <para>
347  * Besides being able to define color names, the CSS parser is also able
348  * to read different color expressions, which can also be nested, providing
349  * a rich language to define colors which are derived from a set of base
350  * colors.
351  * </para>
352  * <example>
353  * <title>Using symbolic colors</title>
354  * <programlisting language="text">
355  * &commat;define-color entry-color shade (&commat;bg_color, 0.7);
356  *
357  * GtkEntry {
358  *     background-color: @entry-color;
359  * }
360  *
361  * GtkEntry:focused {
362  *     background-color: mix (&commat;entry-color,
363  *                            shade (&num;fff, 0.5),
364  *                            0.8);
365  * }
366  * </programlisting>
367  * </example>
368  * <para>
369  *   The various ways to express colors in GTK+ CSS are:
370  * </para>
371  * <informaltable>
372  *   <tgroup cols="3">
373  *     <thead>
374  *       <row>
375  *         <entry>Syntax</entry>
376  *         <entry>Explanation</entry>
377  *         <entry>Examples</entry>
378  *       </row>
379  *     </thead>
380  *     <tbody>
381  *       <row>
382  *         <entry>rgb(@r, @g, @b)</entry>
383  *         <entry>An opaque color; @r, @g, @b can be either integers between
384  *                0 and 255 or percentages</entry>
385  *         <entry><literallayout>rgb(128, 10, 54)
386  * rgb(20%, 30%, 0%)</literallayout></entry>
387  *       </row>
388  *       <row>
389  *         <entry>rgba(@r, @g, @b, @a)</entry>
390  *         <entry>A translucent color; @r, @g, @b are as in the previous row,
391  *                @a is a floating point number between 0 and 1</entry>
392  *         <entry><literallayout>rgba(255, 255, 0, 0.5)</literallayout></entry>
393  *       </row>
394  *       <row>
395  *         <entry>&num;@xxyyzz</entry>
396  *         <entry>An opaque color; @xx, @yy, @zz are hexadecimal numbers
397  *                specifying @r, @g, @b variants with between 1 and 4
398  *                hexadecimal digits per component are allowed</entry>
399  *         <entry><literallayout>&num;ff12ab
400  * &num;f0c</literallayout></entry>
401  *       </row>
402  *       <row>
403  *         <entry>&commat;name</entry>
404  *         <entry>Reference to a color that has been defined with
405  *                &commat;define-color
406  *         </entry>
407  *         <entry>&commat;bg_color</entry>
408  *       </row>
409  *       <row>
410  *         <entry>mix(@color1, @color2, @f)</entry>
411  *         <entry>A linear combination of @color1 and @color2. @f is a
412  *                floating point number between 0 and 1.</entry>
413  *         <entry><literallayout>mix(&num;ff1e0a, &commat;bg_color, 0.8)</literallayout></entry>
414  *       </row>
415  *       <row>
416  *         <entry>shade(@color, @f)</entry>
417  *         <entry>A lighter or darker variant of @color. @f is a
418  *                floating point number.
419  *         </entry>
420  *         <entry>shade(&commat;fg_color, 0.5)</entry>
421  *       </row>
422  *       <row>
423  *         <entry>lighter(@color)</entry>
424  *         <entry>A lighter variant of @color</entry>
425  *       </row>
426  *       <row>
427  *         <entry>darker(@color)</entry>
428  *         <entry>A darker variant of @color</entry>
429  *       </row>
430  *     </tbody>
431  *   </tgroup>
432  * </informaltable>
433  * </refsect2>
434  * <refsect2 id="gtkcssprovider-gradients">
435  * <title>Gradients</title>
436  * <para>
437  * Linear or radial Gradients can be used as background images.
438  * </para>
439  * <para>
440  * A linear gradient along the line from (@start_x, @start_y) to
441  * (@end_x, @end_y) is specified using the syntax
442  * <literallayout>-gtk-gradient (linear,
443  *               @start_x @start_y, @end_x @end_y,
444  *               color-stop (@position, @color),
445  *               ...)</literallayout>
446  * where @start_x and @end_x can be either a floating point number between
447  * 0 and 1 or one of the special values 'left', 'right' or 'center', @start_y
448  * and @end_y can be either a floating point number between 0 and 1 or one
449  * of the special values 'top', 'bottom' or 'center', @position is a floating
450  * point number between 0 and 1 and @color is a color expression (see above).
451  * The color-stop can be repeated multiple times to add more than one color
452  * stop. 'from (@color)' and 'to (@color)' can be used as abbreviations for
453  * color stops with position 0 and 1, respectively.
454  * </para>
455  * <example>
456  * <title>A linear gradient</title>
457  * <inlinegraphic fileref="gradient1.png" format="PNG"/>
458  * <para>This gradient was specified with
459  * <literallayout>-gtk-gradient (linear,
460  *                left top, right bottom,
461  *                from(&commat;yellow), to(&commat;blue))</literallayout></para>
462  * </example>
463  * <example>
464  * <title>Another linear gradient</title>
465  * <inlinegraphic fileref="gradient2.png" format="PNG"/>
466  * <para>This gradient was specified with
467  * <literallayout>-gtk-gradient (linear,
468  *                0 0, 0 1,
469  *                color-stop(0, &commat;yellow),
470  *                color-stop(0.2, &commat;blue),
471  *                color-stop(1, &num;0f0))</literallayout></para>
472  * </example>
473  * <para>
474  * A radial gradient along the two circles defined by (@start_x, @start_y,
475  * @start_radius) and (@end_x, @end_y, @end_radius) is specified using the
476  * syntax
477  * <literallayout>-gtk-gradient (radial,
478  *                @start_x @start_y, @start_radius,
479  *                @end_x @end_y, @end_radius,
480  *                color-stop (@position, @color),
481  *                ...)</literallayout>
482  * where @start_radius and @end_radius are floating point numbers and
483  * the other parameters are as before.
484  * </para>
485  * <example>
486  * <title>A radial gradient</title>
487  * <inlinegraphic fileref="gradient3.png" format="PNG"/>
488  * <para>This gradient was specified with
489  * <literallayout>-gtk-gradient (radial,
490  *                center center, 0,
491  *                center center, 1,
492  *                from(&commat;yellow), to(&commat;green))</literallayout></para>
493  * </example>
494  * <example>
495  * <title>Another radial gradient</title>
496  * <inlinegraphic fileref="gradient4.png" format="PNG"/>
497  * <para>This gradient was specified with
498  * <literallayout>-gtk-gradient (radial,
499  *                0.4 0.4, 0.1,
500  *                0.6 0.6, 0.7,
501  *                color-stop (0, &num;f00),
502  *                color-stop (0.1, &num;a0f),
503  *                color-stop (0.2, &commat;yellow),
504  *                color-stop (1, &commat;green))</literallayout></para>
505  * </example>
506  * </refsect2>
507  * <refsect2 id="gtkcssprovider-slices">
508  * <title>Border images</title>
509  * <para>
510  * Images can be used in 'slices' for the purpose of creating scalable
511  * borders.
512  * </para>
513  * <inlinegraphic fileref="slices.png" format="PNG"/>
514  * <para>
515  * The syntax for specifying border images of this kind is:
516  * <literallayout>url(@path) @top @right @bottom @left [repeat|stretch]? [repeat|stretch]?</literallayout>
517  * The sizes of the 'cut off' portions are specified
518  * with the @top, @right, @bottom and @left parameters.
519  * The 'middle' sections can be repeated or stretched to create
520  * the desired effect, by adding the 'repeat' or 'stretch' options after
521  * the dimensions. If two options are specified, the first one affects
522  * the horizontal behaviour and the second one the vertical behaviour.
523  * If only one option is specified, it affects both.
524  * </para>
525  * <example>
526  * <title>A border image</title>
527  * <inlinegraphic fileref="border1.png" format="PNG"/>
528  * <para>This border image was specified with
529  * <literallayout>url("gradient1.png") 10 10 10 10</literallayout>
530  * </para>
531  * </example>
532  * <example>
533  * <title>A repeating border image</title>
534  * <inlinegraphic fileref="border2.png" format="PNG"/>
535  * <para>This border image was specified with
536  * <literallayout>url("gradient1.png") 10 10 10 10 repeat</literallayout>
537  * </para>
538  * </example>
539  * <example>
540  * <title>A stretched border image</title>
541  * <inlinegraphic fileref="border3.png" format="PNG"/>
542  * <para>This border image was specified with
543  * <literallayout>url("gradient1.png") 10 10 10 10 stretch</literallayout>
544  * </para>
545  * </example>
546  * </refsect2>
547  * <refsect2 id="gtkcssprovider-transitions">
548  * <para>Styles can specify transitions that will be used to create a gradual
549  * change in the appearance when a widget state changes. The following
550  * syntax is used to specify transitions:
551  * <literallayout>@duration [s|ms] [linear|ease|ease-in|ease-out|ease-in-out] [loop]?</literallayout>
552  * The @duration is the amount of time that the animation will take for
553  * a complete cycle from start to end. If the loop option is given, the
554  * animation will be repated until the state changes again.
555  * The option after the duration determines the transition function from a
556  * small set of predefined functions.
557  * <figure><title>Linear transition</title>
558  * <graphic fileref="linear.png" format="PNG"/>
559  * </figure>
560  * <figure><title>Ease transition</title>
561  * <graphic fileref="ease.png" format="PNG"/>
562  * </figure>
563  * <figure><title>Ease-in-out transition</title>
564  * <graphic fileref="ease-in-out.png" format="PNG"/>
565  * </figure>
566  * <figure><title>Ease-in transition</title>
567  * <graphic fileref="ease-in.png" format="PNG"/>
568  * </figure>
569  * <figure><title>Ease-out transition</title>
570  * <graphic fileref="ease-out.png" format="PNG"/>
571  * </figure>
572  * </para>
573  * </refsect2>
574  * <refsect2 id="gtkcssprovider-properties">
575  * <title>Supported properties</title>
576  * <para>
577  * Properties are the part that differ the most to common CSS,
578  * not all properties are supported (some are planned to be
579  * supported eventually, some others are meaningless or don't
580  * map intuitively in a widget based environment).
581  * </para>
582  * <para>
583  * There is also a difference in shorthand properties, for
584  * example in common CSS it is fine to define a font through
585  * the different @font-family, @font-style, @font-size
586  * properties, meanwhile in GTK+'s CSS only the canonical
587  * @font property is supported.
588  * </para>
589  * <para>
590  * The currently supported properties are:
591  * </para>
592  * <informaltable>
593  *   <tgroup cols="4">
594  *     <thead>
595  *       <row>
596  *         <entry>Property name</entry>
597  *         <entry>Syntax</entry>
598  *         <entry>Maps to</entry>
599  *         <entry>Examples</entry>
600  *       </row>
601  *     </thead>
602  *     <tbody>
603  *       <row>
604  *         <entry>engine</entry>
605  *         <entry>engine-name</entry>
606  *         <entry>#GtkThemingEngine</entry>
607  *         <entry>engine: clearlooks;
608  *  engine: none; /&ast; use the default (i.e. builtin) engine) &ast;/ </entry>
609  *       </row>
610  *       <row>
611  *         <entry>background-color</entry>
612  *         <entry morerows="2">color (see above)</entry>
613  *         <entry morerows="2">#GdkRGBA</entry>
614  *         <entry morerows="2"><literallayout>background-color: &num;fff;
615  * color: &amp;color1;
616  * background-color: shade (&amp;color1, 0.5);
617  * color: mix (&amp;color1, &num;f0f, 0.8);</literallayout>
618  *         </entry>
619  *       </row>
620  *       <row>
621  *         <entry>color</entry>
622  *       </row>
623  *       <row>
624  *         <entry>border-color</entry>
625  *       </row>
626  *       <row>
627  *         <entry>font</entry>
628  *         <entry>@family [@style] [@size]</entry>
629  *         <entry>#PangoFontDescription</entry>
630  *         <entry>font: Sans 15;</entry>
631  *       </row>
632  *       <row>
633  *         <entry>margin</entry>
634  *         <entry morerows="1"><literallayout>@width
635  * @vertical_width @horizontal_width
636  * @top_width @horizontal_width @bottom_width
637  * @top_width @right_width @bottom_width @left_width</literallayout>
638  *         </entry>
639  *         <entry morerows="1">#GtkBorder</entry>
640  *         <entry morerows="1"><literallayout>margin: 5;
641  * margin: 5 10;
642  * margin: 5 10 3;
643  * margin: 5 10 3 5;</literallayout>
644  *         </entry>
645  *       </row>
646  *       <row>
647  *         <entry>padding</entry>
648  *       </row>
649  *       <row>
650  *         <entry>background-image</entry>
651  *         <entry><literallayout>gradient (see above) or
652  * url(@path)</literallayout></entry>
653  *         <entry>#cairo_pattern_t</entry>
654  *         <entry><literallayout>-gtk-gradient (linear,
655  *                left top, right top,
656  *                from (&num;fff), to (&num;000));
657  * -gtk-gradient (linear, 0.0 0.5, 0.5 1.0,
658  *                from (&num;fff),
659  *                color-stop (0.5, &num;f00),
660  *                to (&num;000));
661  * -gtk-gradient (radial,
662  *                center center, 0.2,
663  *                center center, 0.8,
664  *                color-stop (0.0, &num;fff),
665  *                color-stop (1.0, &num;000));
666  * url ('background.png');</literallayout>
667  *         </entry>
668  *       </row>
669  *       <row>
670  *         <entry>border-width</entry>
671  *         <entry>integer</entry>
672  *         <entry>#gint</entry>
673  *         <entry>border-width: 5;</entry>
674  *       </row>
675  *       <row>
676  *         <entry>border-radius</entry>
677  *         <entry>integer</entry>
678  *         <entry>#gint</entry>
679  *         <entry>border-radius: 5;</entry>
680  *       </row>
681  *       <row>
682  *         <entry>border-style</entry>
683  *         <entry>[none|solid|inset|outset]</entry>
684  *         <entry>#GtkBorderStyle</entry>
685  *         <entry>border-style: solid;</entry>
686  *       </row>
687  *       <row>
688  *         <entry>border-image</entry>
689  *         <entry><literallayout>border image (see above)</literallayout></entry>
690  *         <entry>internal use only</entry>
691  *         <entry><literallayout>border-image: url("/path/to/image.png") 3 4 3 4 stretch;
692  * border-image: url("/path/to/image.png") 3 4 4 3 repeat stretch;</literallayout>
693  *         </entry>
694  *       </row>
695  *       <row>
696  *         <entry>transition</entry>
697  *         <entry>transition (see above)</entry>
698  *         <entry>internal use only</entry>
699  *         <entry><literallayout>transition: 150ms ease-in-out;
700  * transition: 1s linear loop;</literallayout>
701  *         </entry>
702  *       </row>
703  *       <row>
704  *         <entry>gtk-key-bindings</entry>
705  *         <entry>binding set name list</entry>
706  *         <entry>internal use only</entry>
707  *         <entry><literallayout>gtk-bindings: binding1, binding2, ...;</literallayout>
708  *         </entry>
709  *       </row>
710  *     </tbody>
711  *   </tgroup>
712  * </informaltable>
713  * <para>
714  * GtkThemingEngines can register their own, engine-specific style properties
715  * with the function gtk_theming_engine_register_property(). These properties
716  * can be set in CSS like other properties, using a name of the form
717  * <literallayout>-<replaceable>namespace</replaceable>-<replaceable>name</replaceable></literallayout>, where <replaceable>namespace</replaceable> is typically
718  * the name of the theming engine, and <replaceable>name</replaceable> is the
719  * name of the property. Style properties that have been registered by widgets
720  * using gtk_widget_class_install_style_property() can also be set in this
721  * way, using the widget class name for <replaceable>namespace</replaceable>.
722  * </para>
723  * <example>
724  * <title>Using engine-specific style properties</title>
725  * <programlisting>
726  * * {
727  *     engine: clearlooks;
728  *     border-radius: 4;
729  *     -GtkPaned-handle-size: 6;
730  *     -clearlooks-colorize-scrollbar: false;
731  * }
732  * </programlisting>
733  * </example>
734  * </refsect2>
735  */
736
737 typedef struct SelectorStyleInfo SelectorStyleInfo;
738 typedef struct _GtkCssScanner GtkCssScanner;
739 typedef enum ParserScope ParserScope;
740 typedef enum ParserSymbol ParserSymbol;
741
742 struct SelectorStyleInfo
743 {
744   GtkCssSelector *selector;
745   GHashTable *style;
746 };
747
748 struct _GtkCssScanner
749 {
750   GtkCssProvider *provider;
751   GtkCssParser *parser;
752   GtkCssScanner *parent;
753   GFile *file;
754   GFile *base;
755   GSList *state;
756   GSList *cur_selectors;
757   GHashTable *cur_properties;
758 };
759
760 struct _GtkCssProviderPrivate
761 {
762   GScanner *scanner;
763
764   GHashTable *symbolic_colors;
765
766   GPtrArray *selectors_info;
767 };
768
769 enum ParserScope {
770   SCOPE_SELECTOR,
771   SCOPE_PSEUDO_CLASS,
772   SCOPE_NTH_CHILD,
773   SCOPE_DECLARATION,
774   SCOPE_VALUE,
775   SCOPE_BINDING_SET
776 };
777
778 /* Extend GtkStateType, since these
779  * values are also used as symbols
780  */
781 enum ParserSymbol {
782   /* Scope: pseudo-class */
783   SYMBOL_NTH_CHILD = GTK_STATE_FOCUSED + 1,
784   SYMBOL_FIRST_CHILD,
785   SYMBOL_LAST_CHILD,
786   SYMBOL_SORTED_CHILD,
787
788   /* Scope: nth-child */
789   SYMBOL_NTH_CHILD_EVEN,
790   SYMBOL_NTH_CHILD_ODD,
791   SYMBOL_NTH_CHILD_FIRST,
792   SYMBOL_NTH_CHILD_LAST
793 };
794
795 enum {
796   PARSING_ERROR,
797   LAST_SIGNAL
798 };
799
800 static guint css_provider_signals[LAST_SIGNAL] = { 0 };
801
802 static void gtk_css_provider_finalize (GObject *object);
803 static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
804
805 static gboolean
806 gtk_css_provider_load_internal (GtkCssProvider *css_provider,
807                                 GtkCssScanner  *scanner,
808                                 GFile          *file,
809                                 const char     *data,
810                                 gsize           length,
811                                 GError        **error);
812
813 GQuark
814 gtk_css_provider_error_quark (void)
815 {
816   return g_quark_from_static_string ("gtk-css-provider-error-quark");
817 }
818
819 G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0,
820                         G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
821                                                gtk_css_style_provider_iface_init));
822
823 static void
824 gtk_css_provider_parsing_error (GtkCssProvider  *provider,
825                                 const gchar     *path,
826                                 guint            line,
827                                 guint            position,
828                                 const GError *   error)
829 {
830   /* Only emit a warning when we have no error handlers. This is our
831    * default handlers. And in this case erroneous CSS files are a bug
832    * and should be fixed.
833    * Note that these warnings can also be triggered by a broken theme
834    * that people installed from some weird location on the internets.
835    */
836   if (!g_signal_has_handler_pending (provider,
837                                      css_provider_signals[PARSING_ERROR],
838                                      0,
839                                      TRUE))
840     {
841       g_warning ("Theme parsing error: %s:%u:%u: %s", path ? path : "<unknown>", line, position, error->message);
842     }
843 }
844
845 static void
846 gtk_css_provider_class_init (GtkCssProviderClass *klass)
847 {
848   GObjectClass *object_class = G_OBJECT_CLASS (klass);
849
850   /**
851    * GtkCssProvider::parsing-error:
852    * @provider: the provider that had a parsing error
853    * @path: path to the parsed file or %NULL if the file cannot be
854    *   identified or the data was not loaded from a file
855    * @line: line in the file or data or 0 if unknown
856    * @position: offset into the current line or 0 if unknown or the
857    *   whole line is affected
858    * @error: The parsing error
859    *
860    * Signals that a parsing error occured. the @path, @line and @position
861    * describe the actual location of the error as accurately as possible.
862    *
863    * Parsing errors are never fatal, so the parsing will resume after
864    * the error. Errors may however cause parts of the given
865    * data or even all of it to not be parsed at all. So it is a useful idea
866    * to check that the parsing succeeds by connecting to this signal.
867    *
868    * Note that this signal may be emitted at any time as the css provider
869    * may opt to defer parsing parts or all of the input to a later time
870    * than when a loading function was called.
871    */
872   css_provider_signals[PARSING_ERROR] =
873     g_signal_new (I_("parsing-error"),
874                   G_TYPE_FROM_CLASS (object_class),
875                   G_SIGNAL_RUN_LAST,
876                   G_STRUCT_OFFSET (GtkCssProviderClass, parsing_error),
877                   NULL, NULL,
878                   _gtk_marshal_VOID__STRING_UINT_UINT_BOXED,
879                   G_TYPE_NONE, 4,
880                   G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_ERROR);
881
882   object_class->finalize = gtk_css_provider_finalize;
883
884   klass->parsing_error = gtk_css_provider_parsing_error;
885
886   g_type_class_add_private (object_class, sizeof (GtkCssProviderPrivate));
887 }
888
889 static void
890 gtk_css_provider_take_error_full (GtkCssProvider *provider,
891                                   GFile          *file,
892                                   guint           line,
893                                   guint           position,
894                                   GError         *error)
895 {
896   char *filename;
897
898   if (file)
899     filename = g_file_get_path (file);
900   else
901     filename = NULL;
902
903   g_signal_emit (provider, css_provider_signals[PARSING_ERROR], 0,
904                  filename, line, position, error);
905
906   g_free (filename);
907   g_error_free (error);
908 }
909
910 static SelectorStyleInfo *
911 selector_style_info_new (GtkCssSelector *selector)
912 {
913   SelectorStyleInfo *info;
914
915   info = g_slice_new0 (SelectorStyleInfo);
916   info->selector = selector;
917
918   return info;
919 }
920
921 static void
922 selector_style_info_free (SelectorStyleInfo *info)
923 {
924   if (info->style)
925     g_hash_table_unref (info->style);
926
927   if (info->selector)
928     _gtk_css_selector_free (info->selector);
929
930   g_slice_free (SelectorStyleInfo, info);
931 }
932
933 static void
934 selector_style_info_set_style (SelectorStyleInfo *info,
935                                GHashTable        *style)
936 {
937   if (info->style)
938     g_hash_table_unref (info->style);
939
940   if (style)
941     info->style = g_hash_table_ref (style);
942   else
943     info->style = NULL;
944 }
945
946 static void
947 property_value_free (GValue *value)
948 {
949   if (G_IS_VALUE (value))
950     g_value_unset (value);
951
952   g_slice_free (GValue, value);
953 }
954
955 static void
956 gtk_css_scanner_reset (GtkCssScanner *scanner)
957 {
958   g_slist_free (scanner->state);
959   scanner->state = NULL;
960
961   g_slist_free_full (scanner->cur_selectors, (GDestroyNotify) _gtk_css_selector_free);
962   scanner->cur_selectors = NULL;
963
964   if (scanner->cur_properties)
965     g_hash_table_unref (scanner->cur_properties);
966
967   scanner ->cur_properties = g_hash_table_new_full (g_str_hash,
968                                                     g_str_equal,
969                                                     (GDestroyNotify) g_free,
970                                                     (GDestroyNotify) property_value_free);
971 }
972
973 static void
974 gtk_css_scanner_destroy (GtkCssScanner *scanner)
975 {
976   gtk_css_scanner_reset (scanner);
977
978   g_object_unref (scanner->provider);
979   if (scanner->file)
980     g_object_unref (scanner->file);
981   g_object_unref (scanner->base);
982   g_hash_table_destroy (scanner->cur_properties);
983   _gtk_css_parser_free (scanner->parser);
984
985   g_slice_free (GtkCssScanner, scanner);
986 }
987
988 static void
989 gtk_css_scanner_parser_error (GtkCssParser *parser,
990                               const GError *error,
991                               gpointer      user_data)
992 {
993   GtkCssScanner *scanner = user_data;
994
995   gtk_css_provider_take_error_full (scanner->provider,
996                                     scanner->file,
997                                     _gtk_css_parser_get_line (scanner->parser),
998                                     _gtk_css_parser_get_position (scanner->parser),
999                                     g_error_copy (error));
1000 }
1001
1002 static GtkCssScanner *
1003 gtk_css_scanner_new (GtkCssProvider *provider,
1004                      GtkCssScanner  *parent,
1005                      GFile          *file,
1006                      const gchar    *data,
1007                      gsize           length)
1008 {
1009   GtkCssScanner *scanner;
1010
1011   g_assert (data[length] == 0);
1012
1013   scanner = g_slice_new0 (GtkCssScanner);
1014
1015   g_object_ref (provider);
1016   scanner->provider = provider;
1017   scanner->parent = parent;
1018
1019   if (file)
1020     {
1021       scanner->file = g_object_ref (file);
1022       scanner->base = g_file_get_parent (file);
1023     }
1024   else
1025     {
1026       char *dir = g_get_current_dir ();
1027       scanner->base = g_file_new_for_path (dir);
1028       g_free (dir);
1029     }
1030
1031   scanner->cur_properties = g_hash_table_new_full (g_str_hash,
1032                                                    g_str_equal,
1033                                                    (GDestroyNotify) g_free,
1034                                                    (GDestroyNotify) property_value_free);
1035
1036   scanner->parser = _gtk_css_parser_new (data,
1037                                          gtk_css_scanner_parser_error,
1038                                          scanner);
1039
1040   return scanner;
1041 }
1042
1043 static GFile *
1044 gtk_css_scanner_get_base_url (GtkCssScanner *scanner)
1045 {
1046   return scanner->base;
1047 }
1048
1049 static gboolean
1050 gtk_css_scanner_would_recurse (GtkCssScanner *scanner,
1051                                GFile         *file)
1052 {
1053   while (scanner)
1054     {
1055       if (scanner->file && g_file_equal (scanner->file, file))
1056         return TRUE;
1057
1058       scanner = scanner->parent;
1059     }
1060
1061   return FALSE;
1062 }
1063
1064 static void
1065 gtk_css_provider_init (GtkCssProvider *css_provider)
1066 {
1067   GtkCssProviderPrivate *priv;
1068
1069   priv = css_provider->priv = G_TYPE_INSTANCE_GET_PRIVATE (css_provider,
1070                                                            GTK_TYPE_CSS_PROVIDER,
1071                                                            GtkCssProviderPrivate);
1072
1073   priv->selectors_info = g_ptr_array_new_with_free_func ((GDestroyNotify) selector_style_info_free);
1074
1075   priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal,
1076                                                  (GDestroyNotify) g_free,
1077                                                  (GDestroyNotify) gtk_symbolic_color_unref);
1078 }
1079
1080 static void
1081 css_provider_dump_symbolic_colors (GtkCssProvider     *css_provider,
1082                                    GtkStyleProperties *props)
1083 {
1084   GtkCssProviderPrivate *priv;
1085   GHashTableIter iter;
1086   gpointer key, value;
1087
1088   priv = css_provider->priv;
1089   g_hash_table_iter_init (&iter, priv->symbolic_colors);
1090
1091   while (g_hash_table_iter_next (&iter, &key, &value))
1092     {
1093       const gchar *name;
1094       GtkSymbolicColor *color;
1095
1096       name = key;
1097       color = value;
1098
1099       gtk_style_properties_map_color (props, name, color);
1100     }
1101 }
1102
1103 static GtkStyleProperties *
1104 gtk_css_provider_get_style (GtkStyleProvider *provider,
1105                             GtkWidgetPath    *path)
1106 {
1107   GtkCssProvider *css_provider;
1108   GtkCssProviderPrivate *priv;
1109   GtkStyleProperties *props;
1110   guint i;
1111
1112   css_provider = GTK_CSS_PROVIDER (provider);
1113   priv = css_provider->priv;
1114   props = gtk_style_properties_new ();
1115
1116   css_provider_dump_symbolic_colors (css_provider, props);
1117
1118   for (i = 0; i < priv->selectors_info->len; i++)
1119     {
1120       SelectorStyleInfo *info;
1121       GHashTableIter iter;
1122       gpointer key, value;
1123
1124       info = g_ptr_array_index (priv->selectors_info, i);
1125
1126       if (!_gtk_css_selector_matches (info->selector, path, gtk_widget_path_length (path)))
1127         continue;
1128
1129       g_hash_table_iter_init (&iter, info->style);
1130
1131       while (g_hash_table_iter_next (&iter, &key, &value))
1132         {
1133           gchar *prop = key;
1134
1135           /* Properties starting with '-' may be both widget style properties
1136            * or custom properties from the theming engine, so check whether
1137            * the type is registered or not.
1138            */
1139           if (prop[0] == '-' &&
1140               !gtk_style_properties_lookup_property (prop, NULL, NULL))
1141             continue;
1142
1143           gtk_style_properties_set_property (props,
1144                                              key,
1145                                              _gtk_css_selector_get_state_flags (info->selector),
1146                                              value);
1147         }
1148     }
1149
1150   return props;
1151 }
1152
1153 static gboolean
1154 gtk_css_provider_get_style_property (GtkStyleProvider *provider,
1155                                      GtkWidgetPath    *path,
1156                                      GtkStateFlags     state,
1157                                      GParamSpec       *pspec,
1158                                      GValue           *value)
1159 {
1160   GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider);
1161   GtkCssProviderPrivate *priv = css_provider->priv;
1162   const GValue *val;
1163   gboolean found = FALSE;
1164   gchar *prop_name;
1165   gint i;
1166
1167   prop_name = g_strdup_printf ("-%s-%s",
1168                                g_type_name (pspec->owner_type),
1169                                pspec->name);
1170
1171   for (i = priv->selectors_info->len - 1; i >= 0; i--)
1172     {
1173       SelectorStyleInfo *info;
1174       GtkStateFlags selector_state;
1175
1176       info = g_ptr_array_index (priv->selectors_info, i);
1177
1178       if (!_gtk_css_selector_matches (info->selector, path, gtk_widget_path_length (path)))
1179         continue;
1180
1181       selector_state = _gtk_css_selector_get_state_flags (info->selector);
1182       val = g_hash_table_lookup (info->style, prop_name);
1183
1184       if (val &&
1185           (selector_state == 0 ||
1186            selector_state == state ||
1187            ((selector_state & state) != 0 &&
1188             (selector_state & ~(state)) == 0)))
1189         {
1190           GError *error = NULL;
1191
1192           found = _gtk_css_value_from_string (value,
1193                                               NULL,
1194                                               g_value_get_string (val),
1195                                               &error);
1196
1197           if (found)
1198             break;
1199           
1200           /* error location should be _way_ better */
1201           gtk_css_provider_take_error_full (GTK_CSS_PROVIDER (provider),
1202                                             NULL,
1203                                             0, 0,
1204                                             error);
1205         }
1206     }
1207
1208   g_free (prop_name);
1209
1210   return found;
1211 }
1212
1213 static void
1214 gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface)
1215 {
1216   iface->get_style = gtk_css_provider_get_style;
1217   iface->get_style_property = gtk_css_provider_get_style_property;
1218 }
1219
1220 static void
1221 gtk_css_provider_finalize (GObject *object)
1222 {
1223   GtkCssProvider *css_provider;
1224   GtkCssProviderPrivate *priv;
1225
1226   css_provider = GTK_CSS_PROVIDER (object);
1227   priv = css_provider->priv;
1228
1229   g_ptr_array_free (priv->selectors_info, TRUE);
1230
1231   if (priv->symbolic_colors)
1232     g_hash_table_destroy (priv->symbolic_colors);
1233
1234   G_OBJECT_CLASS (gtk_css_provider_parent_class)->finalize (object);
1235 }
1236
1237 /**
1238  * gtk_css_provider_new:
1239  *
1240  * Returns a newly created #GtkCssProvider.
1241  *
1242  * Returns: A new #GtkCssProvider
1243  **/
1244 GtkCssProvider *
1245 gtk_css_provider_new (void)
1246 {
1247   return g_object_new (GTK_TYPE_CSS_PROVIDER, NULL);
1248 }
1249
1250 static void
1251 gtk_css_provider_take_error (GtkCssProvider *provider,
1252                              GtkCssScanner  *scanner,
1253                              GError         *error)
1254 {
1255   gtk_css_provider_take_error_full (provider,
1256                                     scanner->file,
1257                                     _gtk_css_parser_get_line (scanner->parser),
1258                                     _gtk_css_parser_get_position (scanner->parser),
1259                                     error);
1260 }
1261
1262 static void
1263 gtk_css_provider_error_literal (GtkCssProvider *provider,
1264                                 GtkCssScanner  *scanner,
1265                                 GQuark          domain,
1266                                 gint            code,
1267                                 const char     *message)
1268 {
1269   gtk_css_provider_take_error (provider,
1270                                scanner,
1271                                g_error_new_literal (domain, code, message));
1272 }
1273
1274 static void
1275 gtk_css_provider_error (GtkCssProvider *provider,
1276                         GtkCssScanner  *scanner,
1277                         GQuark          domain,
1278                         gint            code,
1279                         const char     *format,
1280                         ...)  G_GNUC_PRINTF (5, 6);
1281 static void
1282 gtk_css_provider_error (GtkCssProvider *provider,
1283                         GtkCssScanner  *scanner,
1284                         GQuark          domain,
1285                         gint            code,
1286                         const char     *format,
1287                         ...)
1288 {
1289   GError *error;
1290   va_list args;
1291
1292   va_start (args, format);
1293   error = g_error_new_valist (domain, code, format, args);
1294   va_end (args);
1295
1296   gtk_css_provider_take_error (provider, scanner, error);
1297 }
1298
1299 static void
1300 gtk_css_provider_invalid_token (GtkCssProvider *provider,
1301                                 GtkCssScanner  *scanner,
1302                                 const char     *expected)
1303 {
1304   gtk_css_provider_error (provider,
1305                           scanner,
1306                           GTK_CSS_PROVIDER_ERROR,
1307                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
1308                           "expected a valid %s", expected);
1309 }
1310
1311 static void 
1312 css_provider_commit (GtkCssProvider *css_provider,
1313                      GSList         *selectors,
1314                      GHashTable     *properties)
1315 {
1316   GtkCssProviderPrivate *priv;
1317   GSList *l;
1318
1319   priv = css_provider->priv;
1320
1321   if (g_hash_table_size (properties) == 0)
1322     {
1323       g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
1324       g_hash_table_unref (properties);
1325       return;
1326     }
1327
1328   for (l = selectors; l; l = l->next)
1329     {
1330       GtkCssSelector *selector = l->data;
1331       SelectorStyleInfo *info;
1332
1333       info = selector_style_info_new (selector);
1334       selector_style_info_set_style (info, properties);
1335
1336       g_ptr_array_add (priv->selectors_info, info);
1337     }
1338
1339   g_hash_table_unref (properties);
1340 }
1341
1342 static void
1343 resolve_binding_sets (const gchar *value_str,
1344                       GValue      *value)
1345 {
1346   GPtrArray *array;
1347   gchar **bindings, **str;
1348
1349   bindings = g_strsplit (value_str, ",", -1);
1350   array = g_ptr_array_new ();
1351
1352   for (str = bindings; *str; str++)
1353     {
1354       GtkBindingSet *binding_set;
1355
1356       binding_set = gtk_binding_set_find (g_strstrip (*str));
1357
1358       if (!binding_set)
1359         continue;
1360
1361       g_ptr_array_add (array, binding_set);
1362     }
1363
1364   g_value_take_boxed (value, array);
1365   g_strfreev (bindings);
1366 }
1367
1368 static void
1369 gtk_css_provider_reset (GtkCssProvider *css_provider)
1370 {
1371   GtkCssProviderPrivate *priv;
1372
1373   priv = css_provider->priv;
1374
1375   if (priv->selectors_info->len > 0)
1376     g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
1377 }
1378
1379 static void
1380 gtk_css_provider_propagate_error (GtkCssProvider  *provider,
1381                                   const gchar     *path,
1382                                   guint            line,
1383                                   guint            position,
1384                                   const GError    *error,
1385                                   GError         **propagate_to)
1386 {
1387   /* we already set an error. And we'd like to keep the first one */
1388   if (*propagate_to)
1389     return;
1390
1391   *propagate_to = g_error_copy (error);
1392   g_prefix_error (propagate_to, "%s:%u:%u: ", path ? path : "<unknown>", line, position);
1393 }
1394
1395 static void
1396 parse_import (GtkCssScanner *scanner)
1397 {
1398   GFile *file;
1399   char *uri;
1400
1401   uri = _gtk_css_parser_read_uri (scanner->parser);
1402   if (uri == NULL)
1403     {
1404       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1405       return;
1406     }
1407
1408   file = g_file_resolve_relative_path (gtk_css_scanner_get_base_url (scanner), uri);
1409   g_free (uri);
1410
1411   if (gtk_css_scanner_would_recurse (scanner, file))
1412     {
1413        char *path = g_file_get_path (file);
1414        gtk_css_provider_error (scanner->provider,
1415                                scanner,
1416                                GTK_CSS_PROVIDER_ERROR,
1417                                GTK_CSS_PROVIDER_ERROR_IMPORT,
1418                                "Loading '%s' would recurse",
1419                                path);
1420        g_free (path);
1421     }
1422   else
1423     {
1424       gtk_css_provider_load_internal (scanner->provider,
1425                                       scanner,
1426                                       file,
1427                                       NULL, 0,
1428                                       NULL);
1429     }
1430
1431   if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
1432     {
1433       g_object_unref (file);
1434       gtk_css_provider_invalid_token (scanner->provider, scanner, "semicolon");
1435       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1436       return;
1437     }
1438
1439   g_object_unref (file);
1440 }
1441
1442 static void
1443 parse_color_definition (GtkCssScanner *scanner)
1444 {
1445   GtkSymbolicColor *symbolic;
1446   char *name;
1447
1448   name = _gtk_css_parser_try_name (scanner->parser, TRUE);
1449   if (name == NULL)
1450     {
1451       gtk_css_provider_error_literal (scanner->provider,
1452                                       scanner,
1453                                       GTK_CSS_PROVIDER_ERROR,
1454                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1455                                       "Not a valid color name");
1456       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1457       return;
1458     }
1459
1460   symbolic = _gtk_css_parser_read_symbolic_color (scanner->parser);
1461   if (symbolic == NULL)
1462     {
1463       g_free (name);
1464       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1465       return;
1466     }
1467
1468   if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
1469     {
1470       g_free (name);
1471       gtk_symbolic_color_unref (symbolic);
1472       gtk_css_provider_error_literal (scanner->provider,
1473                                       scanner,
1474                                       GTK_CSS_PROVIDER_ERROR,
1475                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1476                                       "Missing semicolon at end of color definition");
1477       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1478       return;
1479     }
1480
1481   g_hash_table_insert (scanner->provider->priv->symbolic_colors, name, symbolic);
1482 }
1483
1484 static void
1485 parse_binding_set (GtkCssScanner *scanner)
1486 {
1487   GtkBindingSet *binding_set;
1488   char *name;
1489
1490   name = _gtk_css_parser_try_ident (scanner->parser, TRUE);
1491   if (name == NULL)
1492     {
1493       gtk_css_provider_error_literal (scanner->provider,
1494                                       scanner,
1495                                       GTK_CSS_PROVIDER_ERROR,
1496                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1497                                       "Expected name for binding set");
1498       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1499       goto skip_semicolon;
1500     }
1501
1502   binding_set = gtk_binding_set_find (name);
1503   if (!binding_set)
1504     {
1505       binding_set = gtk_binding_set_new (name);
1506       binding_set->parsed = TRUE;
1507     }
1508   g_free (name);
1509
1510   if (!_gtk_css_parser_try (scanner->parser, "{", TRUE))
1511     {
1512       gtk_css_provider_error_literal (scanner->provider,
1513                                       scanner,
1514                                       GTK_CSS_PROVIDER_ERROR,
1515                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1516                                       "Expected '{' for binding set");
1517       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1518       goto skip_semicolon;
1519     }
1520
1521   while (!_gtk_css_parser_is_eof (scanner->parser) &&
1522          !_gtk_css_parser_begins_with (scanner->parser, '}'))
1523     {
1524       name = _gtk_css_parser_read_value (scanner->parser);
1525       if (name == NULL)
1526         {
1527           _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1528           continue;
1529         }
1530
1531       gtk_binding_entry_add_signal_from_string (binding_set, name);
1532       g_free (name);
1533
1534       if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
1535         {
1536           if (!_gtk_css_parser_begins_with (scanner->parser, '}') &&
1537               !_gtk_css_parser_is_eof (scanner->parser))
1538             {
1539               gtk_css_provider_error_literal (scanner->provider,
1540                                               scanner,
1541                                               GTK_CSS_PROVIDER_ERROR,
1542                                               GTK_CSS_PROVIDER_ERROR_SYNTAX,
1543                                               "Expected semicolon");
1544               _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1545             }
1546         }
1547     }
1548
1549   if (!_gtk_css_parser_try (scanner->parser, "}", TRUE))
1550     {
1551       gtk_css_provider_error_literal (scanner->provider,
1552                                       scanner,
1553                                       GTK_CSS_PROVIDER_ERROR,
1554                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1555                                       "expected '}' after declarations");
1556       if (!_gtk_css_parser_is_eof (scanner->parser))
1557         _gtk_css_parser_resync (scanner->parser, FALSE, 0);
1558     }
1559
1560 skip_semicolon:
1561   if (_gtk_css_parser_try (scanner->parser, ";", TRUE))
1562     gtk_css_provider_error_literal (scanner->provider,
1563                                     scanner,
1564                                     GTK_CSS_PROVIDER_ERROR,
1565                                     GTK_CSS_PROVIDER_ERROR_DEPRECATED,
1566                                     "Nonstandard semicolon at end of binding set");
1567 }
1568
1569 static void
1570 parse_at_keyword (GtkCssScanner *scanner)
1571 {
1572   if (_gtk_css_parser_try (scanner->parser, "@import", TRUE))
1573     parse_import (scanner);
1574   else if (_gtk_css_parser_try (scanner->parser, "@define-color", TRUE))
1575     parse_color_definition (scanner);
1576   else if (_gtk_css_parser_try (scanner->parser, "@binding-set", TRUE))
1577     parse_binding_set (scanner);
1578   else
1579     {
1580       gtk_css_provider_error_literal (scanner->provider,
1581                                       scanner,
1582                                       GTK_CSS_PROVIDER_ERROR,
1583                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1584                                       "unknown @ rule");
1585       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
1586     }
1587 }
1588
1589 static gboolean
1590 parse_selector_class (GtkCssScanner *scanner, GArray *classes)
1591 {
1592   GQuark qname;
1593   char *name;
1594     
1595   name = _gtk_css_parser_try_name (scanner->parser, FALSE);
1596
1597   if (name == NULL)
1598     {
1599       gtk_css_provider_error_literal (scanner->provider,
1600                                       scanner,
1601                                       GTK_CSS_PROVIDER_ERROR,
1602                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1603                                       "Expected a valid name for class");
1604       return FALSE;
1605     }
1606
1607   qname = g_quark_from_string (name);
1608   g_array_append_val (classes, qname);
1609   g_free (name);
1610   return TRUE;
1611 }
1612
1613 static gboolean
1614 parse_selector_name (GtkCssScanner *scanner, GArray *names)
1615 {
1616   GQuark qname;
1617   char *name;
1618     
1619   name = _gtk_css_parser_try_name (scanner->parser, FALSE);
1620
1621   if (name == NULL)
1622     {
1623       gtk_css_provider_error_literal (scanner->provider,
1624                                       scanner,
1625                                       GTK_CSS_PROVIDER_ERROR,
1626                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1627                                       "Expected a valid name for id");
1628       return FALSE;
1629     }
1630
1631   qname = g_quark_from_string (name);
1632   g_array_append_val (names, qname);
1633   g_free (name);
1634   return TRUE;
1635 }
1636
1637 static gboolean
1638 parse_selector_pseudo_class (GtkCssScanner  *scanner,
1639                              GtkRegionFlags *region_to_modify,
1640                              GtkStateFlags  *state_to_modify)
1641 {
1642   struct {
1643     const char *name;
1644     GtkRegionFlags region_flag;
1645     GtkStateFlags state_flag;
1646   } pseudo_classes[] = {
1647     { "first",        GTK_REGION_FIRST, 0 },
1648     { "last",         GTK_REGION_LAST, 0 },
1649     { "sorted",       GTK_REGION_SORTED, 0 },
1650     { "active",       0, GTK_STATE_FLAG_ACTIVE },
1651     { "prelight",     0, GTK_STATE_FLAG_PRELIGHT },
1652     { "hover",        0, GTK_STATE_FLAG_PRELIGHT },
1653     { "selected",     0, GTK_STATE_FLAG_SELECTED },
1654     { "insensitive",  0, GTK_STATE_FLAG_INSENSITIVE },
1655     { "inconsistent", 0, GTK_STATE_FLAG_INCONSISTENT },
1656     { "focused",      0, GTK_STATE_FLAG_FOCUSED },
1657     { "focus",        0, GTK_STATE_FLAG_FOCUSED },
1658     { NULL, }
1659   }, nth_child_classes[] = {
1660     { "first",        GTK_REGION_FIRST, 0 },
1661     { "last",         GTK_REGION_LAST, 0 },
1662     { "even",         GTK_REGION_EVEN, 0 },
1663     { "odd",          GTK_REGION_ODD, 0 },
1664     { NULL, }
1665   }, *classes;
1666   guint i;
1667   char *name;
1668
1669   name = _gtk_css_parser_try_ident (scanner->parser, FALSE);
1670   if (name == NULL)
1671     {
1672       gtk_css_provider_error_literal (scanner->provider,
1673                                       scanner,
1674                                       GTK_CSS_PROVIDER_ERROR,
1675                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
1676                                       "Missing name of pseudo-class");
1677       return FALSE;
1678     }
1679
1680   if (_gtk_css_parser_try (scanner->parser, "(", TRUE))
1681     {
1682       char *function = name;
1683
1684       name = _gtk_css_parser_try_ident (scanner->parser, TRUE);
1685       if (!_gtk_css_parser_try (scanner->parser, ")", FALSE))
1686         {
1687           gtk_css_provider_error_literal (scanner->provider,
1688                                           scanner,
1689                                           GTK_CSS_PROVIDER_ERROR,
1690                                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
1691                                           "Missing closing bracket for pseudo-class");
1692           return FALSE;
1693         }
1694
1695       if (g_ascii_strcasecmp (function, "nth-child") != 0)
1696         {
1697           gtk_css_provider_error (scanner->provider,
1698                                   scanner,
1699                                   GTK_CSS_PROVIDER_ERROR,
1700                                   GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
1701                                   "Unknown pseudo-class '%s(%s)'", function, name ? name : "");
1702           g_free (function);
1703           g_free (name);
1704           return FALSE;
1705         }
1706       
1707       g_free (function);
1708     
1709       if (name == NULL)
1710         {
1711           gtk_css_provider_error (scanner->provider,
1712                                   scanner,
1713                                   GTK_CSS_PROVIDER_ERROR,
1714                                   GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
1715                                   "nth-child() requires an argument");
1716           return FALSE;
1717         }
1718
1719       classes = nth_child_classes;
1720     }
1721   else
1722     classes = pseudo_classes;
1723
1724   for (i = 0; classes[i].name != NULL; i++)
1725     {
1726       if (g_ascii_strcasecmp (name, classes[i].name) == 0)
1727         {
1728           if ((*region_to_modify & classes[i].region_flag) ||
1729               (*state_to_modify & classes[i].state_flag))
1730             {
1731               if (classes == nth_child_classes)
1732                 gtk_css_provider_error (scanner->provider,
1733                                         scanner,
1734                                         GTK_CSS_PROVIDER_ERROR,
1735                                         GTK_CSS_PROVIDER_ERROR_SYNTAX,
1736                                         "Duplicate pseudo-class 'nth-child(%s)'", name);
1737               else
1738                 gtk_css_provider_error (scanner->provider,
1739                                         scanner,
1740                                         GTK_CSS_PROVIDER_ERROR,
1741                                         GTK_CSS_PROVIDER_ERROR_SYNTAX,
1742                                         "Duplicate pseudo-class '%s'", name);
1743             }
1744           *region_to_modify |= classes[i].region_flag;
1745           *state_to_modify |= classes[i].state_flag;
1746
1747           g_free (name);
1748           return TRUE;
1749         }
1750     }
1751
1752   if (classes == nth_child_classes)
1753     gtk_css_provider_error (scanner->provider,
1754                             scanner,
1755                             GTK_CSS_PROVIDER_ERROR,
1756                             GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
1757                             "Unknown pseudo-class 'nth-child(%s)'", name);
1758   else
1759     gtk_css_provider_error (scanner->provider,
1760                             scanner,
1761                             GTK_CSS_PROVIDER_ERROR,
1762                             GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
1763                             "Unknown pseudo-class '%s'", name);
1764   g_free (name);
1765   return FALSE;
1766 }
1767
1768 static gboolean
1769 parse_simple_selector (GtkCssScanner *scanner,
1770                        char **name,
1771                        GArray *ids,
1772                        GArray *classes,
1773                        GtkRegionFlags *pseudo_classes,
1774                        GtkStateFlags *state)
1775 {
1776   gboolean parsed_something;
1777   
1778   *name = _gtk_css_parser_try_ident (scanner->parser, FALSE);
1779   if (*name)
1780     parsed_something = TRUE;
1781   else
1782     parsed_something = _gtk_css_parser_try (scanner->parser, "*", FALSE);
1783
1784   do {
1785       if (_gtk_css_parser_try (scanner->parser, "#", FALSE))
1786         {
1787           if (!parse_selector_name (scanner, ids))
1788             return FALSE;
1789         }
1790       else if (_gtk_css_parser_try (scanner->parser, ".", FALSE))
1791         {
1792           if (!parse_selector_class (scanner, classes))
1793             return FALSE;
1794         }
1795       else if (_gtk_css_parser_try (scanner->parser, ":", FALSE))
1796         {
1797           if (!parse_selector_pseudo_class (scanner, pseudo_classes, state))
1798             return FALSE;
1799         }
1800       else if (!parsed_something)
1801         {
1802           gtk_css_provider_error_literal (scanner->provider,
1803                                           scanner,
1804                                           GTK_CSS_PROVIDER_ERROR,
1805                                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
1806                                           "Expected a valid selector");
1807           return FALSE;
1808         }
1809       else
1810         break;
1811
1812       parsed_something = TRUE;
1813     }
1814   while (!_gtk_css_parser_is_eof (scanner->parser));
1815
1816   _gtk_css_parser_skip_whitespace (scanner->parser);
1817   return TRUE;
1818 }
1819
1820 static GtkCssSelector *
1821 parse_selector (GtkCssScanner *scanner)
1822 {
1823   GtkCssSelector *selector = NULL;
1824
1825   do {
1826       char *name = NULL;
1827       GArray *ids = g_array_new (TRUE, FALSE, sizeof (GQuark));
1828       GArray *classes = g_array_new (TRUE, FALSE, sizeof (GQuark));
1829       GtkRegionFlags pseudo_classes = 0;
1830       GtkStateFlags state = 0;
1831       GtkCssCombinator combine = GTK_CSS_COMBINE_DESCANDANT;
1832
1833       if (selector)
1834         {
1835           if (_gtk_css_parser_try (scanner->parser, ">", TRUE))
1836             combine = GTK_CSS_COMBINE_CHILD;
1837         }
1838
1839       if (!parse_simple_selector (scanner, &name, ids, classes, &pseudo_classes, &state))
1840         {
1841           g_array_free (ids, TRUE);
1842           g_array_free (classes, TRUE);
1843           if (selector)
1844             _gtk_css_selector_free (selector);
1845           return NULL;
1846         }
1847
1848       selector = _gtk_css_selector_new (selector,
1849                                         combine,
1850                                         name,
1851                                         (GQuark *) g_array_free (ids, ids->len == 0),
1852                                         (GQuark *) g_array_free (classes, classes->len == 0),
1853                                         pseudo_classes,
1854                                         state);
1855       g_free (name);
1856     }
1857   while (!_gtk_css_parser_is_eof (scanner->parser) &&
1858          !_gtk_css_parser_begins_with (scanner->parser, ',') &&
1859          !_gtk_css_parser_begins_with (scanner->parser, '{'));
1860
1861   return selector;
1862 }
1863
1864 static GSList *
1865 parse_selector_list (GtkCssScanner *scanner)
1866 {
1867   GSList *selectors = NULL;
1868
1869   do {
1870       GtkCssSelector *select = parse_selector (scanner);
1871
1872       if (select == NULL)
1873         {
1874           g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
1875           _gtk_css_parser_resync (scanner->parser, FALSE, 0);
1876           return NULL;
1877         }
1878
1879       selectors = g_slist_prepend (selectors, select);
1880     }
1881   while (_gtk_css_parser_try (scanner->parser, ",", TRUE));
1882
1883   return selectors;
1884 }
1885
1886 static void
1887 parse_declaration (GtkCssScanner *scanner, GHashTable *properties)
1888 {
1889   GtkStylePropertyParser parse_func = NULL;
1890   GParamSpec *pspec = NULL;
1891   char *name, *value_str;
1892
1893   name = _gtk_css_parser_try_ident (scanner->parser, TRUE);
1894   if (name == NULL)
1895     goto check_for_semicolon;
1896
1897   if (!gtk_style_properties_lookup_property (name, &parse_func, &pspec) &&
1898       name[0] != '-')
1899     {
1900       gtk_css_provider_error (scanner->provider,
1901                               scanner,
1902                               GTK_CSS_PROVIDER_ERROR,
1903                               GTK_CSS_PROVIDER_ERROR_NAME,
1904                               "'%s' is not a valid property name",
1905                               name);
1906       _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1907       g_free (name);
1908       return;
1909     }
1910
1911   if (!_gtk_css_parser_try (scanner->parser, ":", TRUE))
1912     {
1913       gtk_css_provider_invalid_token (scanner->provider, scanner, "':'");
1914       _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1915       g_free (name);
1916       return;
1917     }
1918
1919   value_str = _gtk_css_parser_read_value (scanner->parser);
1920   if (value_str == NULL)
1921     {
1922       _gtk_css_parser_resync (scanner->parser, TRUE, '}');
1923       g_free (name);
1924       return;
1925     }
1926   
1927   if (pspec)
1928     {
1929       GValue *val;
1930
1931       val = g_slice_new0 (GValue);
1932       g_value_init (val, pspec->value_type);
1933
1934       if (strcmp (value_str, "none") == 0)
1935         {
1936           /* Insert the default value, so it has an opportunity
1937            * to override other style providers when merged
1938            */
1939           g_param_value_set_default (pspec, val);
1940           g_hash_table_insert (properties, name, val);
1941         }
1942       else if (strcmp (name, "gtk-key-bindings") == 0)
1943         {
1944           /* Private property holding the binding sets */
1945           resolve_binding_sets (value_str, val);
1946           g_hash_table_insert (properties, name, val);
1947         }
1948       else if (parse_func)
1949         {
1950           GError *error = NULL;
1951           
1952           if ((*parse_func) (value_str, val, &error))
1953             g_hash_table_insert (properties, name, val);
1954           else
1955             gtk_css_provider_take_error (scanner->provider, scanner, error);
1956         }
1957       else
1958         {
1959           GError *error = NULL;
1960           
1961           if (_gtk_css_value_from_string (val,
1962                                           gtk_css_scanner_get_base_url (scanner),
1963                                           value_str,
1964                                           &error))
1965             {
1966               g_hash_table_insert (properties, name, val);
1967             }
1968           else
1969             {
1970               g_value_unset (val);
1971               g_slice_free (GValue, val);
1972               g_free (name);
1973
1974               gtk_css_provider_take_error (scanner->provider, scanner, error);
1975             }
1976         }
1977     }
1978   else if (name[0] == '-')
1979     {
1980       GValue *val;
1981
1982       val = g_slice_new0 (GValue);
1983       g_value_init (val, G_TYPE_STRING);
1984       g_value_set_string (val, value_str);
1985
1986       g_hash_table_insert (properties, name, val);
1987     }
1988   else
1989     g_free (name);
1990
1991   g_free (value_str);
1992
1993 check_for_semicolon:
1994   if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
1995     {
1996       if (!_gtk_css_parser_begins_with (scanner->parser, '}') &&
1997           !_gtk_css_parser_is_eof (scanner->parser))
1998         {
1999           gtk_css_provider_error_literal (scanner->provider,
2000                                           scanner,
2001                                           GTK_CSS_PROVIDER_ERROR,
2002                                           GTK_CSS_PROVIDER_ERROR_SYNTAX,
2003                                           "Expected semicolon");
2004           _gtk_css_parser_resync (scanner->parser, TRUE, '}');
2005         }
2006     }
2007 }
2008
2009 static GHashTable *
2010 parse_declarations (GtkCssScanner *scanner)
2011 {
2012   GHashTable *properties;
2013
2014   properties = g_hash_table_new_full (g_str_hash,
2015                                       g_str_equal,
2016                                       (GDestroyNotify) g_free,
2017                                       (GDestroyNotify) property_value_free);
2018
2019   while (!_gtk_css_parser_is_eof (scanner->parser) &&
2020          !_gtk_css_parser_begins_with (scanner->parser, '}'))
2021     {
2022       parse_declaration (scanner, properties);
2023     }
2024
2025   return properties;
2026 }
2027
2028 static void
2029 parse_ruleset (GtkCssScanner *scanner)
2030 {
2031   GSList *selectors;
2032   GHashTable *properties;
2033
2034   selectors = parse_selector_list (scanner);
2035   if (selectors == NULL)
2036     return;
2037
2038   if (!_gtk_css_parser_try (scanner->parser, "{", TRUE))
2039     {
2040       gtk_css_provider_error_literal (scanner->provider,
2041                                       scanner,
2042                                       GTK_CSS_PROVIDER_ERROR,
2043                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
2044                                       "expected '{' after selectors");
2045       _gtk_css_parser_resync (scanner->parser, FALSE, 0);
2046       g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
2047       return;
2048     }
2049
2050   properties = parse_declarations (scanner);
2051
2052   if (!_gtk_css_parser_try (scanner->parser, "}", TRUE))
2053     {
2054       gtk_css_provider_error_literal (scanner->provider,
2055                                       scanner,
2056                                       GTK_CSS_PROVIDER_ERROR,
2057                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
2058                                       "expected '}' after declarations");
2059       if (!_gtk_css_parser_is_eof (scanner->parser))
2060         {
2061           _gtk_css_parser_resync (scanner->parser, FALSE, 0);
2062           g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
2063           if (properties)
2064             g_hash_table_unref (properties);
2065           return;
2066         }
2067     }
2068
2069   if (properties)
2070     css_provider_commit (scanner->provider, selectors, properties);
2071   else
2072     g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
2073 }
2074
2075 static void
2076 parse_statement (GtkCssScanner *scanner)
2077 {
2078   if (_gtk_css_parser_begins_with (scanner->parser, '@'))
2079     parse_at_keyword (scanner);
2080   else
2081     parse_ruleset (scanner);
2082 }
2083
2084 static void
2085 parse_stylesheet (GtkCssScanner *scanner)
2086 {
2087   _gtk_css_parser_skip_whitespace (scanner->parser);
2088
2089   while (!_gtk_css_parser_is_eof (scanner->parser))
2090     {
2091       if (_gtk_css_parser_try (scanner->parser, "<!--", TRUE) ||
2092           _gtk_css_parser_try (scanner->parser, "-->", TRUE))
2093         continue;
2094
2095       parse_statement (scanner);
2096     }
2097 }
2098
2099 static int
2100 gtk_css_provider_compare_rule (gconstpointer a_,
2101                                gconstpointer b_)
2102 {
2103   const SelectorStyleInfo *a = *(const SelectorStyleInfo **) a_;
2104   const SelectorStyleInfo *b = *(const SelectorStyleInfo **) b_;
2105   int compare;
2106
2107   compare = _gtk_css_selector_compare (a->selector, b->selector);
2108   if (compare != 0)
2109     return compare;
2110
2111   /* compare pointers in array to ensure a stable sort */
2112   if (a_ < b_)
2113     return -1;
2114
2115   if (a_ > b_)
2116     return 1;
2117
2118   return 0;
2119 }
2120
2121 static void
2122 gtk_css_provider_postprocess (GtkCssProvider *css_provider)
2123 {
2124   GtkCssProviderPrivate *priv = css_provider->priv;
2125
2126   g_ptr_array_sort (priv->selectors_info, gtk_css_provider_compare_rule);
2127 }
2128
2129 static gboolean
2130 gtk_css_provider_load_internal (GtkCssProvider *css_provider,
2131                                 GtkCssScanner  *parent,
2132                                 GFile          *file,
2133                                 const char     *data,
2134                                 gsize           length,
2135                                 GError        **error)
2136 {
2137   GtkCssScanner *scanner;
2138   gulong error_handler;
2139   char *free_data;
2140
2141   if (error)
2142     error_handler = g_signal_connect (css_provider,
2143                                       "parsing-error",
2144                                       G_CALLBACK (gtk_css_provider_propagate_error),
2145                                       error);
2146   else
2147     error_handler = 0; /* silence gcc */
2148
2149   if (data == NULL)
2150     {
2151       GError *load_error = NULL;
2152
2153       if (g_file_load_contents (file, NULL,
2154                                 &free_data, &length,
2155                                 NULL, &load_error))
2156         {
2157           data = free_data;
2158         }
2159       else
2160         {
2161           if (parent)
2162             {
2163               gtk_css_provider_error (css_provider,
2164                                       parent,
2165                                       GTK_CSS_PROVIDER_ERROR,
2166                                       GTK_CSS_PROVIDER_ERROR_IMPORT,
2167                                       "Failed to import: %s",
2168                                       load_error->message);
2169               g_error_free (load_error);
2170             }
2171           else
2172             {
2173               gtk_css_provider_take_error_full (css_provider,
2174                                                 file,
2175                                                 0, 0,
2176                                                 load_error);
2177             }
2178         }
2179     }
2180   else
2181     free_data = NULL;
2182
2183   if (data)
2184     {
2185       scanner = gtk_css_scanner_new (css_provider, parent, file, data, length);
2186
2187       parse_stylesheet (scanner);
2188
2189       gtk_css_scanner_destroy (scanner);
2190
2191       if (parent == NULL)
2192         gtk_css_provider_postprocess (css_provider);
2193     }
2194
2195   if (error)
2196     {
2197       g_signal_handler_disconnect (css_provider, error_handler);
2198       
2199       if (*error)
2200         {
2201           /* We clear all contents from the provider for backwards compat reasons */
2202           gtk_css_provider_reset (css_provider);
2203           return FALSE;
2204         }
2205     }
2206
2207   return TRUE;
2208 }
2209
2210 /**
2211  * gtk_css_provider_load_from_data:
2212  * @css_provider: a #GtkCssProvider
2213  * @data: CSS data loaded in memory
2214  * @length: the length of @data in bytes, or -1 for NUL terminated strings
2215  * @error: (out) (allow-none): return location for a #GError, or %NULL
2216  *
2217  * Loads @data into @css_provider, making it clear any previously loaded
2218  * information.
2219  *
2220  * Returns: %TRUE if the data could be loaded.
2221  **/
2222 gboolean
2223 gtk_css_provider_load_from_data (GtkCssProvider  *css_provider,
2224                                  const gchar     *data,
2225                                  gssize           length,
2226                                  GError         **error)
2227 {
2228   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
2229   g_return_val_if_fail (data != NULL, FALSE);
2230
2231   if (length < 0)
2232     length = strlen (data);
2233
2234   gtk_css_provider_reset (css_provider);
2235
2236   return gtk_css_provider_load_internal (css_provider, NULL, NULL, data, length, error);
2237 }
2238
2239 /**
2240  * gtk_css_provider_load_from_file:
2241  * @css_provider: a #GtkCssProvider
2242  * @file: #GFile pointing to a file to load
2243  * @error: (out) (allow-none): return location for a #GError, or %NULL
2244  *
2245  * Loads the data contained in @file into @css_provider, making it
2246  * clear any previously loaded information.
2247  *
2248  * Returns: %TRUE if the data could be loaded.
2249  **/
2250 gboolean
2251 gtk_css_provider_load_from_file (GtkCssProvider  *css_provider,
2252                                  GFile           *file,
2253                                  GError         **error)
2254 {
2255   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
2256   g_return_val_if_fail (G_IS_FILE (file), FALSE);
2257
2258   gtk_css_provider_reset (css_provider);
2259
2260   return gtk_css_provider_load_internal (css_provider, NULL, file, NULL, 0, error);
2261 }
2262
2263 /**
2264  * gtk_css_provider_load_from_path:
2265  * @css_provider: a #GtkCssProvider
2266  * @path: the path of a filename to load, in the GLib filename encoding
2267  * @error: (out) (allow-none): return location for a #GError, or %NULL
2268  *
2269  * Loads the data contained in @path into @css_provider, making it clear
2270  * any previously loaded information.
2271  *
2272  * Returns: %TRUE if the data could be loaded.
2273  **/
2274 gboolean
2275 gtk_css_provider_load_from_path (GtkCssProvider  *css_provider,
2276                                  const gchar     *path,
2277                                  GError         **error)
2278 {
2279   GFile *file;
2280   gboolean result;
2281
2282   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
2283   g_return_val_if_fail (path != NULL, FALSE);
2284
2285   file = g_file_new_for_path (path);
2286   
2287   result = gtk_css_provider_load_from_file (css_provider, file, error);
2288
2289   g_object_unref (file);
2290
2291   return result;
2292 }
2293
2294 /**
2295  * gtk_css_provider_get_default:
2296  *
2297  * Returns the provider containing the style settings used as a
2298  * fallback for all widgets.
2299  *
2300  * Returns: (transfer none): The provider used for fallback styling.
2301  *          This memory is owned by GTK+, and you must not free it.
2302  **/
2303 GtkCssProvider *
2304 gtk_css_provider_get_default (void)
2305 {
2306   static GtkCssProvider *provider;
2307
2308   if (G_UNLIKELY (!provider))
2309     {
2310       const gchar *str =
2311         "@define-color fg_color #000; \n"
2312         "@define-color bg_color #dcdad5; \n"
2313         "@define-color text_color #000; \n"
2314         "@define-color base_color #fff; \n"
2315         "@define-color selected_bg_color #4b6983; \n"
2316         "@define-color selected_fg_color #fff; \n"
2317         "@define-color tooltip_bg_color #eee1b3; \n"
2318         "@define-color tooltip_fg_color #000; \n"
2319         "@define-color placeholder_text_color #808080; \n"
2320         "\n"
2321         "@define-color info_fg_color rgb (181, 171, 156);\n"
2322         "@define-color info_bg_color rgb (252, 252, 189);\n"
2323         "@define-color warning_fg_color rgb (173, 120, 41);\n"
2324         "@define-color warning_bg_color rgb (250, 173, 61);\n"
2325         "@define-color question_fg_color rgb (97, 122, 214);\n"
2326         "@define-color question_bg_color rgb (138, 173, 212);\n"
2327         "@define-color error_fg_color rgb (166, 38, 38);\n"
2328         "@define-color error_bg_color rgb (237, 54, 54);\n"
2329         "\n"
2330         "* {\n"
2331         "  background-color: @bg_color;\n"
2332         "  color: @fg_color;\n"
2333         "  border-color: shade (@bg_color, 0.6);\n"
2334         "  padding: 2;\n"
2335         "  border-width: 0;\n"
2336         "}\n"
2337         "\n"
2338         "*:prelight {\n"
2339         "  background-color: shade (@bg_color, 1.05);\n"
2340         "  color: shade (@fg_color, 1.3);\n"
2341         "}\n"
2342         "\n"
2343         "*:selected {\n"
2344         "  background-color: @selected_bg_color;\n"
2345         "  color: @selected_fg_color;\n"
2346         "}\n"
2347         "\n"
2348         ".expander, GtkTreeView.view.expander {\n"
2349         "  color: #fff;\n"
2350         "}\n"
2351         "\n"
2352         ".expander:prelight,\n"
2353         "GtkTreeView.view.expander:selected:prelight {\n"
2354         "  color: @text_color;\n"
2355         "}\n"
2356         "\n"
2357         ".expander:active {\n"
2358         "  transition: 200ms linear;\n"
2359         "}\n"
2360         "\n"
2361         "*:insensitive {\n"
2362         "  border-color: shade (@bg_color, 0.7);\n"
2363         "  background-color: shade (@bg_color, 0.9);\n"
2364         "  color: shade (@bg_color, 0.7);\n"
2365         "}\n"
2366         "\n"
2367         ".view {\n"
2368         "  border-width: 0;\n"
2369         "  border-radius: 0;\n"
2370         "  background-color: @base_color;\n"
2371         "  color: @text_color;\n"
2372         "}\n"
2373         ".view:selected {\n"
2374         "  background-color: shade (@bg_color, 0.9);\n"
2375         "  color: @fg_color;\n"
2376         "}\n"
2377         "\n"
2378         ".view:selected:focused {\n"
2379         "  background-color: @selected_bg_color;\n"
2380         "  color: @selected_fg_color;\n"
2381         "}\n"
2382         "\n"
2383         ".view column:sorted row,\n"
2384         ".view column:sorted row:prelight {\n"
2385         "  background-color: shade (@bg_color, 0.85);\n"
2386         "}\n"
2387         "\n"
2388         ".view column:sorted row:nth-child(odd),\n"
2389         ".view column:sorted row:nth-child(odd):prelight {\n"
2390         "  background-color: shade (@bg_color, 0.8);\n"
2391         "}\n"
2392         "\n"
2393         ".view row,\n"
2394         ".view row:prelight {\n"
2395         "  background-color: @base_color;\n"
2396         "  color: @text_color;\n"
2397         "}\n"
2398         "\n"
2399         ".view row:nth-child(odd),\n"
2400         ".view row:nth-child(odd):prelight {\n"
2401         "  background-color: shade (@base_color, 0.93); \n"
2402         "}\n"
2403         "\n"
2404         ".view row:selected:focused {\n"
2405         "  background-color: @selected_bg_color;\n"
2406         "}\n"
2407         "\n"
2408         ".view row:selected {\n"
2409         "  background-color: darker (@bg_color);\n"
2410         "  color: @selected_fg_color;\n"
2411         "}\n"
2412         "\n"
2413         ".view.cell.trough,\n"
2414         ".view.cell.trough:hover,\n"
2415         ".view.cell.trough:selected,\n"
2416         ".view.cell.trough:selected:focused {\n"
2417         "  background-color: @bg_color;\n"
2418         "  color: @fg_color;\n"
2419         "}\n"
2420         "\n"
2421         ".view.cell.progressbar,\n"
2422         ".view.cell.progressbar:hover,\n"
2423         ".view.cell.progressbar:selected,\n"
2424         ".view.cell.progressbar:selected:focused {\n"
2425         "  background-color: @selected_bg_color;\n"
2426         "  color: @selected_fg_color;\n"
2427         "}\n"
2428         "\n"
2429         ".rubberband {\n"
2430         "  background-color: alpha (@fg_color, 0.25);\n"
2431         "  border-color: @fg_color;\n"
2432         "  border-style: solid;\n"
2433         "  border-width: 1;\n"
2434         "}\n"
2435         "\n"
2436         ".tooltip {\n"
2437         "  background-color: @tooltip_bg_color; \n"
2438         "  color: @tooltip_fg_color; \n"
2439         "  border-color: @tooltip_fg_color; \n"
2440         "  border-width: 1;\n"
2441         "  border-style: solid;\n"
2442         "}\n"
2443         "\n"
2444         ".button,\n"
2445         ".slider {\n"
2446         "  border-style: outset; \n"
2447         "  border-width: 2; \n"
2448         "}\n"
2449         "\n"
2450         ".button:active {\n"
2451         "  background-color: shade (@bg_color, 0.7);\n"
2452         "  border-style: inset; \n"
2453         "}\n"
2454         "\n"
2455         ".button:prelight,\n"
2456         ".slider:prelight {\n"
2457         "  background-color: @selected_bg_color;\n"
2458         "  color: @selected_fg_color;\n"
2459         "  border-color: shade (@selected_bg_color, 0.7);\n"
2460         "}\n"
2461         "\n"
2462         ".trough {\n"
2463         "  background-color: darker (@bg_color);\n"
2464         "  border-style: inset;\n"
2465         "  border-width: 1;\n"
2466         "  padding: 0;\n"
2467         "}\n"
2468         "\n"
2469         ".entry {\n"
2470         "  border-style: inset;\n"
2471         "  border-width: 2;\n"
2472         "  background-color: @base_color;\n"
2473         "  color: @text_color;\n"
2474         "}\n"
2475         "\n"
2476         ".entry:insensitive {\n"
2477         "  background-color: shade (@base_color, 0.9);\n"
2478         "  color: shade (@base_color, 0.7);\n"
2479         "}\n"
2480         ".entry:active {\n"
2481         "  background-color: #c4c2bd;\n"
2482         "  color: #000;\n"
2483         "}\n"
2484         "\n"
2485         ".progressbar,\n"
2486         ".entry.progressbar, \n"
2487         ".cell.progressbar {\n"
2488         "  background-color: @selected_bg_color;\n"
2489         "  border-color: shade (@selected_bg_color, 0.7);\n"
2490         "  color: @selected_fg_color;\n"
2491         "  border-style: outset;\n"
2492         "  border-width: 1;\n"
2493         "}\n"
2494         "\n"
2495         "GtkCheckButton:hover,\n"
2496         "GtkCheckButton:selected,\n"
2497         "GtkRadioButton:hover,\n"
2498         "GtkRadioButton:selected {\n"
2499         "  background-color: shade (@bg_color, 1.05);\n"
2500         "}\n"
2501         "\n"
2502         ".check, .radio,"
2503         ".cell.check, .cell.radio,\n"
2504         ".cell.check:hover, .cell.radio:hover {\n"
2505         "  border-style: solid;\n"
2506         "  border-width: 1;\n"
2507         "  background-color: @base_color;\n"
2508         "  border-color: @fg_color;\n"
2509         "}\n"
2510         "\n"
2511         ".check:active, .radio:active,\n"
2512         ".check:hover, .radio:hover {\n"
2513         "  background-color: @base_color;\n"
2514         "  border-color: @fg_color;\n"
2515         "  color: @text_color;\n"
2516         "}\n"
2517         "\n"
2518         ".check:selected, .radio:selected {\n"
2519         "  background-color: darker (@bg_color);\n"
2520         "  color: @selected_fg_color;\n"
2521         "  border-color: @selected_fg_color;\n"
2522         "}\n"
2523         "\n"
2524         ".check:selected:focused, .radio:selected:focused {\n"
2525         "  background-color: @selected_bg_color;\n"
2526         "}\n"
2527         "\n"
2528         ".menu.check, .menu.radio {\n"
2529         "  color: @fg_color;\n"
2530         "  border-style: none;\n"
2531         "  border-width: 0;\n"
2532         "}\n"
2533         "\n"
2534         ".popup {\n"
2535         "  border-style: outset;\n"
2536         "  border-width: 1;\n"
2537         "}\n"
2538         "\n"
2539         ".viewport {\n"
2540         "  border-style: inset;\n"
2541         "  border-width: 2;\n"
2542         "}\n"
2543         "\n"
2544         ".notebook {\n"
2545         "  border-style: outset;\n"
2546         "  border-width: 1;\n"
2547         "}\n"
2548         "\n"
2549         ".frame {\n"
2550         "  border-style: inset;\n"
2551         "  border-width: 1;\n"
2552         "}\n"
2553         "\n"
2554         "GtkScrolledWindow.frame {\n"
2555         "  padding: 0;\n"
2556         "}\n"
2557         "\n"
2558         ".menu,\n"
2559         ".menubar,\n"
2560         ".toolbar {\n"
2561         "  border-style: outset;\n"
2562         "  border-width: 1;\n"
2563         "}\n"
2564         "\n"
2565         ".menu:hover,\n"
2566         ".menubar:hover,\n"
2567         ".menu.check:hover,\n"
2568         ".menu.radio:hover {\n"
2569         "  background-color: @selected_bg_color;\n"
2570         "  color: @selected_fg_color;\n"
2571         "}\n"
2572         "\n"
2573         "GtkSpinButton.button {\n"
2574         "  border-width: 1;\n"
2575         "}\n"
2576         "\n"
2577         ".scale.slider:hover,\n"
2578         "GtkSpinButton.button:hover {\n"
2579         "  background-color: shade (@bg_color, 1.05);\n"
2580         "  border-color: shade (@bg_color, 0.8);\n"
2581         "}\n"
2582         "\n"
2583         "GtkSwitch.trough:active {\n"
2584         "  background-color: @selected_bg_color;\n"
2585         "  color: @selected_fg_color;\n"
2586         "}\n"
2587         "\n"
2588         "GtkToggleButton.button:inconsistent {\n"
2589         "  border-style: outset;\n"
2590         "  border-width: 1px;\n"
2591         "  background-color: shade (@bg_color, 0.9);\n"
2592         "  border-color: shade (@bg_color, 0.7);\n"
2593         "}\n"
2594         "\n"
2595         "GtkLabel:selected {\n"
2596         "  background-color: shade (@bg_color, 0.9);\n"
2597         "}\n"
2598         "\n"
2599         "GtkLabel:selected:focused {\n"
2600         "  background-color: @selected_bg_color;\n"
2601         "}\n"
2602         "\n"
2603         ".spinner:active {\n"
2604         "  transition: 750ms linear loop;\n"
2605         "}\n"
2606         "\n"
2607         ".info {\n"
2608         "  background-color: @info_bg_color;\n"
2609         "  color: @info_fg_color;\n"
2610         "}\n"
2611         "\n"
2612         ".warning {\n"
2613         "  background-color: @warning_bg_color;\n"
2614         "  color: @warning_fg_color;\n"
2615         "}\n"
2616         "\n"
2617         ".question {\n"
2618         "  background-color: @question_bg_color;\n"
2619         "  color: @question_fg_color;\n"
2620         "}\n"
2621         "\n"
2622         ".error {\n"
2623         "  background-color: @error_bg_color;\n"
2624         "  color: @error_fg_color;\n"
2625         "}\n"
2626         "\n"
2627         ".highlight {\n"
2628         "  background-color: @selected_bg_color;\n"
2629         "  color: @selected_fg_color;\n"
2630         "}\n"
2631         "\n"
2632         ".light-area-focus {\n"
2633         "  color: #000;\n"
2634         "}\n"
2635         "\n"
2636         ".dark-area-focus {\n"
2637         "  color: #fff;\n"
2638         "}\n"
2639         "GtkCalendar.view {\n"
2640         "  border-width: 1;\n"
2641         "  border-style: inset;\n"
2642         "  padding: 1;\n"
2643         "}\n"
2644         "\n"
2645         "GtkCalendar.view:inconsistent {\n"
2646         "  color: darker (@bg_color);\n"
2647         "}\n"
2648         "\n"
2649         "GtkCalendar.header {\n"
2650         "  background-color: @bg_color;\n"
2651         "  border-style: outset;\n"
2652         "  border-width: 2;\n"
2653         "}\n"
2654         "\n"
2655         "GtkCalendar.highlight {\n"
2656         "  border-width: 0;\n"
2657         "}\n"
2658         "\n"
2659         "GtkCalendar.button {\n"
2660         "  background-color: @bg_color;\n"
2661         "}\n"
2662         "\n"
2663         "GtkCalendar.button:hover {\n"
2664         "  background-color: lighter (@bg_color);\n"
2665         "  color: @fg_color;\n"
2666         "}\n"
2667         "\n"
2668         ".menu * {\n"
2669         "  border-width: 0;\n"
2670         "  padding: 2;\n"
2671         "}\n"
2672         "\n";
2673
2674       provider = gtk_css_provider_new ();
2675       if (!gtk_css_provider_load_from_data (provider, str, -1, NULL))
2676         {
2677           g_error ("Failed to load the internal default CSS.");
2678         }
2679     }
2680
2681   return provider;
2682 }
2683
2684 gchar *
2685 _gtk_css_provider_get_theme_dir (void)
2686 {
2687   const gchar *var;
2688   gchar *path;
2689
2690   var = g_getenv ("GTK_DATA_PREFIX");
2691
2692   if (var)
2693     path = g_build_filename (var, "share", "themes", NULL);
2694   else
2695     path = g_build_filename (GTK_DATA_PREFIX, "share", "themes", NULL);
2696
2697   return path;
2698 }
2699
2700 /**
2701  * gtk_css_provider_get_named:
2702  * @name: A theme name
2703  * @variant: (allow-none): variant to load, for example, "dark", or
2704  *     %NULL for the default
2705  *
2706  * Loads a theme from the usual theme paths
2707  *
2708  * Returns: (transfer none): a #GtkCssProvider with the theme loaded.
2709  *     This memory is owned by GTK+, and you must not free it.
2710  */
2711 GtkCssProvider *
2712 gtk_css_provider_get_named (const gchar *name,
2713                             const gchar *variant)
2714 {
2715   static GHashTable *themes = NULL;
2716   GtkCssProvider *provider;
2717   gchar *key;
2718
2719   if (G_UNLIKELY (!themes))
2720     themes = g_hash_table_new (g_str_hash, g_str_equal);
2721
2722   if (variant == NULL)
2723     key = (gchar *)name;
2724   else
2725     key = g_strconcat (name, "-", variant, NULL);
2726
2727   provider = g_hash_table_lookup (themes, key);
2728
2729   if (!provider)
2730     {
2731       const gchar *home_dir;
2732       gchar *subpath, *path = NULL;
2733
2734       if (variant)
2735         subpath = g_strdup_printf ("gtk-3.0" G_DIR_SEPARATOR_S "gtk-%s.css", variant);
2736       else
2737         subpath = g_strdup ("gtk-3.0" G_DIR_SEPARATOR_S "gtk.css");
2738
2739       /* First look in the users home directory
2740        */
2741       home_dir = g_get_home_dir ();
2742       if (home_dir)
2743         {
2744           path = g_build_filename (home_dir, ".themes", name, subpath, NULL);
2745
2746           if (!g_file_test (path, G_FILE_TEST_EXISTS))
2747             {
2748               g_free (path);
2749               path = NULL;
2750             }
2751         }
2752
2753       if (!path)
2754         {
2755           gchar *theme_dir;
2756
2757           theme_dir = _gtk_css_provider_get_theme_dir ();
2758           path = g_build_filename (theme_dir, name, subpath, NULL);
2759           g_free (theme_dir);
2760
2761           if (!g_file_test (path, G_FILE_TEST_EXISTS))
2762             {
2763               g_free (path);
2764               path = NULL;
2765             }
2766         }
2767
2768       g_free (subpath);
2769
2770       if (path)
2771         {
2772           GError *error;
2773
2774           provider = gtk_css_provider_new ();
2775           error = NULL;
2776           if (!gtk_css_provider_load_from_path (provider, path, &error))
2777             {
2778               g_warning ("Could not load named theme \"%s\": %s", name, error->message);
2779               g_error_free (error);
2780
2781               g_object_unref (provider);
2782               provider = NULL;
2783             }
2784           else
2785             g_hash_table_insert (themes, g_strdup (key), provider);
2786
2787           g_free (path);
2788         }
2789     }
2790
2791   if (key != name)
2792     g_free (key);
2793
2794   return provider;
2795 }
2796
2797 static void
2798 selector_style_info_print (const SelectorStyleInfo *info,
2799                            GString                 *str)
2800 {
2801   GList *keys, *walk;
2802   char *s;
2803
2804   _gtk_css_selector_print (info->selector, str);
2805
2806   g_string_append (str, " {\n");
2807
2808   keys = g_hash_table_get_keys (info->style);
2809   /* so the output is identical for identical selector styles */
2810   keys = g_list_sort (keys, (GCompareFunc) strcmp);
2811
2812   for (walk = keys; walk; walk = walk->next)
2813     {
2814       const char *name = walk->data;
2815       const GValue *value = g_hash_table_lookup (info->style, (gpointer) name);
2816
2817       g_string_append (str, "  ");
2818       g_string_append (str, name);
2819       g_string_append (str, ": ");
2820       s = _gtk_css_value_to_string (value);
2821       g_string_append (str, s);
2822       g_free (s);
2823       g_string_append (str, ";\n");
2824     }
2825
2826   g_list_free (keys);
2827
2828   g_string_append (str, "}\n");
2829 }
2830
2831 static void
2832 gtk_css_provider_print_colors (GHashTable *colors,
2833                                GString    *str)
2834 {
2835   GList *keys, *walk;
2836   char *s;
2837
2838   keys = g_hash_table_get_keys (colors);
2839   /* so the output is identical for identical styles */
2840   keys = g_list_sort (keys, (GCompareFunc) strcmp);
2841
2842   for (walk = keys; walk; walk = walk->next)
2843     {
2844       const char *name = walk->data;
2845       GtkSymbolicColor *symbolic = g_hash_table_lookup (colors, (gpointer) name);
2846
2847       g_string_append (str, "@define-color ");
2848       g_string_append (str, name);
2849       g_string_append (str, " ");
2850       s = gtk_symbolic_color_to_string (symbolic);
2851       g_string_append (str, s);
2852       g_free (s);
2853       g_string_append (str, ";\n");
2854     }
2855
2856   g_list_free (keys);
2857 }
2858
2859 /**
2860  * gtk_css_provider_to_string:
2861  * @provider: the provider to write to a string
2862  *
2863  * Convertes the @provider into a string representation in CSS
2864  * format.
2865  * 
2866  * Using gtk_css_provider_load_from_data() with the return value
2867  * from this function on a new provider created with
2868  * gtk_css_provider_new() will basicallu create a duplicate of
2869  * this @provider.
2870  *
2871  * Returns: a new string representing the @provider.
2872  **/
2873 char *
2874 gtk_css_provider_to_string (GtkCssProvider *provider)
2875 {
2876   GtkCssProviderPrivate *priv;
2877   GString *str;
2878   guint i;
2879
2880   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (provider), NULL);
2881
2882   priv = provider->priv;
2883
2884   str = g_string_new ("");
2885
2886   gtk_css_provider_print_colors (priv->symbolic_colors, str);
2887
2888   for (i = 0; i < priv->selectors_info->len; i++)
2889     {
2890       if (i > 0)
2891         g_string_append (str, "\n");
2892       selector_style_info_print (g_ptr_array_index (priv->selectors_info, i),
2893                                  str);
2894     }
2895
2896   return g_string_free (str, FALSE);
2897 }
2898