]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkcssprovider.c
Make GtkTooltip use GtkStyleContext
[~andy/gtk] / gtk / gtkcssprovider.c
index 8f703f4ef160c90bf61e4274b6b8fbf4d96dd587..1bd7bcd5fa725c8b99345cbda123885ea586f2ae 100644 (file)
@@ -29,6 +29,7 @@
 #include "gtkanimationdescription.h"
 #include "gtk9slice.h"
 #include "gtkcssprovider.h"
+#include "gtkprivate.h"
 
 /**
  * SECTION:gtkcssprovider
  * @Title: GtkCssProvider
  * @See_also: #GtkStyleContext, #GtkStyleProvider
  *
- * #GtkCssProvider is an object implementing #GtkStyleProvider, it is able
- * to parse CSS-like input in order to style widgets.
+ * GtkCssProvider is an object implementing the #GtkStyleProvider interface.
+ * It is able to parse <ulink url="http://www.w3.org/TR/CSS2">CSS</ulink>-like
+ * input in order to style widgets.
  *
+ * <refsect2 id="gtkcssprovider-files">
+ * <title>Default files</title>
+ * <para>
+ * An application can cause GTK+ to parse a specific CSS style sheet by
+ * calling gtk_css_provider_load_from_file() and adding the provider with
+ * gtk_style_context_add_provider() or gtk_style_context_add_provider_for_screen().
+ * In addition, certain files will be read when GTK+ is initialized. First,
+ * the file <filename><envar>$XDG_CONFIG_HOME</envar>/gtk-3.0/gtk.css</filename>
+ * is loaded if it exists. Then, GTK+ tries to load
+ * <filename><envar>$HOME</envar>/.themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
+ * falling back to
+ * <filename><replaceable>datadir</replaceable>/share/themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
+ * where <replaceable>theme-name</replaceable> is the name of the current theme
+ * (see the #GtkSettings:gtk-theme-name setting) and <replaceable>datadir</replaceable>
+ * is the prefix configured when GTK+ was compiled, unless overridden by the
+ * <envar>GTK_DATA_PREFIX</envar> environment variable.
+ * </para>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-stylesheets">
+ * <title>Style sheets</title>
+ * <para>
+ * The basic structure of the style sheets understood by this provider is
+ * a series of statements, which are either rule sets or '@-rules', separated
+ * by whitespace.
+ * </para>
+ * <para>
+ * A rule set consists of a selector and a declaration block, which is
+ * a series of declarations enclosed in curly braces ({ and }). The
+ * declarations are separated by semicolons (;). Multiple selectors can
+ * share the same declaration block, by putting all the separators in
+ * front of the block, separated by commas.
+ * </para>
+ * <example><title>A rule set with two selectors</title>
+ * <programlisting language="text">
+ * GtkButton, GtkEntry {
+ *     color: &num;ff00ea;
+ *     font: Comic Sans 12
+ * }
+ * </programlisting>
+ * </example>
+ * </refsect2>
  * <refsect2 id="gtkcssprovider-selectors">
- * <title>Widget selectors</title>
+ * <title>Selectors</title>
+ * <para>
+ * Selectors work very similar to the way they do in CSS, with widget class
+ * names taking the role of element names, and widget names taking the role
+ * of IDs. When used in a selector, widget names must be prefixed with a
+ * '&num;' character. The '*' character represents the so-called universal
+ * selector, which matches any widget.
+ * </para>
  * <para>
- * Selectors work in a really similar way than in common CSS, widget object
- * names act as HTML tags:
+ * To express more complicated situations, selectors can be combined in
+ * various ways:
+ * <itemizedlist>
+ * <listitem><para>To require that a widget satisfies several conditions,
+ *   combine several selectors into one by concatenating them. E.g.
+ *   <literal>GtkButton&num;button1</literal> matches a GtkButton widget
+ *   with the name button1.</para></listitem>
+ * <listitem><para>To only match a widget when it occurs inside some other
+ *   widget, write the two selectors after each other, separated by whitespace.
+ *   E.g. <literal>GtkToolBar GtkButton</literal> matches GtkButton widgets
+ *   that occur inside a GtkToolBar.</para></listitem>
+ * <listitem><para>In the previous example, the GtkButton is matched even
+ *   if it occurs deeply nested inside the toolbar. To restrict the match
+ *   to direct children of the parent widget, insert a '>' character between
+ *   the two selectors. E.g. <literal>GtkNotebook > GtkLabel</literal> matches
+ *   GtkLabel widgets that are direct children of a GtkNotebook.</para></listitem>
+ * </itemizedlist>
  * </para>
  * <example>
- * <title>Widgets in selectors</title>
- * <programlisting>
+ * <title>Widget classes and names in selectors</title>
+ * <programlisting language="text">
  * /&ast; Theme labels that are descendants of a window &ast;/
  * GtkWindow GtkLabel {
- *     background-color: &num;898989;
+ *     background-color: &num;898989
  * }
  *
  * /&ast; Theme notebooks, and anything that's within these &ast;/
  * GtkNotebook {
- *     background-color: &num;a939f0;
+ *     background-color: &num;a939f0
  * }
  *
  * /&ast; Theme combo boxes, and entries that
  *  are direct children of a notebook &ast;/
  * GtkComboBox,
  * GtkNotebook > GtkEntry {
- *     background-color: &num;1209a2;
+ *     color: @fg_color;
+ *     background-color: &num;1209a2
  * }
  *
  * /&ast; Theme any widget within a GtkBin &ast;/
  * GtkBin * {
- *     font-name: Sans 20;
+ *     font-name: Sans 20
  * }
- * </programlisting>
- * </example>
- * <para>
- * Widget names may be matched in CSS as well:
- * </para>
- * <example>
- * <title>Widget names in selectors</title>
- * <programlisting>
+ *
  * /&ast; Theme a label named title-label &ast;/
  * GtkLabel&num;title-label {
- *     font-name: Sans 15;
+ *     font-name: Sans 15
  * }
  *
  * /&ast; Theme any widget named main-entry &ast;/
  * &num;main-entry {
- *     background-color: &num;f0a810;
+ *     background-color: &num;f0a810
  * }
  * </programlisting>
  * </example>
  * <para>
- * Widgets may also define different classes, so these can be matched
- * in CSS:
+ * Widgets may also define style classes, which can be used for matching.
+ * When used in a selector, style classes must be prefixed with a '.'
+ * character.
+ * </para>
+ * <para>
+ * Refer to the documentation of individual widgets to learn which
+ * style classes they define and see <xref linkend="gtkstylecontext-classes"/>
+ * for a list of all style classes used by GTK+ widgets.
  * </para>
  * <example>
- * <title>Widget names in selectors</title>
- * <programlisting>
+ * <title>Style classes in selectors</title>
+ * <programlisting language="text">
  * /&ast; Theme all widgets defining the class entry &ast;/
  * .entry {
  *     color: &num;39f1f9;
  *
  * /&ast; Theme spinbuttons' entry &ast;/
  * GtkSpinButton.entry {
- *     color: &num;900185;
+ *     color: &num;900185
  * }
  * </programlisting>
  * </example>
  * <para>
- * Container widgets may define regions, these region names may be
- * referenced in CSS, it's also possible to apply :nth-child
- * pseudo-class information if the widget implementation provides
- * such data.
+ * In complicated widgets like e.g. a GtkNotebook, it may be desirable
+ * to style different parts of the widget differently. To make this
+ * possible, container widgets may define regions, whose names
+ * may be used for matching in selectors.
+ * </para>
+ * <para>
+ * Some containers allow to further differentiate between regions by
+ * applying so-called pseudo-classes to the region. For example, the
+ * tab region in GtkNotebook allows to single out the first or last
+ * tab by using the :first-child or :last-child pseudo-class.
+ * When used in selectors, pseudo-classes must be prefixed with a
+ * ':' character.
+ * </para>
+ * <para>
+ * Refer to the documentation of individual widgets to learn which
+ * regions and pseudo-classes they define and see
+ * <xref linkend="gtkstylecontext-classes"/> for a list of all regions
+ * used by GTK+ widgets.
  * </para>
  * <example>
- * <title>Region names in containers</title>
- * <programlisting>
+ * <title>Regions in selectors</title>
+ * <programlisting language="text">
  * /&ast; Theme any label within a notebook &ast;/
  * GtkNotebook GtkLabel {
  *     color: &num;f90192;
  * }
  *
  * /&ast; Theme labels within notebook tabs &ast;/
- * GtkNotebook tab:nth-child GtkLabel {
+ * GtkNotebook tab GtkLabel {
  *     color: &num;703910;
  * }
  *
  * </programlisting>
  * </example>
  * <para>
- * Widget states may be matched as pseudoclasses.
- * Given the needed widget states differ from the
- * offered pseudoclasses in CSS, some are unsupported,
- * and some custom ones have been added.
+ * Another use of pseudo-classes is to match widgets depending on their
+ * state. This is conceptually similar to the :hover, :active or :focus
+ * pseudo-classes in CSS. The available pseudo-classes for widget states
+ * are :active, :prelight (or :hover), :insensitive, :selected, :focused
+ * and :inconsistent.
  * </para>
  * <example>
  * <title>Styling specific widget states</title>
- * <programlisting>
+ * <programlisting language="text">
  * /&ast; Theme active (pressed) buttons &ast;/
  * GtkButton:active {
  *     background-color: &num;0274d9;
  * }
  *
- * /&ast; Theme buttons with the mouse pointer on it &ast;/
+ * /&ast; Theme buttons with the mouse pointer on it,
+ *    both are equivalent &ast;/
  * GtkButton:hover,
  * GtkButton:prelight {
  *     background-color: &num;3085a9;
  * </programlisting>
  * </example>
  * <para>
- * Widget state pseudoclasses may only apply to the
- * last element in a selector.
+ * Widget state pseudoclasses may only apply to the last element
+ * in a selector.
  * </para>
  * <para>
- * All the mentioned elements may be combined to create
- * complex selectors that match specific widget paths.
- * As in CSS, rules apply by specificity, so the selectors
- * describing the best a widget path will take precedence
+ * To determine the effective style for a widget, all the matching rule
+ * sets are merged. As in CSS, rules apply by specificity, so the rules
+ * whose selectors more closely match a widget path will take precedence
  * over the others.
  * </para>
  * </refsect2>
  * <refsect2 id="gtkcssprovider-rules">
- * <title>&commat; rules</title>
+ * <title>&commat; Rules</title>
  * <para>
- * GTK+'s CSS supports the &commat;import rule, in order
- * to load another CSS file in addition to the currently
- * parsed one.
+ * GTK+'s CSS supports the &commat;import rule, in order to load another
+ * CSS style sheet in addition to the currently parsed one.
  * </para>
  * <example>
  * <title>Using the &commat;import rule</title>
- * <programlisting>
- * &commat;import url (path/to/common.css)
+ * <programlisting language="text">
+ * &commat;import url ("path/to/common.css");
  * </programlisting>
  * </example>
  * <para>
- * GTK+ also supports an additional &commat;define-color
- * rule, in order to define a color name which may be used
- * instead of color numeric representations.
+ * GTK+ also supports an additional &commat;define-color rule, in order
+ * to define a color name which may be used instead of color numeric
+ * representations. Also see the #GtkSettings:gtk-color-scheme setting
+ * for a way to override the values of these named colors.
  * </para>
  * <example>
  * <title>Defining colors</title>
- * <programlisting>
+ * <programlisting language="text">
  * &commat;define-color bg_color &num;f9a039;
  *
  * &ast; {
  * <refsect2 id="gtkcssprovider-symbolic-colors">
  * <title>Symbolic colors</title>
  * <para>
- * Besides being able to define color names, the CSS
- * parser is also able to read different color modifiers,
- * which can also be nested, providing a rich language
- * to define colors starting from other colors.
+ * Besides being able to define color names, the CSS parser is also able
+ * to read different color expressions, which can also be nested, providing
+ * a rich language to define colors which are derived from a set of base
+ * colors.
  * </para>
  * <example>
  * <title>Using symbolic colors</title>
- * <programlisting>
+ * <programlisting language="text">
  * &commat;define-color entry-color shade (&commat;bg_color, 0.7);
  *
  * GtkEntry {
  * }
  * </programlisting>
  * </example>
+ * <para>
+ *   The various ways to express colors in GTK+ CSS are:
+ * </para>
+ * <informaltable>
+ *   <tgroup cols="3">
+ *     <thead>
+ *       <row>
+ *         <entry>Syntax</entry>
+ *         <entry>Explanation</entry>
+ *         <entry>Examples</entry>
+ *       </row>
+ *     </thead>
+ *     <tbody>
+ *       <row>
+ *         <entry>rgb(@r, @g, @b)</entry>
+ *         <entry>An opaque color; @r, @g, @b can be either integers between
+ *                0 and 255 or percentages</entry>
+ *         <entry><literallayout>rgb(128, 10, 54)
+ * rgb(20%, 30%, 0%)</literallayout></entry>
+ *       </row>
+ *       <row>
+ *         <entry>rgba(@r, @g, @b, @a)</entry>
+ *         <entry>A translucent color; @r, @g, @b are as in the previous row,
+ *                @a is a floating point number between 0 and 1</entry>
+ *         <entry><literallayout>rgba(255, 255, 0, 0.5)</literallayout></entry>
+ *       </row>
+ *       <row>
+ *         <entry>&num;@xxyyzz</entry>
+ *         <entry>An opaque color; @xx, @yy, @zz are hexadecimal numbers
+ *                specifying @r, @g, @b variants with between 1 and 4
+ *                hexadecimal digits per component are allowed</entry>
+ *         <entry><literallayout>&num;ff12ab
+ * &num;f0c</literallayout></entry>
+ *       </row>
+ *       <row>
+ *         <entry>&commat;name</entry>
+ *         <entry>Reference to a color that has been defined with
+ *                &commat;define-color
+ *         </entry>
+ *         <entry>&commat;bg_color</entry>
+ *       </row>
+ *       <row>
+ *         <entry>mix(@color1, @color2, @f)</entry>
+ *         <entry>A linear combination of @color1 and @color2. @f is a
+ *                floating point number between 0 and 1.</entry>
+ *         <entry><literallayout>mix(&num;ff1e0a, &commat;bg_color, 0.8)</literallayout></entry>
+ *       </row>
+ *       <row>
+ *         <entry>shade(@color, @f)</entry>
+ *         <entry>A lighter or darker variant of @color. @f is a
+ *                floating point number.
+ *         </entry>
+ *         <entry>shade(&commat;fg_color, 0.5)</entry>
+ *       </row>
+ *       <row>
+ *         <entry>lighter(@color)</entry>
+ *         <entry>A lighter variant of @color</entry>
+ *       </row>
+ *       <row>
+ *         <entry>darker(@color)</entry>
+ *         <entry>A darker variant of @color</entry>
+ *       </row>
+ *     </tbody>
+ *   </tgroup>
+ * </informaltable>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-gradients">
+ * <title>Gradients</title>
+ * <para>
+ * Linear or radial Gradients can be used as background images.
+ * </para>
+ * <para>
+ * A linear gradient along the line from (@start_x, @start_y) to
+ * (@end_x, @end_y) is specified using the syntax
+ * <literallayout>-gtk-gradient (linear,
+ *               @start_x @start_y, @end_x @end_y,
+ *               color-stop (@position, @color),
+ *               ...)</literallayout>
+ * where @start_x and @end_x can be either a floating point number between
+ * 0 and 1 or one of the special values 'left', 'right' or 'center', @start_y
+ * and @end_y can be either a floating point number between 0 and 1 or one
+ * of the special values 'top', 'bottom' or 'center', @position is a floating
+ * point number between 0 and 1 and @color is a color expression (see above).
+ * The color-stop can be repeated multiple times to add more than one color
+ * stop. 'from (@color)' and 'to (@color)' can be used as abbreviations for
+ * color stops with position 0 and 1, respectively.
+ * </para>
+ * <example>
+ * <title>A linear gradient</title>
+ * <inlinegraphic fileref="gradient1.png" format="PNG"/>
+ * <para>This gradient was specified with
+ * <literallayout>-gtk-gradient (linear,
+ *                left top, right bottom,
+ *                from(&commat;yellow), to(&commat;blue))</literallayout></para>
+ * </example>
+ * <example>
+ * <title>Another linear gradient</title>
+ * <inlinegraphic fileref="gradient2.png" format="PNG"/>
+ * <para>This gradient was specified with
+ * <literallayout>-gtk-gradient (linear,
+ *                0 0, 0 1,
+ *                color-stop(0, &commat;yellow),
+ *                color-stop(0.2, &commat;blue),
+ *                color-stop(1, &num;0f0))</literallayout></para>
+ * </example>
+ * <para>
+ * A radial gradient along the two circles defined by (@start_x, @start_y,
+ * @start_radius) and (@end_x, @end_y, @end_radius) is specified using the
+ * syntax
+ * <literallayout>-gtk-gradient (radial,
+ *                @start_x @start_y, @start_radius,
+ *                @end_x @end_y, @end_radius,
+ *                color-stop (@position, @color),
+ *                ...)</literallayout>
+ * where @start_radius and @end_radius are floating point numbers and
+ * the other parameters are as before.
+ * </para>
+ * <example>
+ * <title>A radial gradient</title>
+ * <inlinegraphic fileref="gradient3.png" format="PNG"/>
+ * <para>This gradient was specified with
+ * <literallayout>-gtk-gradient (radial,
+ *                center center, 0,
+ *                center center, 1,
+ *                from(&commat;yellow), to(&commat;green))</literallayout></para>
+ * </example>
+ * <example>
+ * <title>Another radial gradient</title>
+ * <inlinegraphic fileref="gradient4.png" format="PNG"/>
+ * <para>This gradient was specified with
+ * <literallayout>-gtk-gradient (radial,
+ *                0.4 0.4, 0.1,
+ *                0.6 0.6, 0.7,
+ *                color-stop (0, &num;f00),
+ *                color-stop (0.1, &num;a0f),
+ *                color-stop (0.2, &commat;yellow),
+ *                color-stop (1, &commat;green))</literallayout></para>
+ * </example>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-slices">
+ * <title>Border images</title>
+ * <para>
+ * Images can be used in 'slices' for the purpose of creating scalable
+ * borders.
+ * </para>
+ * <inlinegraphic fileref="slices.png" format="PNG"/>
+ * <para>
+ * The syntax for specifying border images of this kind is:
+ * <literallayout>url(@path) @top @right @bottom @left [repeat|stretch]? [repeat|stretch]?</literallayout>
+ * The sizes of the 'cut off' portions are specified
+ * with the @top, @right, @bottom and @left parameters.
+ * The 'middle' sections can be repeated or stretched to create
+ * the desired effect, by adding the 'repeat' or 'stretch' options after
+ * the dimensions. If two options are specified, the first one affects
+ * the horizontal behaviour and the second one the vertical behaviour.
+ * If only one option is specified, it affects both.
+ * </para>
+ * <example>
+ * <title>A border image</title>
+ * <inlinegraphic fileref="border1.png" format="PNG"/>
+ * <para>This border image was specified with
+ * <literallayout>url("gradient1.png") 10 10 10 10</literallayout>
+ * </para>
+ * </example>
+ * <example>
+ * <title>A repeating border image</title>
+ * <inlinegraphic fileref="border2.png" format="PNG"/>
+ * <para>This border image was specified with
+ * <literallayout>url("gradient1.png") 10 10 10 10 repeat</literallayout>
+ * </para>
+ * </example>
+ * <example>
+ * <title>A stretched border image</title>
+ * <inlinegraphic fileref="border3.png" format="PNG"/>
+ * <para>This border image was specified with
+ * <literallayout>url("gradient1.png") 10 10 10 10 stretch</literallayout>
+ * </para>
+ * </example>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-transitions">
+ * <para>Styles can specify transitions that will be used to create a gradual
+ * change in the appearance when a widget state changes. The following
+ * syntax is used to specify transitions:
+ * <literallayout>@duration [s|ms] [linear|ease|ease-in|ease-out|ease-in-out] [loop]?</literallayout>
+ * The @duration is the amount of time that the animation will take for
+ * a complete cycle from start to end. If the loop option is given, the
+ * animation will be repated until the state changes again.
+ * The option after the duration determines the transition function from a
+ * small set of predefined functions.
+ * <figure><title>Linear transition</title>
+ * <graphic fileref="linear.png" format="PNG"/>
+ * </figure>
+ * <figure><title>Ease transition</title>
+ * <graphic fileref="ease.png" format="PNG"/>
+ * </figure>
+ * <figure><title>Ease-in-out transition</title>
+ * <graphic fileref="ease-in-out.png" format="PNG"/>
+ * </figure>
+ * <figure><title>Ease-in transition</title>
+ * <graphic fileref="ease-in.png" format="PNG"/>
+ * </figure>
+ * <figure><title>Ease-out transition</title>
+ * <graphic fileref="ease-out.png" format="PNG"/>
+ * </figure>
+ * </para>
  * </refsect2>
  * <refsect2 id="gtkcssprovider-properties">
  * <title>Supported properties</title>
  * example in common CSS it is fine to define a font through
  * the different @font-family, @font-style, @font-size
  * properties, meanwhile in GTK+'s CSS only the canonical
- * @font property would be supported.
+ * @font property is supported.
  * </para>
  * <para>
  * The currently supported properties are:
  *     <tbody>
  *       <row>
  *         <entry>engine</entry>
- *         <entry><programlisting>engine-name</programlisting></entry>
+ *         <entry>engine-name</entry>
  *         <entry>#GtkThemingEngine</entry>
- *         <entry><programlisting>engine: clearlooks;</programlisting></entry>
+ *         <entry>engine: clearlooks;</entry>
  *       </row>
  *       <row>
  *         <entry>background-color</entry>
- *         <entry morerows="1"><programlisting>color</programlisting></entry>
- *         <entry morerows="1">#GdkRGBA</entry>
- *         <entry morerows="1">
- *           <programlisting>
- * background-color: &num;fff;
- * color: @color-name;
- * background-color: shade (@color-name, 0.5);
- * color: mix (@color-name, &num;f0f, 0.8);</programlisting>
+ *         <entry morerows="2">color (see above)</entry>
+ *         <entry morerows="2">#GdkRGBA</entry>
+ *         <entry morerows="2"><literallayout>background-color: &num;fff;
+ * color: &amp;color1;
+ * background-color: shade (&amp;color1, 0.5);
+ * color: mix (&amp;color1, &num;f0f, 0.8);</literallayout>
  *         </entry>
  *       </row>
  *       <row>
  *         <entry>color</entry>
  *       </row>
  *       <row>
+ *         <entry>border-color</entry>
+ *       </row>
+ *       <row>
  *         <entry>font</entry>
- *         <entry><programlisting>family [style] [size]</programlisting></entry>
+ *         <entry>@family [@style] [@size]</entry>
  *         <entry>#PangoFontDescription</entry>
- *         <entry><programlisting>font: Sans 15;</programlisting></entry>
+ *         <entry>font: Sans 15;</entry>
  *       </row>
  *       <row>
  *         <entry>margin</entry>
- *         <entry morerows="1">
- *           <programlisting>
- * width
- * vertical-width horizontal-width
- * top-width horizontal-width bottom-width
- * top-width right-width bottom-width left-width
- *           </programlisting>
+ *         <entry morerows="1"><literallayout>@width
+ * @vertical_width @horizontal_width
+ * @top_width @horizontal_width @bottom_width
+ * @top_width @right_width @bottom_width @left_width</literallayout>
  *         </entry>
  *         <entry morerows="1">#GtkBorder</entry>
- *         <entry morerows="1">
- *           <programlisting>
- * margin: 5;
+ *         <entry morerows="1"><literallayout>margin: 5;
  * margin: 5 10;
  * margin: 5 10 3;
- * margin: 5 10 3 5;</programlisting>
+ * margin: 5 10 3 5;</literallayout>
  *         </entry>
  *       </row>
  *       <row>
  *       </row>
  *       <row>
  *         <entry>background-image</entry>
- *         <entry>
- *           <programlisting>
- * -gtk-gradient (linear,
- *                starting-x-position starting-y-position,
- *                ending-x-position ending-y-position,
- *                [ [from|to] (color) |
- *                  color-stop (percentage, color) ] )
- *
- * -gtk-gradient (radial,
- *                starting-x-position starting-y-position, starting-radius,
- *                ending-x-position ending-y-position, ending-radius,
- *                [ [from|to] (color) |
- *                  color-stop (percentage, color) ]* )</programlisting>
- *         </entry>
+ *         <entry><literallayout>gradient (see above) or
+ * url(@path)</literallayout></entry>
  *         <entry>#cairo_pattern_t</entry>
- *         <entry>
- *           <programlisting>
- * -gtk-gradient (linear,
+ *         <entry><literallayout>-gtk-gradient (linear,
  *                left top, right top,
  *                from (&num;fff), to (&num;000));
  * -gtk-gradient (linear, 0.0 0.5, 0.5 1.0,
  *                center center, 0.2,
  *                center center, 0.8,
  *                color-stop (0.0, &num;fff),
- *                color-stop (1.0, &num;000));</programlisting>
+ *                color-stop (1.0, &num;000));
+ * url ('background.png');</literallayout>
  *         </entry>
  *       </row>
  *       <row>
+ *         <entry>border-width</entry>
+ *         <entry>integer</entry>
+ *         <entry>#gint</entry>
+ *         <entry>border-width: 5;</entry>
+ *       </row>
+ *       <row>
+ *         <entry>border-radius</entry>
+ *         <entry>integer</entry>
+ *         <entry>#gint</entry>
+ *         <entry>border-radius: 5;</entry>
+ *       </row>
+ *       <row>
+ *         <entry>border-style</entry>
+ *         <entry>[none|solid|inset|outset]</entry>
+ *         <entry>#GtkBorderStyle</entry>
+ *         <entry>border-style: solid;</entry>
+ *       </row>
+ *       <row>
  *         <entry>border-image</entry>
- *         <entry><programlisting>url([path]) top-distance right-distance bottom-distance left-distance horizontal-option vertical-option</programlisting></entry>
- *         <entry></entry>
- *         <entry>
- *           <programlisting>
- * border-image: url (/path/to/image.png) 3 4 3 4 stretch;
- * border-image: url (/path/to/image.png) 3 4 4 3 repeat stretch;</programlisting>
+ *         <entry><literallayout>border image (see above)</literallayout></entry>
+ *         <entry>internal use only</entry>
+ *         <entry><literallayout>border-image: url("/path/to/image.png") 3 4 3 4 stretch;
+ * border-image: url("/path/to/image.png") 3 4 4 3 repeat stretch;</literallayout>
  *         </entry>
  *       </row>
  *       <row>
  *         <entry>transition</entry>
- *         <entry><programlisting>duration [s|ms] [linear|ease|ease-in|ease-out|ease-in-out] [loop]?</programlisting></entry>
- *         <entry></entry>
- *         <entry>
- *           <programlisting>
- * transition: 150ms ease-in-out;
- * transition: 1s linear loop;</programlisting>
+ *         <entry>transition (see above)</entry>
+ *         <entry>internal use only</entry>
+ *         <entry><literallayout>transition: 150ms ease-in-out;
+ * transition: 1s linear loop;</literallayout>
  *         </entry>
  *       </row>
  *     </tbody>
  *   </tgroup>
  * </informaltable>
+ * <para>
+ * GtkThemingEngines can register their own, engine-specific style properties
+ * with the function gtk_theming_engine_register_property(). These properties
+ * can be set in CSS like other properties, using a name of the form
+ * <literallayout>-<replaceable>namespace</replaceable>-<replaceable>name</replaceable></literallayout>, where <replaceable>namespace</replaceable> is typically
+ * the name of the theming engine, and <replaceable>name</replaceable> is the
+ * name of the property. Style properties that have been registered by widgets
+ * using gtk_widget_class_install_style_property() can also be set in this
+ * way, using the widget class name for <replaceable>namespace</replaceable>.
+ * </para>
+ * <example>
+ * <title>Using engine-specific style properties</title>
+ * <programlisting>
+ * * {
+ *     engine: clearlooks;
+ *     border-radius: 4;
+ *     -GtkPaned-handle-size: 6;
+ *     -clearlooks-colorize-scrollbar: false;
+ * }
+ * </programlisting>
+ * </example>
  * </refsect2>
  */
 
