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