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