@@ -437,6 +740,9 @@ struct GtkCssProviderPrivate
   GScanner *scanner;
   gchar *filename;
 
+  const gchar *buffer;
+  const gchar *value_pos;
+
   GHashTable *symbolic_colors;
 
   GPtrArray *selectors_info;
@@ -477,14 +783,20 @@ static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
 
 static void scanner_apply_scope (GScanner    *scanner,
                                  ParserScope  scope);
-static gboolean css_provider_parse_value (GtkCssProvider *css_provider,
-                                          const gchar    *value_str,
-                                          GValue         *value);
+static gboolean css_provider_parse_value (GtkCssProvider  *css_provider,
+                                          const gchar     *value_str,
+                                          GValue          *value,
+                                          GError         **error);
 static gboolean gtk_css_provider_load_from_path_internal (GtkCssProvider  *css_provider,
                                                           const gchar     *path,
                                                           gboolean         reset,
                                                           GError         **error);
 
+GQuark
+gtk_css_provider_error_quark (void)
+{
+  return g_quark_from_static_string ("gtk-css-provider-error-quark");
+}
 
 G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
@@ -686,6 +998,7 @@ create_scanner (void)
   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "insensitive", GUINT_TO_POINTER (GTK_STATE_INSENSITIVE));
   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "inconsistent", GUINT_TO_POINTER (GTK_STATE_INCONSISTENT));
   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "focused", GUINT_TO_POINTER (GTK_STATE_FOCUSED));
+  g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "focus", GUINT_TO_POINTER (GTK_STATE_FOCUSED));
 
   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "nth-child", GUINT_TO_POINTER (SYMBOL_NTH_CHILD));
   g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "first-child", GUINT_TO_POINTER (SYMBOL_FIRST_CHILD));
@@ -761,7 +1074,7 @@ compare_selector_element (GtkWidgetPath   *path,
     {
       GType type;
 
-      type = gtk_widget_path_iter_get_widget_type (path, index);
+      type = gtk_widget_path_iter_get_object_type (path, index);
 
       if (!g_type_is_a (type, elem->type))
         return FALSE;
@@ -838,7 +1151,7 @@ compare_selector (GtkWidgetPath *path,
                   SelectorPath  *selector)
 {
   GSList *elements = selector->elements;
-  gboolean match = TRUE;
+  gboolean match = TRUE, first = TRUE, first_match = FALSE;
   guint64 score = 0;
   gint i;
 
@@ -853,6 +1166,9 @@ compare_selector (GtkWidgetPath *path,
 
       match = compare_selector_element (path, i, elem, &elem_score);
 
+      if (match && first)
+        first_match = TRUE;
+
       /* Only move on to the next index if there is no match
        * with the current element (whether to continue or not
        * handled right after in the combinator check), or a
@@ -885,6 +1201,8 @@ compare_selector (GtkWidgetPath *path,
           score <<= 4;
           score |= elem_score;
         }
+
+      first = FALSE;
     }
 
   /* If there are pending selector
@@ -896,6 +1214,13 @@ compare_selector (GtkWidgetPath *path,
 
   if (!match)
     score = 0;
+  else if (first_match)
+    {
+      /* Assign more weight to these selectors
+       * that matched right from the first element.
+       */
+      score <<= 4;
+    }
 
   return score;
 }
@@ -1031,20 +1356,18 @@ gtk_css_provider_get_style (GtkStyleProvider *provider,
 static gboolean
 gtk_css_provider_get_style_property (GtkStyleProvider *provider,
                                      GtkWidgetPath    *path,
-                                     const gchar      *property_name,
+                                     GtkStateFlags     state,
+                                     GParamSpec       *pspec,
                                      GValue           *value)
 {
   GArray *priority_info;
   gboolean found = FALSE;
   gchar *prop_name;
-  GType path_type;
   gint i;
 
-  path_type = gtk_widget_path_get_widget_type (path);
-
   prop_name = g_strdup_printf ("-%s-%s",
-                               g_type_name (path_type),
-                               property_name);
+                               g_type_name (pspec->owner_type),
+                               pspec->name);
 
   priority_info = css_provider_get_selectors (GTK_CSS_PROVIDER (provider), path);
 
@@ -1056,14 +1379,18 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider,
       info = &g_array_index (priority_info, StylePriorityInfo, i);
       val = g_hash_table_lookup (info->style, prop_name);
 
-      if (val)
+      if (val &&
+          (info->state == 0 ||
+           info->state == state ||
+           ((info->state & state) != 0 &&
+            (info->state & ~(state)) == 0)))
         {
           const gchar *val_str;
 
           val_str = g_value_get_string (val);
           found = TRUE;
 
-          css_provider_parse_value (GTK_CSS_PROVIDER (provider), val_str, value);
+          css_provider_parse_value (GTK_CSS_PROVIDER (provider), val_str, value, NULL);
           break;
         }
     }
@@ -1098,8 +1425,10 @@ gtk_css_provider_finalize (GObject *object)
   g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL);
   g_slist_free (priv->cur_selectors);
 
-  g_hash_table_unref (priv->cur_properties);
-  g_hash_table_destroy (priv->symbolic_colors);
+  if (priv->cur_properties)
+    g_hash_table_unref (priv->cur_properties);
+  if (priv->symbolic_colors)
+    g_hash_table_destroy (priv->symbolic_colors);
 
   G_OBJECT_CLASS (gtk_css_provider_parent_class)->finalize (object);
 }
@@ -1134,22 +1463,22 @@ scanner_apply_scope (GScanner    *scanner,
 
   if (scope == SCOPE_VALUE)
     {
-      scanner->config->cset_identifier_first = G_CSET_a_2_z "@#-_0123456789" G_CSET_A_2_Z;
-      scanner->config->cset_identifier_nth = G_CSET_a_2_z "@#-_ 0123456789(),.%\t\n" G_CSET_A_2_Z;
+      scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "@#-_";
+      scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "@#-_ +(),.%\t\n'/\"";
       scanner->config->scan_identifier_1char = TRUE;
     }
   else if (scope == SCOPE_SELECTOR)
     {
       scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "*@";
-      scanner->config->cset_identifier_nth = G_CSET_a_2_z "-_#." G_CSET_A_2_Z;
+      scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_#.";
       scanner->config->scan_identifier_1char = TRUE;
     }
   else if (scope == SCOPE_PSEUDO_CLASS ||
            scope == SCOPE_NTH_CHILD ||
            scope == SCOPE_DECLARATION)
     {
-      scanner->config->cset_identifier_first = G_CSET_a_2_z "-" G_CSET_A_2_Z;
-      scanner->config->cset_identifier_nth = G_CSET_a_2_z "-" G_CSET_A_2_Z;
+      scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "-_";
+      scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_";
       scanner->config->scan_identifier_1char = FALSE;
     }
   else
@@ -1208,6 +1537,8 @@ css_provider_reset_parser (GtkCssProvider *css_provider)
   priv->state = NULL;
 
   scanner_apply_scope (priv->scanner, SCOPE_SELECTOR);
+  priv->scanner->user_data = NULL;
+  priv->value_pos = NULL;
 
   g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL);
   g_slist_free (priv->cur_selectors);
@@ -1553,10 +1884,11 @@ parse_selector (GtkCssProvider  *css_provider,
 }
 
 #define SKIP_SPACES(s) while (s[0] == ' ' || s[0] == '\t' || s[0] == '\n') s++;
+#define SKIP_SPACES_BACK(s) while (s[0] == ' ' || s[0] == '\t' || s[0] == '\n') s--;
 
 static GtkSymbolicColor *
 symbolic_color_parse_str (const gchar  *string,
-                         gchar       **end_ptr)
+                          gchar       **end_ptr)
 {
   GtkSymbolicColor *symbolic_color = NULL;
   gchar *str;
@@ -1572,8 +1904,7 @@ symbolic_color_parse_str (const gchar  *string,
       str++;
       end = str;
 
-      while (*end == '-' || *end == '_' ||
-             g_ascii_isalpha (*end))
+      while (*end == '-' || *end == '_' || g_ascii_isalpha (*end))
         end++;
 
       name = g_strndup (str, end - str);
@@ -1750,7 +2081,7 @@ symbolic_color_parse_str (const gchar  *string,
       *end_ptr = (gchar *) str;
 
       if (str[0] != ')')
-       {
+        {
           gtk_symbolic_color_unref (color1);
           gtk_symbolic_color_unref (color2);
           return NULL;
@@ -1786,8 +2117,9 @@ symbolic_color_parse_str (const gchar  *string,
         }
       else
         {
-          /* color name? parse until first whitespace */
-          while (*end != ' ' && *end != '\0')
+          /* Color name */
+          while (*end != '\0' &&
+                 (g_ascii_isalnum (*end) || *end == ' '))
             end++;
         }
 
@@ -1808,7 +2140,8 @@ symbolic_color_parse_str (const gchar  *string,
 }
 
 static GtkSymbolicColor *
-symbolic_color_parse (const gchar *str)
+symbolic_color_parse (const gchar  *str,
+                      GError      **error)
 {
   GtkSymbolicColor *color;
   gchar *end;
@@ -1817,8 +2150,10 @@ symbolic_color_parse (const gchar *str)
 
   if (*end != '\0')
     {
-      g_warning ("Error parsing symbolic color \"%s\", stopped at char %ld : '%c'",
-                 str, end - str, *end);
+      g_set_error_literal (error,
+                           GTK_CSS_PROVIDER_ERROR,
+                           GTK_CSS_PROVIDER_ERROR_FAILED,
+                           "Could not parse symbolic color");
 
       if (color)
         {
@@ -1904,6 +2239,13 @@ gradient_parse_str (const gchar  *str,
           else
             {
               coords[i * 3] = g_ascii_strtod (str, &end);
+
+              if (str == end)
+                {
+                  *end_ptr = (gchar *) str;
+                  return NULL;
+                }
+
               str = end;
             }
 
@@ -1927,6 +2269,13 @@ gradient_parse_str (const gchar  *str,
           else
             {
               coords[(i * 3) + 1] = g_ascii_strtod (str, &end);
+
+              if (str == end)
+                {
+                  *end_ptr = (gchar *) str;
+                  return NULL;
+                }
+
               str = end;
             }
 
@@ -2009,7 +2358,7 @@ gradient_parse_str (const gchar  *str,
               str++;
               SKIP_SPACES (str);
 
-              position = g_strtod (str, &end);
+              position = g_ascii_strtod (str, &end);
 
               str = end;
               SKIP_SPACES (str);
@@ -2064,67 +2413,71 @@ gradient_parse_str (const gchar  *str,
   return gradient;
 }
 
-static GtkGradient *
-gradient_parse (const gchar *str)
+static gchar *
+path_parse_str (GtkCssProvider  *css_provider,
+                const gchar     *str,
+                gchar          **end_ptr,
+                GError         **error)
 {
-  GtkGradient *gradient;
-  gchar *end;
-
-  gradient = gradient_parse_str (str, &end);
+  gchar *path, *chr;
+  const gchar *start, *end;
+  start = str;
 
-  if (*end != '\0')
+  if (g_str_has_prefix (str, "url"))
     {
-      g_warning ("Error parsing pattern \"%s\", stopped at char %ld : '%c'",
-                 str, end - str, *end);
+      str += strlen ("url");
+      SKIP_SPACES (str);
 
-      if (gradient)
+      if (*str != '(')
         {
-          gtk_gradient_unref (gradient);
-          gradient = NULL;
+          *end_ptr = (gchar *) str;
+          return NULL;
         }
-    }
 
-  return gradient;
-}
+      chr = strchr (str, ')');
+      if (!chr)
+        {
+          *end_ptr = (gchar *) str;
+          return NULL;
+        }
 
-static gchar *
-path_parse_str (GtkCssProvider  *css_provider,
-               const gchar     *str,
-               gchar          **end_ptr)
-{
-  gchar *path, *chr;
+      end = chr + 1;
 
-  if (!g_str_has_prefix (str, "url"))
-    {
-      *end_ptr = (gchar *) str;
-      return NULL;
-    }
+      str++;
+      SKIP_SPACES (str);
 
-  str += strlen ("url");
-  SKIP_SPACES (str);
+      if (*str == '"' || *str == '\'')
+        {
+          const gchar *p;
+          p = str;
+          str++;
 
-  if (*str != '(')
-    {
-      *end_ptr = (gchar *) str;
-      return NULL;
-    }
+          chr--;
+          SKIP_SPACES_BACK (chr);
+
+          if (*chr != *p || chr == p)
+            {
+              *end_ptr = (gchar *)str;
+              return NULL;
+            }
+        }
+      else
+        {
+          *end_ptr = (gchar *)str;
+          return NULL;
+        }
 
-  chr = strchr (str, ')');
+      path = g_strndup (str, chr - str);
+      g_strstrip (path);
 
-  if (!chr)
+      *end_ptr = (gchar *)end;
+    }
+  else
     {
-      *end_ptr = (gchar *) str;
-      return NULL;
+      path = g_strdup (str);
+      *end_ptr = (gchar *)str + strlen (str);
     }
 
-  str++;
-  SKIP_SPACES (str);
-
-  path = g_strndup (str, chr - str);
-  g_strstrip (path);
-
-  *end_ptr = chr + 1;
-
   /* Always return an absolute path */
   if (!g_path_is_absolute (path))
     {
@@ -2134,7 +2487,10 @@ path_parse_str (GtkCssProvider  *css_provider,
       priv = css_provider->priv;
 
       /* Use relative path to the current CSS file path, if any */
-      dirname = g_path_get_dirname (priv->filename);
+      if (priv->filename)
+        dirname = g_path_get_dirname (priv->filename);
+      else
+        dirname = g_get_current_dir ();
 
       full_path = g_build_filename (dirname, path, NULL);
       g_free (path);
@@ -2145,32 +2501,37 @@ path_parse_str (GtkCssProvider  *css_provider,
 
   if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
     {
-      g_warning ("File doesn't exist: %s\n", path);
+      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_EXIST,
+                   "File doesn't exist: %s", path);
       g_free (path);
       path = NULL;
+      *end_ptr = (gchar *)start;
     }
 
   return path;
 }
 
 static gchar *
-path_parse (GtkCssProvider *css_provider,
-            const gchar    *str)
+path_parse (GtkCssProvider  *css_provider,
+            const gchar     *str,
+            GError         **error)
 {
-  gchar *path, *end;
+  gchar *path;
+  gchar *end;
+
+  path = path_parse_str (css_provider, str, &end, error);
 
-  path = path_parse_str (css_provider, str, &end);
+  if (!path)
+    return NULL;
 
   if (*end != '\0')
     {
-      g_warning ("Error parsing file path \"%s\", stopped at char %ld : '%c'",
-                 str, end - str, *end);
-
-      if (path)
-        {
-          g_free (path);
-          path = NULL;
-        }
+      g_set_error_literal (error,
+                           GTK_CSS_PROVIDER_ERROR,
+                           GTK_CSS_PROVIDER_ERROR_FAILED,
+                           "Error parsing path");
+      g_free (path);
+      path = NULL;
     }
 
   return path;
@@ -2179,12 +2540,12 @@ path_parse (GtkCssProvider *css_provider,
 static Gtk9Slice *
 slice_parse_str (GtkCssProvider  *css_provider,
                  const gchar     *str,
-                 gchar          **end_ptr)
+                 gchar          **end_ptr,
+                 GError         **error)
 {
   gdouble distance_top, distance_bottom;
   gdouble distance_left, distance_right;
   GtkSliceSideModifier mods[2];
-  GError *error = NULL;
   GdkPixbuf *pixbuf;
   Gtk9Slice *slice;
   gchar *path;
@@ -2193,7 +2554,7 @@ slice_parse_str (GtkCssProvider  *css_provider,
   SKIP_SPACES (str);
 
   /* Parse image url */
-  path = path_parse_str (css_provider, str, end_ptr);
+  path = path_parse_str (css_provider, str, end_ptr, error);
 
   if (!path)
       return NULL;
@@ -2202,22 +2563,22 @@ slice_parse_str (GtkCssProvider  *css_provider,
   SKIP_SPACES (str);
 
   /* Parse top/left/bottom/right distances */
-  distance_top = g_strtod (str, end_ptr);
+  distance_top = g_ascii_strtod (str, end_ptr);
 
   str = *end_ptr;
   SKIP_SPACES (str);
 
-  distance_right = g_strtod (str, end_ptr);
+  distance_right = g_ascii_strtod (str, end_ptr);
 
   str = *end_ptr;
   SKIP_SPACES (str);
 
-  distance_bottom = g_strtod (str, end_ptr);
+  distance_bottom = g_ascii_strtod (str, end_ptr);
 
   str = *end_ptr;
   SKIP_SPACES (str);
 
-  distance_left = g_strtod (str, end_ptr);
+  distance_left = g_ascii_strtod (str, end_ptr);
 
   str = *end_ptr;
   SKIP_SPACES (str);
@@ -2259,50 +2620,24 @@ slice_parse_str (GtkCssProvider  *css_provider,
       mods[1] = mods[0];
     }
 
-  pixbuf = gdk_pixbuf_new_from_file (path, &error);
+  pixbuf = gdk_pixbuf_new_from_file (path, error);
   g_free (path);
 
-  if (error)
+  if (!pixbuf)
     {
-      g_warning ("Pixbuf could not be loaded: %s\n", error->message);
-      g_error_free (error);
       *end_ptr = (gchar *) str;
       return NULL;
     }
 
-  slice = gtk_9slice_new (pixbuf,
-                          distance_top, distance_bottom,
-                          distance_left, distance_right,
-                          mods[0], mods[1]);
+  slice = _gtk_9slice_new (pixbuf,
+                           distance_top, distance_bottom,
+                           distance_left, distance_right,
+                           mods[0], mods[1]);
   g_object_unref (pixbuf);
 
   return slice;
 }
 
-static Gtk9Slice *
-slice_parse (GtkCssProvider *css_provider,
-             const gchar    *str)
-{
-  Gtk9Slice *slice;
-  gchar *end;
-
-  slice = slice_parse_str (css_provider, str, &end);
-
-  if (*end != '\0')
-    {
-      g_warning ("Error parsing sliced image \"%s\", stopped at char %ld : '%c'",
-                 str, end - str, *end);
-
-      if (slice)
-        {
-          gtk_9slice_unref (slice);
-          slice = NULL;
-        }
-    }
-
-  return slice;
-}
-
 static gdouble
 unit_parse_str (const gchar     *str,
                 gchar          **end_str)
@@ -2310,7 +2645,7 @@ unit_parse_str (const gchar     *str,
   gdouble unit;
 
   SKIP_SPACES (str);
-  unit = g_strtod (str, end_str);
+  unit = g_ascii_strtod (str, end_str);
   str = *end_str;
 
   /* Now parse the unit type, if any. We
@@ -2395,62 +2730,46 @@ border_parse_str (const gchar  *str,
   return border;
 }
 
-static GtkBorder *
-border_parse (const gchar *str)
-{
-  GtkBorder *border;
-  gchar *end;
-
-  border = border_parse_str (str, &end);
-
-  if (*end != '\0')
-    {
-      g_warning ("Error parsing border \"%s\", stopped at char %ld : '%c'",
-                 str, end - str, *end);
-
-      if (border)
-        gtk_border_free (border);
-
-      return NULL;
-    }
-
-  return border;
-}
-
 static gboolean
-css_provider_parse_value (GtkCssProvider *css_provider,
-                          const gchar    *value_str,
-                          GValue         *value)
+css_provider_parse_value (GtkCssProvider  *css_provider,
+                          const gchar     *value_str,
+                          GValue          *value,
+                          GError         **error)
 {
+  GtkCssProviderPrivate *priv;
   GType type;
   gboolean parsed = TRUE;
+  gchar *end = NULL;
 
+  priv = css_provider->priv;
   type = G_VALUE_TYPE (value);
 
   if (type == GDK_TYPE_RGBA ||
       type == GDK_TYPE_COLOR)
     {
-      GdkRGBA color;
-      GdkColor rgb;
+      GdkRGBA rgba;
+      GdkColor color;
 
       if (type == GDK_TYPE_RGBA &&
-          gdk_rgba_parse (&color, value_str))
-        g_value_set_boxed (value, &color);
+          gdk_rgba_parse (&rgba, value_str))
+        g_value_set_boxed (value, &rgba);
       else if (type == GDK_TYPE_COLOR &&
-               gdk_color_parse (value_str, &rgb))
-        g_value_set_boxed (value, &rgb);
+               gdk_color_parse (value_str, &color))
+        g_value_set_boxed (value, &color);
       else
         {
           GtkSymbolicColor *symbolic_color;
 
-          symbolic_color = symbolic_color_parse (value_str);
-
-          if (!symbolic_color)
-            return FALSE;
+          symbolic_color = symbolic_color_parse_str (value_str, &end);
 
-          g_value_unset (value);
-          g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
-          g_value_take_boxed (value, symbolic_color);
+          if (symbolic_color)
+            {
+              g_value_unset (value);
+              g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
+              g_value_take_boxed (value, symbolic_color);
+            }
+          else
+            parsed = FALSE;
         }
     }
   else if (type == PANGO_TYPE_FONT_DESCRIPTION)
@@ -2470,20 +2789,27 @@ css_provider_parse_value (GtkCssProvider *css_provider,
     }
   else if (type == G_TYPE_INT)
     g_value_set_int (value, atoi (value_str));
+  else if (type == G_TYPE_UINT)
+    g_value_set_uint (value, (guint) atoi (value_str));
   else if (type == G_TYPE_DOUBLE)
     g_value_set_double (value, g_ascii_strtod (value_str, NULL));
+  else if (type == G_TYPE_FLOAT)
+    g_value_set_float (value, (gfloat) g_ascii_strtod (value_str, NULL));
   else if (type == GTK_TYPE_THEMING_ENGINE)
     {
       GtkThemingEngine *engine;
 
       engine = gtk_theming_engine_load (value_str);
-      g_value_set_object (value, engine);
+      if (engine)
+        g_value_set_object (value, engine);
+      else
+        parsed = FALSE;
     }
   else if (type == GTK_TYPE_ANIMATION_DESCRIPTION)
     {
       GtkAnimationDescription *desc;
 
-      desc = gtk_animation_description_from_string (value_str);
+      desc = _gtk_animation_description_from_string (value_str);
 
       if (desc)
         g_value_take_boxed (value, desc);
@@ -2494,14 +2820,14 @@ css_provider_parse_value (GtkCssProvider *css_provider,
     {
       GtkBorder *border;
 
-      border = border_parse (value_str);
+      border = border_parse_str (value_str, &end);
       g_value_take_boxed (value, border);
     }
   else if (type == CAIRO_GOBJECT_TYPE_PATTERN)
     {
       GtkGradient *gradient;
 
-      gradient = gradient_parse (value_str);
+      gradient = gradient_parse_str (value_str, &end);
 
       if (gradient)
         {
@@ -2510,7 +2836,50 @@ css_provider_parse_value (GtkCssProvider *css_provider,
           g_value_take_boxed (value, gradient);
         }
       else
-        parsed = FALSE;
+        {
+          gchar *path;
+          GdkPixbuf *pixbuf;
+
+          g_clear_error (error);
+          path = path_parse_str (css_provider, value_str, &end, error);
+
+          if (path)
+            {
+              pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+              g_free (path);
+
+              if (pixbuf)
+                {
+                  cairo_surface_t *surface;
+                  cairo_pattern_t *pattern;
+                  cairo_t *cr;
+                  cairo_matrix_t matrix;
+
+                  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                                        gdk_pixbuf_get_width (pixbuf),
+                                                        gdk_pixbuf_get_height (pixbuf));
+                  cr = cairo_create (surface);
+                  gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+                  cairo_paint (cr);
+                  pattern = cairo_pattern_create_for_surface (surface);
+
+                  cairo_matrix_init_scale (&matrix,
+                                           gdk_pixbuf_get_width (pixbuf),
+                                           gdk_pixbuf_get_height (pixbuf));
+                  cairo_pattern_set_matrix (pattern, &matrix);
+
+                  cairo_surface_destroy (surface);
+                  cairo_destroy (cr);
+                  g_object_unref (pixbuf);
+
+                  g_value_take_boxed (value, pattern);
+                }
+              else
+                parsed = FALSE;
+            }
+          else
+            parsed = FALSE;
+        }
     }
   else if (G_TYPE_IS_ENUM (type))
     {
@@ -2522,8 +2891,11 @@ css_provider_parse_value (GtkCssProvider *css_provider,
 
       if (!enum_value)
         {
-          g_warning ("Unknown value '%s' for enum type '%s'",
-                     value_str, g_type_name (type));
+          g_set_error (error,
+                       GTK_CSS_PROVIDER_ERROR,
+                       GTK_CSS_PROVIDER_ERROR_FAILED,
+                       "Unknown value '%s' for enum type '%s'",
+                       value_str, g_type_name (type));
           parsed = FALSE;
         }
       else
@@ -2556,8 +2928,11 @@ css_provider_parse_value (GtkCssProvider *css_provider,
 
           if (!flag_value)
             {
-              g_warning ("Unknown flag '%s' for type '%s'",
-                         value_str, g_type_name (type));
+              g_set_error (error,
+                           GTK_CSS_PROVIDER_ERROR,
+                           GTK_CSS_PROVIDER_ERROR_FAILED,
+                           "Unknown flag '%s' for type '%s'",
+                           value_str, g_type_name (type));
               parsed = FALSE;
             }
           else
@@ -2572,8 +2947,11 @@ css_provider_parse_value (GtkCssProvider *css_provider,
 
       if (!flag_value)
         {
-          g_warning ("Unknown flag '%s' for type '%s'",
-                     value_str, g_type_name (type));
+          g_set_error (error,
+                       GTK_CSS_PROVIDER_ERROR,
+                       GTK_CSS_PROVIDER_ERROR_FAILED,
+                       "Unknown flag '%s' for type '%s'",
+                       value_str, g_type_name (type));
           parsed = FALSE;
         }
       else
@@ -2588,7 +2966,7 @@ css_provider_parse_value (GtkCssProvider *css_provider,
     {
       Gtk9Slice *slice;
 
-      slice = slice_parse (css_provider, value_str);
+      slice = slice_parse_str (css_provider, value_str, &end, error);
 
       if (slice)
         g_value_take_boxed (value, slice);
@@ -2597,16 +2975,111 @@ css_provider_parse_value (GtkCssProvider *css_provider,
     }
   else
     {
-      g_warning ("Cannot parse string '%s' for type %s", value_str, g_type_name (type));
+      g_set_error (error,
+                   GTK_CSS_PROVIDER_ERROR,
+                   GTK_CSS_PROVIDER_ERROR_FAILED,
+                   "Cannot parse string '%s' for type %s",
+                   value_str, g_type_name (type));
       parsed = FALSE;
     }
 
+  if (end && *end)
+    {
+      /* Set error position in the scanner
+       * according to what we've parsed so far
+       */
+      priv->value_pos += (end - value_str);
+
+      if (error && !*error)
+        g_set_error_literal (error,
+                             GTK_CSS_PROVIDER_ERROR,
+                             GTK_CSS_PROVIDER_ERROR_FAILED,
+                             "Failed to parse value");
+    }
+
   return parsed;
 }
 
+static void
+scanner_report_warning (GtkCssProvider *css_provider,
+                        GTokenType      expected_token,
+                        GError         *error)
+{
+  GtkCssProviderPrivate *priv;
+  const gchar *line_end, *line_start;
+  const gchar *expected_str;
+  gchar buf[2], *line, *str;
+  guint pos;
+
+  priv = css_provider->priv;
+
+  if (error)
+    str = g_strdup (error->message);
+  else
+    {
+      if (priv->scanner->user_data)
+        expected_str = priv->scanner->user_data;
+      else
+        {
+          switch (expected_token)
+            {
+            case G_TOKEN_SYMBOL:
+              expected_str = "Symbol";
+            case G_TOKEN_IDENTIFIER:
+              expected_str = "Identifier";
+            default:
+              buf[0] = expected_token;
+              buf[1] = '\0';
+              expected_str = buf;
+            }
+        }
+
+      str = g_strdup_printf ("Parse error, expecting a %s '%s'",
+                             (expected_str != buf) ? "valid" : "",
+                             expected_str);
+    }
+
+  if (priv->value_pos)
+    line_start = priv->value_pos - 1;
+  else
+    line_start = priv->scanner->text - 1;
+
+  while (*line_start != '\n' &&
+         line_start != priv->buffer)
+    line_start--;
+
+  if (*line_start == '\n')
+    line_start++;
+
+  if (priv->value_pos)
+    pos = priv->value_pos - line_start + 1;
+  else
+    pos = priv->scanner->text - line_start - 1;
+
+  line_end = strchr (line_start, '\n');
+
+  if (line_end)
+    line = g_strndup (line_start, (line_end - line_start));
+  else
+    line = g_strdup (line_start);
+
+  g_message ("CSS: %s\n"
+             "%s, line %d, char %d:\n"
+             "%*c %s\n"
+             "%*c ^",
+             str, priv->scanner->input_name,
+             priv->scanner->line, priv->scanner->position,
+             3, ' ', line,
+             3 + pos, ' ');
+
+  g_free (line);
+  g_free (str);
+}
+
 static GTokenType
-parse_rule (GtkCssProvider *css_provider,
-            GScanner       *scanner)
+parse_rule (GtkCssProvider  *css_provider,
+            GScanner        *scanner,
+            GError         **error)
 {
   GtkCssProviderPrivate *priv;
   GTokenType expected_token;
@@ -2633,20 +3106,29 @@ parse_rule (GtkCssProvider *css_provider,
           g_scanner_get_next_token (scanner);
 
           if (scanner->token != G_TOKEN_IDENTIFIER)
-            return G_TOKEN_IDENTIFIER;
+            {
+              scanner->user_data = "Color name";
+              return G_TOKEN_IDENTIFIER;
+            }
 
           color_name = g_strdup (scanner->value.v_identifier);
           css_provider_push_scope (css_provider, SCOPE_VALUE);
           g_scanner_get_next_token (scanner);
 
           if (scanner->token != G_TOKEN_IDENTIFIER)
-            return G_TOKEN_IDENTIFIER;
+            {
+              scanner->user_data = "Color definition";
+              return G_TOKEN_IDENTIFIER;
+            }
 
           color_str = g_strstrip (scanner->value.v_identifier);
-          color = symbolic_color_parse (color_str);
+          color = symbolic_color_parse (color_str, error);
 
           if (!color)
-            return G_TOKEN_IDENTIFIER;
+            {
+              scanner->user_data = "Color definition";
+              return G_TOKEN_IDENTIFIER;
+            }
 
           g_hash_table_insert (priv->symbolic_colors, color_name, color);
 
@@ -2662,21 +3144,27 @@ parse_rule (GtkCssProvider *css_provider,
         {
           GScanner *scanner_backup;
           GSList *state_backup;
-          GError *error = NULL;
           gboolean loaded;
-          gchar *path;
+          gchar *path = NULL;
 
           css_provider_push_scope (css_provider, SCOPE_VALUE);
           g_scanner_get_next_token (scanner);
 
-          if (scanner->token != G_TOKEN_IDENTIFIER)
-            return G_TOKEN_IDENTIFIER;
-
-          path = path_parse (css_provider,
-                             g_strstrip (scanner->value.v_identifier));
-
-          if (!path)
-            return G_TOKEN_IDENTIFIER;
+          if (scanner->token == G_TOKEN_IDENTIFIER &&
+              g_str_has_prefix (scanner->value.v_identifier, "url"))
+            path = path_parse (css_provider,
+                               g_strstrip (scanner->value.v_identifier),
+                               error);
+          else if (scanner->token == G_TOKEN_STRING)
+            path = path_parse (css_provider,
+                               g_strstrip (scanner->value.v_string),
+                               error);
+
+          if (path == NULL)
+            {
+              scanner->user_data = "File URL";
+              return G_TOKEN_IDENTIFIER;
+            }
 
           css_provider_pop_scope (css_provider);
           g_scanner_get_next_token (scanner);
@@ -2696,7 +3184,7 @@ parse_rule (GtkCssProvider *css_provider,
 
           /* FIXME: Avoid recursive importing */
           loaded = gtk_css_provider_load_from_path_internal (css_provider, path,
-                                                             FALSE, &error);
+                                                             FALSE, error);
 
           /* Restore previous state */
           css_provider_reset_parser (css_provider);
@@ -2708,16 +3196,17 @@ parse_rule (GtkCssProvider *css_provider,
 
           if (!loaded)
             {
-              g_warning ("Error loading imported file \"%s\": %s",
-                         path, (error) ? error->message : "");
-              g_error_free (error);
+              scanner->user_data = "File URL";
               return G_TOKEN_IDENTIFIER;
             }
           else
             return G_TOKEN_NONE;
         }
       else
-        return G_TOKEN_IDENTIFIER;
+        {
+          scanner->user_data = "Directive";
+          return G_TOKEN_IDENTIFIER;
+        }
     }
 
   expected_token = parse_selector (css_provider, scanner, &selector);
@@ -2725,6 +3214,7 @@ parse_rule (GtkCssProvider *css_provider,
   if (expected_token != G_TOKEN_NONE)
     {
       selector_path_unref (selector);
+      scanner->user_data = "Selector";
       return expected_token;
     }
 
@@ -2739,6 +3229,7 @@ parse_rule (GtkCssProvider *css_provider,
       if (expected_token != G_TOKEN_NONE)
         {
           selector_path_unref (selector);
+          scanner->user_data = "Selector";
           return expected_token;
         }
 
@@ -2756,10 +3247,9 @@ parse_rule (GtkCssProvider *css_provider,
 
   while (scanner->token == G_TOKEN_IDENTIFIER)
     {
-      const gchar *value_str = NULL;
+      gchar *value_str = NULL;
       GtkStylePropertyParser parse_func = NULL;
       GParamSpec *pspec;
-      GError *error = NULL;
       gchar *prop;
 
       prop = g_strdup (scanner->value.v_identifier);
@@ -2771,16 +3261,21 @@ parse_rule (GtkCssProvider *css_provider,
           return ':';
         }
 
+      priv->value_pos = priv->scanner->text;
+
       css_provider_push_scope (css_provider, SCOPE_VALUE);
       g_scanner_get_next_token (scanner);
 
       if (scanner->token != G_TOKEN_IDENTIFIER)
         {
           g_free (prop);
+          scanner->user_data = "Property value";
           return G_TOKEN_IDENTIFIER;
         }
 
-      value_str = g_strstrip (scanner->value.v_identifier);
+      value_str = scanner->value.v_identifier;
+      SKIP_SPACES (value_str);
+      g_strchomp (value_str);
 
       if (gtk_style_properties_lookup_property (prop, &parse_func, &pspec))
         {
@@ -2791,29 +3286,27 @@ parse_rule (GtkCssProvider *css_provider,
 
           if (strcmp (value_str, "none") == 0)
             {
-              /* Remove/unset the current value */
-              g_hash_table_remove (priv->cur_properties, prop);
+              /* Insert the default value, so it has an opportunity
+               * to override other style providers when merged
+               */
+              g_param_value_set_default (pspec, val);
+              g_hash_table_insert (priv->cur_properties, prop, val);
             }
           else if (pspec->value_type == G_TYPE_STRING)
             {
               g_value_set_string (val, value_str);
               g_hash_table_insert (priv->cur_properties, prop, val);
             }
-          else if ((parse_func && (parse_func) (value_str, val, &error)) ||
-                   (!parse_func && css_provider_parse_value (css_provider, value_str, val)))
+          else if ((parse_func && (parse_func) (value_str, val, error)) ||
+                   (!parse_func && css_provider_parse_value (css_provider, value_str, val, error)))
             g_hash_table_insert (priv->cur_properties, prop, val);
           else
             {
-              if (error)
-                {
-                  g_warning ("Error parsing property value: %s\n", error->message);
-                  g_error_free (error);
-                }
-
               g_value_unset (val);
               g_slice_free (GValue, val);
               g_free (prop);
 
+              scanner->user_data = "Property value";
               return G_TOKEN_IDENTIFIER;
             }
         }
@@ -2835,7 +3328,7 @@ parse_rule (GtkCssProvider *css_provider,
       g_scanner_get_next_token (scanner);
 
       if (scanner->token != ';')
-        return ';';
+        break;
 
       g_scanner_get_next_token (scanner);
     }
@@ -2849,9 +3342,13 @@ parse_rule (GtkCssProvider *css_provider,
 }
 
 static gboolean
-parse_stylesheet (GtkCssProvider *css_provider)
+parse_stylesheet (GtkCssProvider  *css_provider,
+                  GError         **error)
 {
   GtkCssProviderPrivate *priv;
+  gboolean result;
+
+  result = TRUE;
 
   priv = css_provider->priv;
   g_scanner_get_next_token (priv->scanner);
@@ -2859,15 +3356,33 @@ parse_stylesheet (GtkCssProvider *css_provider)
   while (!g_scanner_eof (priv->scanner))
     {
       GTokenType expected_token;
+      GError *err = NULL;
 
       css_provider_reset_parser (css_provider);
-      expected_token = parse_rule (css_provider, priv->scanner);
+      expected_token = parse_rule (css_provider, priv->scanner, &err);
 
       if (expected_token != G_TOKEN_NONE)
         {
-          g_scanner_unexp_token (priv->scanner, expected_token,
-                                 NULL, NULL, NULL,
-                                 "Error parsing style resource", FALSE);
+          /* If a GError was passed in, propagate the error and bail out,
+           * else report a warning and keep going
+           */
+          if (error != NULL)
+            {
+              result = FALSE;
+              if (err)
+                g_propagate_error (error, err);
+              else
+                g_set_error_literal (error,
+                                     GTK_CSS_PROVIDER_ERROR,
+                                     GTK_CSS_PROVIDER_ERROR_FAILED,
+                                     "Error parsing stylesheet");
+              break;
+            }
+          else
+            {
+              scanner_report_warning (css_provider, expected_token, err);
+              g_clear_error (&err);
+            }
 
           while (!g_scanner_eof (priv->scanner) &&
                  priv->scanner->token != G_TOKEN_RIGHT_CURLY)
@@ -2879,7 +3394,7 @@ parse_stylesheet (GtkCssProvider *css_provider)
       g_scanner_get_next_token (priv->scanner);
     }
 
-  return TRUE;
+  return result;
 }
 
 /**
@@ -2895,10 +3410,10 @@ parse_stylesheet (GtkCssProvider *css_provider)
  * Returns: %TRUE if the data could be loaded.
  **/
 gboolean
-gtk_css_provider_load_from_data (GtkCssProvider *css_provider,
-                                 const gchar    *data,
-                                 gssize          length,
-                                 GError         *error)
+gtk_css_provider_load_from_data (GtkCssProvider  *css_provider,
+                                 const gchar     *data,
+                                 gssize           length,
+                                 GError         **error)
 {
   GtkCssProviderPrivate *priv;
 
@@ -2914,14 +3429,14 @@ gtk_css_provider_load_from_data (GtkCssProvider *css_provider,
     g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
 
   priv->scanner->input_name = "-";
+  priv->buffer = data;
   g_scanner_input_text (priv->scanner, data, (guint) length);
 
   g_free (priv->filename);
   priv->filename = NULL;
+  priv->buffer = NULL;
 
-  parse_stylesheet (css_provider);
-
-  return TRUE;
+  return parse_stylesheet (css_provider, error);
 }
 
 /**
@@ -2944,6 +3459,7 @@ gtk_css_provider_load_from_file (GtkCssProvider  *css_provider,
   GError *internal_error = NULL;
   gchar *data;
   gsize length;
+  gboolean ret;
 
   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
   g_return_val_if_fail (G_IS_FILE (file), FALSE);
@@ -2965,13 +3481,15 @@ gtk_css_provider_load_from_file (GtkCssProvider  *css_provider,
   priv->filename = g_file_get_path (file);
 
   priv->scanner->input_name = priv->filename;
+  priv->buffer = data;
   g_scanner_input_text (priv->scanner, data, (guint) length);
 
-  parse_stylesheet (css_provider);
+  ret = parse_stylesheet (css_provider, error);
 
+  priv->buffer = NULL;
   g_free (data);
 
-  return TRUE;
+  return ret;
 }
 
 static gboolean
@@ -2985,6 +3503,7 @@ gtk_css_provider_load_from_path_internal (GtkCssProvider  *css_provider,
   GMappedFile *mapped_file;
   const gchar *data;
   gsize length;
+  gboolean ret;
 
   priv = css_provider->priv;
 
@@ -2999,9 +3518,8 @@ gtk_css_provider_load_from_path_internal (GtkCssProvider  *css_provider,
   length = g_mapped_file_get_length (mapped_file);
   data = g_mapped_file_get_contents (mapped_file);
 
-  /* FIXME: Set error */
   if (!data)
-    return FALSE;
+    data = "";
 
   if (reset)
     {
@@ -3013,13 +3531,15 @@ gtk_css_provider_load_from_path_internal (GtkCssProvider  *css_provider,
     }
 
   priv->scanner->input_name = priv->filename;
+  priv->buffer = data;
   g_scanner_input_text (priv->scanner, data, (guint) length);
 
-  parse_stylesheet (css_provider);
+  ret = parse_stylesheet (css_provider, error);
 
+  priv->buffer = NULL;
   g_mapped_file_unref (mapped_file);
 
-  return TRUE;
+  return ret;
 }
 
 /**
@@ -3071,16 +3591,26 @@ gtk_css_provider_get_default (void)
         "@define-color tooltip_bg_color #eee1b3; \n"
         "@define-color tooltip_fg_color #000; \n"
         "\n"
+        "@define-color info_fg_color rgb (181, 171, 156);\n"
+        "@define-color info_bg_color rgb (252, 252, 189);\n"
+        "@define-color warning_fg_color rgb (173, 120, 41);\n"
+        "@define-color warning_bg_color rgb (250, 173, 61);\n"
+        "@define-color question_fg_color rgb (97, 122, 214);\n"
+        "@define-color question_bg_color rgb (138, 173, 212);\n"
+        "@define-color error_fg_color rgb (166, 38, 38);\n"
+        "@define-color error_bg_color rgb (237, 54, 54);\n"
+        "\n"
         "*,\n"
         "GtkTreeView > GtkButton {\n"
         "  background-color: @bg_color;\n"
         "  color: @fg_color;\n"
-        "  border-color: shade (@bg_color, 0.7);\n"
-        "  padding: 2 2; \n"
+        "  border-color: shade (@bg_color, 0.6);\n"
+        "  padding: 2;\n"
+        "  border-width: 0;\n"
         "}\n"
         "\n"
         "*:prelight {\n"
-        "  background-color: shade (@bg_color, 2.0);\n"
+        "  background-color: shade (@bg_color, 1.05);\n"
         "  color: shade (@fg_color, 1.3);\n"
         "}\n"
         "\n"
@@ -3089,9 +3619,22 @@ gtk_css_provider_get_default (void)
         "  color: @selected_fg_color;\n"
         "}\n"
         "\n"
+        ".expander {\n"
+        "  color: #fff;\n"
+        "}\n"
+        "\n"
+        ".expander:prelight {\n"
+        "  color: @text_color;\n"
+        "}\n"
+        "\n"
+        ".expander:active {\n"
+        "  transition: 300ms linear;\n"
+        "}\n"
+        "\n"
         "*:insensitive {\n"
-        "  background-color: shade (@bg_color, 0.7);\n"
-        "  color: shade (@fg_color, 0.7);\n"
+        "  border-color: shade (@bg_color, 0.7);\n"
+        "  background-color: shade (@bg_color, 0.9);\n"
+        "  color: shade (@bg_color, 0.7);\n"
         "}\n"
         "\n"
         "GtkTreeView, GtkIconView, GtkTextView {\n"
@@ -3111,6 +3654,9 @@ gtk_css_provider_get_default (void)
         ".tooltip {\n"
         "  background-color: @tooltip_bg_color; \n"
         "  color: @tooltip_fg_color; \n"
+        "  border-color: @tooltip_fg_color; \n"
+        "  border-width: 1;\n"
+        "  border-style: solid;\n"
         "}\n"
         "\n"
         ".button,\n"
@@ -3143,19 +3689,45 @@ gtk_css_provider_get_default (void)
         "  color: @text_color;\n"
         "}\n"
         "\n"
+        ".entry:insensitive {\n"
+        "  background-color: shade (@base_color, 0.9);\n"
+        "  color: shade (@base_color, 0.7);\n"
+        "}\n"
+        ".entry:active {\n"
+        "  background-color: #c4c2bd;\n"
+        "  color: #000;\n"
+        "}\n"
+        "\n"
         ".progressbar,\n"
         ".entry.progressbar {\n"
         "  background-color: @selected_bg_color;\n"
         "  border-color: shade (@selected_bg_color, 0.7);\n"
+        "  color: @selected_fg_color;\n"
+        "}\n"
+        "\n"
+        "GtkCheckButton:hover,\n"
+        "GtkCheckButton:selected,\n"
+        "GtkRadioButton:hover,\n"
+        "GtkRadioButton:selected {\n"
+        "  background-color: shade (@bg_color, 1.05);\n"
         "}\n"
         "\n"
-        ".check, .radio {\n"
+        ".check, .radio,\n"
+        ".check:active, .radio:active,\n"
+        ".check:hover, .radio:hover {\n"
         "  background-color: @base_color;\n"
+        "  border-color: @fg_color;\n"
         "  color: @text_color;\n"
+        "  border-style: solid;\n"
+        "  border-width: 1;\n"
+        "}\n"
+        "\n"
+        ".check:selected, .radio:selected {\n"
+        "  background-color: @selected_bg_color;\n"
+        "  color: @selected_fg_color;\n"
         "}\n"
         "\n"
-        ".menu.check,\n"
-        ".menu.radio {\n"
+        ".menu.check, .menu.radio {\n"
         "  color: @fg_color;\n"
         "}\n"
         "\n"
@@ -3164,6 +3736,11 @@ gtk_css_provider_get_default (void)
         "  border-style: none;\n"
         "}\n"
         "\n"
+        ".popup {\n"
+        "  border-style: outset;\n"
+        "  border-width: 1;\n"
+        "}\n"
+        "\n"
         ".viewport {\n"
         "  border-style: inset;\n"
         "  border-width: 2;\n"
@@ -3173,10 +3750,94 @@ gtk_css_provider_get_default (void)
         "  border-style: outset;\n"
         "  border-width: 1;\n"
         "}\n"
+        "\n"
+        ".frame {\n"
+        "  border-style: inset;\n"
+        "  border-width: 1;\n"
+        "}\n"
+        "\n"
+        ".menu,\n"
+        ".menubar,\n"
+        ".toolbar {\n"
+        "  border-style: outset;\n"
+        "  border-width: 1;\n"
+        "}\n"
+        "\n"
+        ".menu:hover,\n"
+        ".menubar:hover {\n"
+        "  background-color: @selected_bg_color;\n"
+        "  color: @selected_fg_color;\n"
+        "}\n"
+        "\n"
+        ".menu .check,\n"
+        ".menu .radio,\n"
+        ".menu .check:active,\n"
+        ".menu .radio:active {\n"
+        "  border-style: none;\n"
+        "}\n"
+        "\n"
+        "GtkSpinButton.button {\n"
+        "  border-width: 1;\n"
+        "}\n"
+        "\n"
+        ".scale.slider:hover,\n"
+        "GtkSpinButton.button:hover {\n"
+        "  background-color: shade (@bg_color, 1.05);\n"
+        "  border-color: shade (@bg_color, 0.8);\n"
+        "}\n"
+        "\n"
+        "GtkToggleButton.button:inconsistent {\n"
+        "  border-style: outset;\n"
+        "  border-width: 1px;\n"
+        "  background-color: shade (@bg_color, 0.9);\n"
+        "  border-color: shade (@bg_color, 0.7);\n"
+        "}\n"
+        "\n"
+        "GtkLabel:selected {\n"
+        "  background-color: shade (@bg_color, 0.9);\n"
+        "  color: @fg_color;\n"
+        "}\n"
+        "\n"
+        "GtkLabel:selected:focused {\n"
+        "  background-color: @selected_bg_color;\n"
+        "  color: @selected_fg_color;\n"
+        "}\n"
+        "\n"
+        ".spinner:active {\n"
+        "  transition: 750ms linear loop;\n"
+        "}\n"
+        "\n"
+        ".info {\n"
+        "  background-color: @info_bg_color;\n"
+        "  color: @info_fg_color;\n"
+        "}\n"
+        "\n"
+        ".warning {\n"
+        "  background-color: @warning_bg_color;\n"
+        "  color: @warning_fg_color;\n"
+        "}\n"
+        "\n"
+        ".question {\n"
+        "  background-color: @question_bg_color;\n"
+        "  color: @question_fg_color;\n"
+        "}\n"
+        "\n"
+        ".error {\n"
+        "  background-color: @error_bg_color;\n"
+        "  color: @error_fg_color;\n"
+        "}\n"
+        "\n"
+        ".highlight {\n"
+        "  background-color: @selected_bg_color;\n"
+        "  color: @selected_fg_color;\n"
+        "}\n"
         "\n";
 
       provider = gtk_css_provider_new ();
-      gtk_css_provider_load_from_data (provider, str, -1, NULL);
+      if (!gtk_css_provider_load_from_data (provider, str, -1, NULL))
+        {
+          g_error ("Failed to load the internal default CSS.");
+        }
     }
 
   return provider;
@@ -3257,6 +3918,8 @@ gtk_css_provider_get_named (const gchar *name,
             }
         }
 
+      g_free (subpath);
+
       if (path)
         {
           GError *error = NULL;